import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { getOAuthProvider, resetOAuthProviders } from "@earendil-works/pi-ai/oauth"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { AuthStorage } from "../src/core/auth-storage.ts"; import { ModelRegistry } from "../src/core/model-registry.ts"; import { RADIUS_PROVIDER_ID } from "../src/core/radius.ts"; function radiusOAuthCredential(gatewayBaseUrl: string) { return { type: "oauth" as const, access: "access-token", refresh: "refresh-token", expires: Date.now() + 60 * 60 * 1000, gatewayConfig: { baseUrl: gatewayBaseUrl, models: [ { id: "auto", name: "Radius Auto", reasoning: false, input: ["text"], cost: { input: 1, output: 2, cacheRead: 0.1, cacheWrite: 0.2 }, contextWindow: 128000, maxTokens: 16384, }, { id: "byok/gpt-5.5", name: "GPT-5.5 (BYOK)", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 256000, maxTokens: 32000, }, ], }, }; } let tempDir: string; beforeEach(() => { tempDir = join(tmpdir(), `pi-test-radius-${Date.now()}-${Math.random().toString(36).slice(2)}`); mkdirSync(tempDir, { recursive: true }); resetOAuthProviders(); }); afterEach(() => { if (tempDir && existsSync(tempDir)) { rmSync(tempDir, { recursive: true }); } resetOAuthProviders(); }); describe("radius oauth provider", () => { it("is registered as a built-in OAuth provider", () => { expect(getOAuthProvider(RADIUS_PROVIDER_ID)?.name).toBe("Radius"); }); }); describe("radius models via ModelRegistry", () => { it("injects catalog models from the stored credential", () => { const registry = ModelRegistry.inMemory( AuthStorage.inMemory({ radius: radiusOAuthCredential("https://radius.example.com/v1") }), ); const auto = registry.find(RADIUS_PROVIDER_ID, "auto"); expect(auto).toBeDefined(); expect(auto?.api).toBe("pi-messages"); expect(auto?.baseUrl).toBe("https://radius.example.com/v1"); expect(auto?.name).toBe("Radius Auto"); // byok ids are registered verbatim const byok = registry.find(RADIUS_PROVIDER_ID, "byok/gpt-5.5"); expect(byok).toBeDefined(); expect(byok?.contextWindow).toBe(256000); expect(registry.hasConfiguredAuth(auto!)).toBe(true); expect(registry.getProviderDisplayName(RADIUS_PROVIDER_ID)).toBe("Radius"); }); it("exposes no radius models without credentials", () => { const registry = ModelRegistry.inMemory(AuthStorage.inMemory()); expect(registry.getAll().filter((model) => model.provider === RADIUS_PROVIDER_ID)).toHaveLength(0); expect(getOAuthProvider(RADIUS_PROVIDER_ID)).toBeDefined(); }); it("keeps radius models across registry refresh", () => { const registry = ModelRegistry.inMemory( AuthStorage.inMemory({ radius: radiusOAuthCredential("https://radius.example.com/v1") }), ); registry.refresh(); expect(registry.find(RADIUS_PROVIDER_ID, "auto")).toBeDefined(); }); }); describe("custom radius gateways via models.json", () => { function createRegistry(providers: Record, authStorage: AuthStorage): ModelRegistry { const modelsJsonPath = join(tempDir, "models.json"); writeFileSync(modelsJsonPath, JSON.stringify({ providers })); return ModelRegistry.create(authStorage, modelsJsonPath); } it("registers an independent radius-style provider", () => { const registry = createRegistry( { "radius-dev": { name: "Radius (dev)", baseUrl: "http://localhost:8788", oauth: "radius" } }, AuthStorage.inMemory({ "radius-dev": radiusOAuthCredential("http://localhost:8788/v1") }), ); expect(registry.getError()).toBeUndefined(); expect(getOAuthProvider("radius-dev")?.name).toBe("Radius (dev)"); expect(getOAuthProvider(RADIUS_PROVIDER_ID)?.name).toBe("Radius"); // Dev gateway models are injected under the custom provider id only. const devAuto = registry.find("radius-dev", "auto"); expect(devAuto).toBeDefined(); expect(devAuto?.api).toBe("pi-messages"); expect(devAuto?.baseUrl).toBe("http://localhost:8788/v1"); expect(registry.find(RADIUS_PROVIDER_ID, "auto")).toBeUndefined(); expect(registry.getProviderDisplayName("radius-dev")).toBe("Radius (dev)"); }); it("survives registry refresh", () => { const registry = createRegistry( { "radius-dev": { baseUrl: "http://localhost:8788", oauth: "radius" } }, AuthStorage.inMemory({ "radius-dev": radiusOAuthCredential("http://localhost:8788/v1") }), ); registry.refresh(); expect(getOAuthProvider("radius-dev")).toBeDefined(); expect(registry.find("radius-dev", "auto")).toBeDefined(); }); it("requires baseUrl when oauth is set", () => { const registry = createRegistry({ "radius-dev": { oauth: "radius" } }, AuthStorage.inMemory()); expect(registry.getError()).toContain('"baseUrl" is required when "oauth" is set'); }); });