import { describe, expect, it } from "vitest"; import { assertKeyRef, DEFAULT_BUDGETS, resolveAiConfig, resolveKeyRef, AiConfigSchema, EnvDefaultsSchema, } from "./config.js"; const fullWorkspace = { enabled: true, provider: "anthropic" as const, chatModel: "claude-sonnet-4-20250514", embedModel: "text-embedding-3-small", budgets: { tokensPerMonth: 1_000_000, requestsPerMinute: 10, maxOutputTokens: 2048, dailySpendUsd: 5 }, policies: { redactPii: true, logPrompts: false, allowUserContentInPrompts: true }, fallback: { provider: "openai" as const, chatModel: "gpt-4o" }, }; describe("resolveAiConfig", () => { it("uses env defaults when no config exists", () => { const resolved = resolveAiConfig(null, null, {}); expect(resolved.provider).toBe("openai"); expect(resolved.chatModel).toBe("gpt-4o"); expect(resolved.embedModel).toBe("text-embedding-3-small"); expect(resolved.embedFallbackProvider).toBe("openai"); expect(resolved.enabled).toBe(true); expect(resolved.budgets).toEqual(DEFAULT_BUDGETS); }); it("workspace config overrides env per field", () => { const resolved = resolveAiConfig( { enabled: true, provider: "google", chatModel: "gemini-2.0-flash", embedModel: "text-embedding-004" }, null, {}, ); expect(resolved.provider).toBe("google"); expect(resolved.chatModel).toBe("gemini-2.0-flash"); expect(resolved.embedModel).toBe("text-embedding-004"); // unset budgets fall to defaults expect(resolved.budgets).toEqual(DEFAULT_BUDGETS); }); it("org policy overrides workspace (enterprise control)", () => { const resolved = resolveAiConfig( { ...fullWorkspace, provider: "anthropic", chatModel: "claude-sonnet-4-20250514" }, { provider: "openai", chatModel: "gpt-4o" }, {}, ); // org wins on the fields it sets expect(resolved.provider).toBe("openai"); expect(resolved.chatModel).toBe("gpt-4o"); // workspace still provides fields org didn't set expect(resolved.embedModel).toBe(fullWorkspace.embedModel); expect(resolved.budgets).toEqual(fullWorkspace.budgets); }); it("per-field fallback: workspace sets only chatModel", () => { const resolved = resolveAiConfig( { enabled: true, provider: "openai", chatModel: "gpt-4o-mini", embedModel: "text-embedding-3-small" }, null, { embedModel: "text-embedding-3-large" }, ); // chatModel from workspace, embedModel — workspace wins (it is set) expect(resolved.chatModel).toBe("gpt-4o-mini"); expect(resolved.embedModel).toBe("text-embedding-3-small"); // provider from workspace expect(resolved.provider).toBe("openai"); }); it("env provides a base URL for OSS", () => { const resolved = resolveAiConfig( { enabled: true, provider: "oss", chatModel: "llama-3.3-70b-instruct", embedModel: "nomic-embed-text" }, null, { baseUrl: "http://ollama:11434/v1" }, ); expect(resolved.endpoint?.baseUrl).toBe("http://ollama:11434/v1"); }); it("disabled via org policy", () => { const resolved = resolveAiConfig(fullWorkspace, { enabled: false }, {}); expect(resolved.enabled).toBe(false); }); it("secretsRef from workspace, apiKeyRef from env", () => { const r1 = resolveAiConfig({ ...fullWorkspace, secretsRef: "my-openai-key" }, null, {}); expect(r1.secretsRef).toBe("my-openai-key"); const r2 = resolveAiConfig(fullWorkspace, null, { apiKeyRef: "env:AI_API_KEY" }); expect(r2.secretsRef).toBe("env:AI_API_KEY"); }); }); describe("AiConfigSchema", () => { it("rejects unknown providers", () => { expect(() => AiConfigSchema.parse({ ...fullWorkspace, provider: "mistral" })).toThrow(); }); it("accepts a valid config", () => { const parsed = AiConfigSchema.parse(fullWorkspace); expect(parsed.provider).toBe("anthropic"); }); }); describe("EnvDefaultsSchema", () => { it("defaults to openai / gpt-4o / text-embedding-3-small", () => { const parsed = EnvDefaultsSchema.parse({}); expect(parsed.provider).toBe("openai"); expect(parsed.chatModel).toBe("gpt-4o"); expect(parsed.embedModel).toBe("text-embedding-3-small"); }); }); describe("key refs", () => { it("rejects literal-looking keys", () => { expect(() => assertKeyRef("sk-abc123")).toThrow(); expect(() => assertKeyRef("sk-ant-abc")).toThrow(); expect(() => assertKeyRef("AIzaSy")).toThrow(); }); it("accepts named refs and env: refs", () => { expect(() => assertKeyRef("my-openai-key")).not.toThrow(); expect(() => assertKeyRef("env:AI_API_KEY")).not.toThrow(); }); it("resolves env:NAME against the environment", () => { expect(resolveKeyRef("env:FOO", { FOO: "bar" })).toBe("bar"); expect(resolveKeyRef("env:MISSING", {})).toBeUndefined(); // non-env refs are resolved by the app's secrets store expect(resolveKeyRef("my-key", { "my-key": "x" })).toBeUndefined(); }); });