import { beforeEach, afterEach, describe, expect, it } from 'vitest'; import { ProviderService } from '../provider.service.js'; const ENV_KEYS = [ 'ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'ZAI_API_KEY', 'OLLAMA_BASE_URL', 'OLLAMA_HOST', 'OLLAMA_MODELS', 'MOSAIC_CUSTOM_PROVIDERS', ] as const; type EnvKey = (typeof ENV_KEYS)[number]; describe('ProviderService', () => { const savedEnv = new Map(); beforeEach(() => { for (const key of ENV_KEYS) { savedEnv.set(key, process.env[key]); delete process.env[key]; } }); afterEach(() => { for (const key of ENV_KEYS) { const value = savedEnv.get(key); if (value === undefined) { delete process.env[key]; } else { process.env[key] = value; } } }); it('skips API-key providers when env vars are missing (no models become available)', async () => { const service = new ProviderService(null); await service.onModuleInit(); // Pi's built-in registry may include model definitions for all providers, but // without API keys none of them should be available (usable). const availableModels = service.listAvailableModels(); const availableProviderIds = new Set(availableModels.map((m) => m.provider)); expect(availableProviderIds).not.toContain('anthropic'); expect(availableProviderIds).not.toContain('openai'); expect(availableProviderIds).not.toContain('zai'); // Providers list may show built-in providers, but they should not be marked available const providers = service.listProviders(); for (const p of providers.filter((p) => ['anthropic', 'openai', 'zai'].includes(p.id))) { expect(p.available).toBe(false); } }); it('registers Anthropic provider with correct models when ANTHROPIC_API_KEY is set', async () => { process.env['ANTHROPIC_API_KEY'] = 'test-anthropic'; const service = new ProviderService(null); await service.onModuleInit(); const providers = service.listProviders(); const anthropic = providers.find((p) => p.id === 'anthropic'); expect(anthropic).toBeDefined(); expect(anthropic!.available).toBe(true); expect(anthropic!.models.map((m) => m.id)).toEqual([ 'claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5', ]); // All Anthropic models have 200k context window for (const m of anthropic!.models) { expect(m.contextWindow).toBe(200000); } }); it('registers OpenAI provider with correct models when OPENAI_API_KEY is set', async () => { process.env['OPENAI_API_KEY'] = 'test-openai'; const service = new ProviderService(null); await service.onModuleInit(); const providers = service.listProviders(); const openai = providers.find((p) => p.id === 'openai'); expect(openai).toBeDefined(); expect(openai!.available).toBe(true); expect(openai!.models.map((m) => m.id)).toEqual(['codex-gpt-5-4']); }); it('registers Z.ai provider with correct models when ZAI_API_KEY is set', async () => { process.env['ZAI_API_KEY'] = 'test-zai'; const service = new ProviderService(null); await service.onModuleInit(); const providers = service.listProviders(); const zai = providers.find((p) => p.id === 'zai'); expect(zai).toBeDefined(); expect(zai!.available).toBe(true); // Pi's registry may include additional glm variants; verify our registered model is present expect(zai!.models.map((m) => m.id)).toContain('glm-5'); }); it('registers all three providers when all keys are set', async () => { process.env['ANTHROPIC_API_KEY'] = 'test-anthropic'; process.env['OPENAI_API_KEY'] = 'test-openai'; process.env['ZAI_API_KEY'] = 'test-zai'; const service = new ProviderService(null); await service.onModuleInit(); const providerIds = service.listProviders().map((p) => p.id); expect(providerIds).toContain('anthropic'); expect(providerIds).toContain('openai'); expect(providerIds).toContain('zai'); }); it('can find registered Anthropic models by provider+id', async () => { process.env['ANTHROPIC_API_KEY'] = 'test-anthropic'; const service = new ProviderService(null); await service.onModuleInit(); const sonnet = service.findModel('anthropic', 'claude-sonnet-4-6'); expect(sonnet).toBeDefined(); expect(sonnet!.provider).toBe('anthropic'); expect(sonnet!.id).toBe('claude-sonnet-4-6'); }); it('can find registered Z.ai models by provider+id', async () => { process.env['ZAI_API_KEY'] = 'test-zai'; const service = new ProviderService(null); await service.onModuleInit(); const glm = service.findModel('zai', 'glm-4.5'); expect(glm).toBeDefined(); expect(glm!.provider).toBe('zai'); expect(glm!.id).toBe('glm-4.5'); }); });