// SPDX-License-Identifier: AGPL-3.0-only // Copyright (C) 2026 Giancarlo Erra - Altaire Limited import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { getEmbeddingConfig, loadEmbeddingConfig, resetEmbeddingConfig, } from "../../src/services/embedding-config.js"; describe("embedding-config", () => { const originalEnv = { ...process.env }; beforeEach(() => { resetEmbeddingConfig(); // Clear all embedding-related env vars delete process.env.EMBEDDING_PROVIDER; delete process.env.OLLAMA_MODE; delete process.env.OLLAMA_URL; delete process.env.EMBEDDING_MODEL; delete process.env.EMBEDDING_DIMENSIONS; delete process.env.EMBEDDING_CONTEXT_LENGTH; delete process.env.OLLAMA_API_KEY; delete process.env.OPENAI_API_KEY; delete process.env.GOOGLE_API_KEY; }); afterEach(() => { resetEmbeddingConfig(); // Restore original env process.env = { ...originalEnv }; }); describe("defaults (no env vars)", () => { it("defaults to ollama provider", () => { const config = loadEmbeddingConfig(); expect(config.embeddingProvider).toBe("ollama"); }); it("defaults to auto mode", () => { const config = loadEmbeddingConfig(); expect(config.ollamaMode).toBe("auto"); }); it("defaults URL to localhost:11434 in auto mode", () => { const config = loadEmbeddingConfig(); expect(config.ollamaUrl).toBe("http://localhost:11434"); }); it("defaults model to nomic-embed-text", () => { const config = loadEmbeddingConfig(); expect(config.embeddingModel).toBe("nomic-embed-text"); }); it("defaults dimensions to 768", () => { const config = loadEmbeddingConfig(); expect(config.embeddingDimensions).toBe(768); }); it("has no API key by default", () => { const config = loadEmbeddingConfig(); expect(config.ollamaApiKey).toBeUndefined(); }); }); describe("docker mode", () => { it("uses docker URL default when OLLAMA_MODE=docker", () => { process.env.OLLAMA_MODE = "docker"; const config = loadEmbeddingConfig(); expect(config.ollamaMode).toBe("docker"); expect(config.ollamaUrl).toBe("http://localhost:11435"); }); it("allows overriding URL in docker mode", () => { process.env.OLLAMA_MODE = "docker"; process.env.OLLAMA_URL = "http://custom:9999"; const config = loadEmbeddingConfig(); expect(config.ollamaUrl).toBe("http://custom:9999"); }); }); describe("external mode", () => { it("uses external URL default when OLLAMA_MODE=external", () => { process.env.OLLAMA_MODE = "external"; const config = loadEmbeddingConfig(); expect(config.ollamaMode).toBe("external"); expect(config.ollamaUrl).toBe("http://localhost:11434"); }); it("allows overriding URL in external mode", () => { process.env.OLLAMA_MODE = "external"; process.env.OLLAMA_URL = "http://gpu-server:11434"; const config = loadEmbeddingConfig(); expect(config.ollamaUrl).toBe("http://gpu-server:11434"); }); }); describe("model and dimensions overrides", () => { it("allows custom model", () => { process.env.EMBEDDING_MODEL = "mxbai-embed-large"; const config = loadEmbeddingConfig(); expect(config.embeddingModel).toBe("mxbai-embed-large"); }); it("allows custom dimensions", () => { process.env.EMBEDDING_DIMENSIONS = "1024"; const config = loadEmbeddingConfig(); expect(config.embeddingDimensions).toBe(1024); }); it("parses dimensions as integer", () => { process.env.EMBEDDING_DIMENSIONS = "512"; const config = loadEmbeddingConfig(); expect(config.embeddingDimensions).toBe(512); expect(typeof config.embeddingDimensions).toBe("number"); }); }); describe("API key", () => { it("reads OLLAMA_API_KEY when set", () => { process.env.OLLAMA_API_KEY = "test-key-123"; const config = loadEmbeddingConfig(); expect(config.ollamaApiKey).toBe("test-key-123"); }); it("is undefined when not set", () => { const config = loadEmbeddingConfig(); expect(config.ollamaApiKey).toBeUndefined(); }); }); describe("invalid mode", () => { it("throws for invalid OLLAMA_MODE", () => { process.env.OLLAMA_MODE = "invalid"; expect(() => loadEmbeddingConfig()).toThrow( 'Invalid OLLAMA_MODE: "invalid". Must be "docker", "external", or "auto".', ); }); }); describe("singleton behavior", () => { it("caches config after first load", () => { const first = loadEmbeddingConfig(); // Change env — should NOT affect cached config process.env.OLLAMA_MODE = "external"; const second = loadEmbeddingConfig(); expect(second).toBe(first); // same reference expect(second.ollamaMode).toBe("auto"); }); it("getEmbeddingConfig returns same as loadEmbeddingConfig", () => { const loaded = loadEmbeddingConfig(); const got = getEmbeddingConfig(); expect(got).toBe(loaded); }); it("resetEmbeddingConfig clears cache", () => { const first = loadEmbeddingConfig(); expect(first.ollamaMode).toBe("auto"); resetEmbeddingConfig(); process.env.OLLAMA_MODE = "external"; const second = loadEmbeddingConfig(); expect(second.ollamaMode).toBe("external"); expect(second).not.toBe(first); }); }); describe("full external config", () => { it("handles complete external config with all options", () => { process.env.OLLAMA_MODE = "external"; process.env.OLLAMA_URL = "http://remote-gpu:11434"; process.env.EMBEDDING_MODEL = "mxbai-embed-large"; process.env.EMBEDDING_DIMENSIONS = "1024"; process.env.OLLAMA_API_KEY = "secret"; const config = loadEmbeddingConfig(); expect(config).toEqual({ embeddingProvider: "ollama", ollamaMode: "external", ollamaUrl: "http://remote-gpu:11434", embeddingModel: "mxbai-embed-large", embeddingDimensions: 1024, embeddingContextLength: 512, ollamaApiKey: "secret", }); }); }); describe("embedding provider selection", () => { it("defaults to ollama when EMBEDDING_PROVIDER is not set", () => { const config = loadEmbeddingConfig(); expect(config.embeddingProvider).toBe("ollama"); expect(config.embeddingModel).toBe("nomic-embed-text"); expect(config.embeddingDimensions).toBe(768); }); it("selects openai provider with correct defaults", () => { process.env.EMBEDDING_PROVIDER = "openai"; const config = loadEmbeddingConfig(); expect(config.embeddingProvider).toBe("openai"); expect(config.embeddingModel).toBe("text-embedding-3-small"); expect(config.embeddingDimensions).toBe(1536); expect(config.embeddingContextLength).toBe(8191); }); it("selects google provider with correct defaults", () => { process.env.EMBEDDING_PROVIDER = "google"; const config = loadEmbeddingConfig(); expect(config.embeddingProvider).toBe("google"); expect(config.embeddingModel).toBe("gemini-embedding-001"); expect(config.embeddingDimensions).toBe(3072); expect(config.embeddingContextLength).toBe(2048); }); it("throws for invalid EMBEDDING_PROVIDER", () => { process.env.EMBEDDING_PROVIDER = "anthropic"; expect(() => loadEmbeddingConfig()).toThrow( 'Invalid EMBEDDING_PROVIDER: "anthropic". Must be "ollama", "openai", or "google".', ); }); it("allows overriding model for openai provider", () => { process.env.EMBEDDING_PROVIDER = "openai"; process.env.EMBEDDING_MODEL = "text-embedding-3-large"; const config = loadEmbeddingConfig(); expect(config.embeddingModel).toBe("text-embedding-3-large"); expect(config.embeddingContextLength).toBe(8191); }); it("allows overriding dimensions for openai provider", () => { process.env.EMBEDDING_PROVIDER = "openai"; process.env.EMBEDDING_DIMENSIONS = "256"; const config = loadEmbeddingConfig(); expect(config.embeddingDimensions).toBe(256); }); it("uses context length for unknown model", () => { process.env.EMBEDDING_PROVIDER = "openai"; process.env.EMBEDDING_MODEL = "custom-embed-model"; const config = loadEmbeddingConfig(); expect(config.embeddingContextLength).toBe(0); }); it("allows explicit context length override for any provider", () => { process.env.EMBEDDING_PROVIDER = "google"; process.env.EMBEDDING_CONTEXT_LENGTH = "4096"; const config = loadEmbeddingConfig(); expect(config.embeddingContextLength).toBe(4096); }); }); });