| import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
|
| import { initDb, getDb } from '../../db/index.js';
|
| import { encrypt } from '../../lib/crypto.js';
|
| import { routeRequest, setRoutingStrategy } from '../../services/router.js';
|
|
|
| describe('Router', () => {
|
| beforeAll(() => {
|
| process.env.ENCRYPTION_KEY = '0'.repeat(64);
|
| initDb(':memory:');
|
| });
|
|
|
| beforeEach(() => {
|
| const db = getDb();
|
|
|
|
|
| setRoutingStrategy('priority');
|
| db.prepare('DELETE FROM api_keys').run();
|
|
|
| const models = db.prepare('SELECT id, intelligence_rank FROM models ORDER BY intelligence_rank ASC').all() as any[];
|
| const update = db.prepare('UPDATE fallback_config SET priority = ? WHERE model_db_id = ?');
|
| for (let i = 0; i < models.length; i++) {
|
| update.run(i + 1, models[i].id);
|
| }
|
| });
|
|
|
| it('should throw when no keys are configured', () => {
|
| expect(() => routeRequest()).toThrow(/exhausted/i);
|
| });
|
|
|
| it('should route to highest priority model with available key', () => {
|
| const db = getDb();
|
| const { encrypted, iv, authTag } = encrypt('test-groq-key');
|
| db.prepare(`
|
| INSERT INTO api_keys (platform, label, encrypted_key, iv, auth_tag, status, enabled)
|
| VALUES (?, ?, ?, ?, ?, ?, ?)
|
| `).run('groq', 'test', encrypted, iv, authTag, 'healthy', 1);
|
|
|
| const result = routeRequest();
|
| expect(result.platform).toBe('groq');
|
| expect(result.apiKey).toBe('test-groq-key');
|
| });
|
|
|
| it('should prefer higher-priority model when keys exist for multiple platforms', () => {
|
| const db = getDb();
|
|
|
| const googleKey = encrypt('test-google-key');
|
| db.prepare(`
|
| INSERT INTO api_keys (platform, label, encrypted_key, iv, auth_tag, status, enabled)
|
| VALUES (?, ?, ?, ?, ?, ?, ?)
|
| `).run('google', 'test', googleKey.encrypted, googleKey.iv, googleKey.authTag, 'healthy', 1);
|
|
|
| const groqKey = encrypt('test-groq-key');
|
| db.prepare(`
|
| INSERT INTO api_keys (platform, label, encrypted_key, iv, auth_tag, status, enabled)
|
| VALUES (?, ?, ?, ?, ?, ?, ?)
|
| `).run('groq', 'test', groqKey.encrypted, groqKey.iv, groqKey.authTag, 'healthy', 1);
|
|
|
|
|
|
|
|
|
| const result = routeRequest();
|
| expect(result.platform).toBe('google');
|
| });
|
|
|
| it('should skip disabled keys', () => {
|
| const db = getDb();
|
|
|
| const googleKey = encrypt('test-google-key');
|
| db.prepare(`
|
| INSERT INTO api_keys (platform, label, encrypted_key, iv, auth_tag, status, enabled)
|
| VALUES (?, ?, ?, ?, ?, ?, ?)
|
| `).run('google', 'disabled', googleKey.encrypted, googleKey.iv, googleKey.authTag, 'healthy', 0);
|
|
|
| const groqKey = encrypt('test-groq-key');
|
| db.prepare(`
|
| INSERT INTO api_keys (platform, label, encrypted_key, iv, auth_tag, status, enabled)
|
| VALUES (?, ?, ?, ?, ?, ?, ?)
|
| `).run('groq', 'test', groqKey.encrypted, groqKey.iv, groqKey.authTag, 'healthy', 1);
|
|
|
| const result = routeRequest();
|
| expect(result.platform).toBe('groq');
|
| });
|
|
|
| it('should skip invalid keys', () => {
|
| const db = getDb();
|
|
|
| const invalidKey = encrypt('invalid-key');
|
| db.prepare(`
|
| INSERT INTO api_keys (platform, label, encrypted_key, iv, auth_tag, status, enabled)
|
| VALUES (?, ?, ?, ?, ?, ?, ?)
|
| `).run('google', 'invalid', invalidKey.encrypted, invalidKey.iv, invalidKey.authTag, 'invalid', 1);
|
|
|
| const groqKey = encrypt('test-groq-key');
|
| db.prepare(`
|
| INSERT INTO api_keys (platform, label, encrypted_key, iv, auth_tag, status, enabled)
|
| VALUES (?, ?, ?, ?, ?, ?, ?)
|
| `).run('groq', 'test', groqKey.encrypted, groqKey.iv, groqKey.authTag, 'healthy', 1);
|
|
|
| const result = routeRequest();
|
| expect(result.platform).toBe('groq');
|
| });
|
|
|
| it('skips a model whose context window cannot hold the request (#167)', () => {
|
| const db = getDb();
|
| const groqKey = encrypt('test-groq-key');
|
| db.prepare(`
|
| INSERT INTO api_keys (platform, label, encrypted_key, iv, auth_tag, status, enabled)
|
| VALUES (?, ?, ?, ?, ?, ?, ?)
|
| `).run('groq', 'test', groqKey.encrypted, groqKey.iv, groqKey.authTag, 'healthy', 1);
|
|
|
|
|
|
|
| db.prepare("UPDATE models SET tpm_limit = NULL, tpd_limit = NULL WHERE platform = 'groq'").run();
|
|
|
|
|
| const baseline = routeRequest(5);
|
| db.prepare('UPDATE models SET context_window = 10 WHERE id = ?').run(baseline.modelDbId);
|
|
|
|
|
| expect(routeRequest(5).modelDbId).toBe(baseline.modelDbId);
|
|
|
|
|
| const large = routeRequest(2000);
|
| expect(large.modelDbId).not.toBe(baseline.modelDbId);
|
| });
|
|
|
| it('still routes a model with an unknown (null) context window (#167)', () => {
|
| const db = getDb();
|
| const groqKey = encrypt('test-groq-key');
|
| db.prepare(`
|
| INSERT INTO api_keys (platform, label, encrypted_key, iv, auth_tag, status, enabled)
|
| VALUES (?, ?, ?, ?, ?, ?, ?)
|
| `).run('groq', 'test', groqKey.encrypted, groqKey.iv, groqKey.authTag, 'healthy', 1);
|
| db.prepare("UPDATE models SET tpm_limit = NULL, tpd_limit = NULL WHERE platform = 'groq'").run();
|
|
|
| db.prepare("UPDATE models SET context_window = NULL WHERE platform = 'groq'").run();
|
| expect(() => routeRequest(500000)).not.toThrow();
|
| });
|
|
|
| it('should skip keys that cannot be decrypted and use a valid fallback key', () => {
|
| const db = getDb();
|
|
|
| db.prepare(`
|
| INSERT INTO api_keys (platform, label, encrypted_key, iv, auth_tag, status, enabled)
|
| VALUES (?, ?, ?, ?, ?, ?, ?)
|
| `).run('google', 'corrupt', 'not-hex', 'not-hex', 'not-hex', 'healthy', 1);
|
|
|
| const groqKey = encrypt('test-groq-key');
|
| db.prepare(`
|
| INSERT INTO api_keys (platform, label, encrypted_key, iv, auth_tag, status, enabled)
|
| VALUES (?, ?, ?, ?, ?, ?, ?)
|
| `).run('groq', 'test', groqKey.encrypted, groqKey.iv, groqKey.authTag, 'healthy', 1);
|
|
|
| const result = routeRequest();
|
| const corruptKey = db.prepare("SELECT status FROM api_keys WHERE label = 'corrupt'").get() as { status: string };
|
|
|
| expect(result.platform).toBe('groq');
|
| expect(result.apiKey).toBe('test-groq-key');
|
| expect(corruptKey.status).toBe('error');
|
| });
|
| });
|
|
|