import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { validateConfig, detectFromEnv, DEFAULT_LOCAL_CONFIG, DEFAULT_STANDALONE_CONFIG, DEFAULT_FEDERATED_CONFIG, } from './mosaic-config.js'; describe('validateConfig — tier enum', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let stderrSpy: any; beforeEach(() => { stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true); }); afterEach(() => { stderrSpy.mockRestore(); }); it('accepts tier="local"', () => { const result = validateConfig({ tier: 'local', storage: { type: 'pglite', dataDir: '.mosaic/storage-pglite' }, queue: { type: 'local', dataDir: '.mosaic/queue' }, memory: { type: 'keyword' }, }); expect(result.tier).toBe('local'); }); it('accepts tier="standalone"', () => { const result = validateConfig({ tier: 'standalone', storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5432/mosaic' }, queue: { type: 'bullmq' }, memory: { type: 'keyword' }, }); expect(result.tier).toBe('standalone'); }); it('accepts tier="federated"', () => { const result = validateConfig({ tier: 'federated', storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5433/mosaic' }, queue: { type: 'bullmq' }, memory: { type: 'pgvector' }, }); expect(result.tier).toBe('federated'); }); it('accepts deprecated tier="team" as alias for "standalone" and emits a deprecation warning', () => { const result = validateConfig({ tier: 'team', storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5432/mosaic' }, queue: { type: 'bullmq' }, memory: { type: 'keyword' }, }); expect(result.tier).toBe('standalone'); expect(stderrSpy).toHaveBeenCalledWith(expect.stringContaining('DEPRECATED')); }); it('rejects an invalid tier with an error listing all three valid values', () => { expect(() => validateConfig({ tier: 'invalid', storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5432/mosaic' }, queue: { type: 'bullmq' }, memory: { type: 'keyword' }, }), ).toThrow(/local.*standalone.*federated|federated.*standalone.*local/); }); it('error message for invalid tier mentions all three valid values', () => { let message = ''; try { validateConfig({ tier: 'invalid', storage: { type: 'postgres', url: 'postgresql://...' }, queue: { type: 'bullmq' }, memory: { type: 'keyword' }, }); } catch (err) { message = err instanceof Error ? err.message : String(err); } expect(message).toContain('"local"'); expect(message).toContain('"standalone"'); expect(message).toContain('"federated"'); }); }); describe('DEFAULT_* config constants', () => { it('DEFAULT_LOCAL_CONFIG has tier="local"', () => { expect(DEFAULT_LOCAL_CONFIG.tier).toBe('local'); }); it('DEFAULT_STANDALONE_CONFIG has tier="standalone"', () => { expect(DEFAULT_STANDALONE_CONFIG.tier).toBe('standalone'); }); it('DEFAULT_FEDERATED_CONFIG has tier="federated" and pgvector memory', () => { expect(DEFAULT_FEDERATED_CONFIG.tier).toBe('federated'); expect(DEFAULT_FEDERATED_CONFIG.memory.type).toBe('pgvector'); }); it('DEFAULT_FEDERATED_CONFIG uses port 5433 (distinct from standalone 5432)', () => { const url = (DEFAULT_FEDERATED_CONFIG.storage as { url: string }).url; expect(url).toContain('5433'); }); it('DEFAULT_FEDERATED_CONFIG has enableVector=true on storage', () => { const storage = DEFAULT_FEDERATED_CONFIG.storage as { type: string; url: string; enableVector?: boolean; }; expect(storage.enableVector).toBe(true); }); }); describe('detectFromEnv — tier env-var routing', () => { const originalEnv = process.env; beforeEach(() => { // Work on a fresh copy so individual tests can set/delete keys freely. process.env = { ...originalEnv }; delete process.env['MOSAIC_STORAGE_TIER']; delete process.env['DATABASE_URL']; delete process.env['VALKEY_URL']; }); afterEach(() => { process.env = originalEnv; }); it('no env vars → returns local config', () => { const config = detectFromEnv(); expect(config.tier).toBe('local'); expect(config.storage.type).toBe('pglite'); expect(config.memory.type).toBe('keyword'); }); it('MOSAIC_STORAGE_TIER=federated alone → returns federated config with enableVector=true', () => { process.env['MOSAIC_STORAGE_TIER'] = 'federated'; const config = detectFromEnv(); expect(config.tier).toBe('federated'); expect(config.memory.type).toBe('pgvector'); const storage = config.storage as { type: string; enableVector?: boolean }; expect(storage.enableVector).toBe(true); }); it('MOSAIC_STORAGE_TIER=federated + DATABASE_URL → uses the URL and still has enableVector=true', () => { process.env['MOSAIC_STORAGE_TIER'] = 'federated'; process.env['DATABASE_URL'] = 'postgresql://custom:pass@db.example.com:5432/mydb'; const config = detectFromEnv(); expect(config.tier).toBe('federated'); const storage = config.storage as { type: string; url: string; enableVector?: boolean }; expect(storage.url).toBe('postgresql://custom:pass@db.example.com:5432/mydb'); expect(storage.enableVector).toBe(true); expect(config.memory.type).toBe('pgvector'); }); it('MOSAIC_STORAGE_TIER=standalone alone → returns standalone-shaped config (not local)', () => { process.env['MOSAIC_STORAGE_TIER'] = 'standalone'; const config = detectFromEnv(); expect(config.tier).toBe('standalone'); expect(config.storage.type).toBe('postgres'); expect(config.memory.type).toBe('keyword'); }); });