/** * Tests for provider mirror metadata and model-registry helpers. */ import { beforeEach, describe, expect, it, vi } from "vitest"; // Mock pi-ai before importing the module under test vi.mock("@mariozechner/pi-ai", () => ({ getModels: vi.fn(), getProviders: vi.fn(), getApiProvider: vi.fn(), })); import { getApiProvider, getModels, getProviders } from "@mariozechner/pi-ai"; import type { MirroredProvider } from "../providers.js"; import { listProviderIdsWithModels, mirrorProvider } from "../providers.js"; // Cast to vi.Mock for mock API access without strict generics const mockGetModels = getModels as unknown as ReturnType; const mockGetProviders = getProviders as unknown as ReturnType; const mockGetApiProvider = getApiProvider as unknown as ReturnType< typeof vi.fn >; function createMockModel(overrides: Record = {}) { return { id: "gpt-4o", name: "GPT-4o", reasoning: false, input: ["text", "image"], cost: { input: 5, output: 15, cacheRead: 2.5, cacheWrite: 5 }, contextWindow: 128_000, maxTokens: 16_384, api: "openai", provider: "openai", baseUrl: "https://api.openai.com/v1", ...overrides, }; } beforeEach(() => { vi.clearAllMocks(); }); describe("mirrorProvider", () => { it("returns undefined when the provider has no models", () => { mockGetModels.mockReturnValue([]); const result = mirrorProvider("openai"); expect(result).toBeUndefined(); }); it("mirrors a provider with a single model", () => { const model = createMockModel(); mockGetModels.mockReturnValue([model]); mockGetApiProvider.mockReturnValue({ api: "openai", stream: vi.fn(), streamSimple: vi.fn(), }); const result = mirrorProvider("openai"); expect(result).toBeDefined(); const mirror = result as MirroredProvider; expect(mirror.providerId).toBe("openai"); expect(mirror.baseUrl).toBe("https://api.openai.com/v1"); expect(mirror.api).toBe("openai"); expect(mirror.hasStreamSimple).toBe(true); expect(mirror.models).toHaveLength(1); expect(mirror.models[0]?.id).toBe("gpt-4o"); }); it("mirrors a provider with multiple models", () => { const models = [ createMockModel({ id: "gpt-4o", name: "GPT-4o" }), createMockModel({ id: "gpt-4o-mini", name: "GPT-4o Mini", maxTokens: 8_192, }), ]; mockGetModels.mockReturnValue(models); mockGetApiProvider.mockReturnValue({ api: "openai", stream: vi.fn(), streamSimple: vi.fn(), }); const result = mirrorProvider("openai"); expect(result).toBeDefined(); const mirror = result as MirroredProvider; expect(mirror.models).toHaveLength(2); expect(mirror.models[0]?.id).toBe("gpt-4o"); expect(mirror.models[1]?.id).toBe("gpt-4o-mini"); }); it("reports hasStreamSimple false when no API provider found", () => { mockGetModels.mockReturnValue([createMockModel()]); mockGetApiProvider.mockReturnValue(undefined); const result = mirrorProvider("openai"); expect(result).toBeDefined(); const mirror = result as MirroredProvider; expect(mirror.hasStreamSimple).toBe(false); }); it("uses empty string for baseUrl when model has none", () => { const model = createMockModel({ baseUrl: undefined }); mockGetModels.mockReturnValue([model]); mockGetApiProvider.mockReturnValue({ api: "openai", stream: vi.fn(), streamSimple: vi.fn(), }); const result = mirrorProvider("openai"); expect(result).toBeDefined(); const mirror = result as MirroredProvider; expect(mirror.baseUrl).toBe(""); }); it("preserves model cost and dimension fields", () => { const model = createMockModel({ cost: { input: 10, output: 30, cacheRead: 5, cacheWrite: 10 }, contextWindow: 200_000, maxTokens: 32_768, reasoning: true, }); mockGetModels.mockReturnValue([model]); mockGetApiProvider.mockReturnValue({ api: "openai", stream: vi.fn(), streamSimple: vi.fn(), }); const result = mirrorProvider("openai"); expect(result).toBeDefined(); const mirror = result as MirroredProvider; const mirrored = mirror.models[0]; expect(mirrored).toBeDefined(); expect(mirrored?.cost).toEqual({ input: 10, output: 30, cacheRead: 5, cacheWrite: 10, }); expect(mirrored?.contextWindow).toBe(200_000); expect(mirrored?.maxTokens).toBe(32_768); expect(mirrored?.reasoning).toBe(true); }); }); describe("listProviderIdsWithModels", () => { it("returns only providers that have registered models", () => { mockGetProviders.mockReturnValue(["openai", "anthropic", "groq"]); mockGetModels.mockImplementation((id: string) => { if (id === "openai") return [createMockModel()]; if (id === "anthropic") return [createMockModel({ api: "anthropic" })]; return []; }); const ids = listProviderIdsWithModels(); expect(ids).toEqual(["openai", "anthropic"]); }); it("returns empty array when no providers have models", () => { mockGetProviders.mockReturnValue(["openai", "anthropic"]); mockGetModels.mockReturnValue([]); const ids = listProviderIdsWithModels(); expect(ids).toEqual([]); }); it("returns empty array when no providers exist", () => { mockGetProviders.mockReturnValue([]); const ids = listProviderIdsWithModels(); expect(ids).toEqual([]); }); });