AUDIT / src /lib /__tests__ /capabilityRouter.test.ts
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 2)
cc11e77 verified
Raw
History Blame Contribute Delete
8.23 kB
/**
* capabilityRouter.test.ts — Unit test S38 Capability Router
*
* Copertura (12 test):
* 1. route: restituisce gemini per reasoning_depth="high" (score massimo)
* 2. route: restituisce groq per speed=true (velocità 10/10)
* 3. route: provider con vision=false escluso (score basso) quando req.vision=true
* 4. route: provider senza tool calling penalizzato quando requires_tools=true
* 5. route: prefer_free=true favorisce provider gratuiti
* 6. route: long_context favorisce provider con max_context>=100k
* 7. route: ritorna null se nessun provider disponibile
* 8. route: ignora provider non disponibili (DI isAvailable=false)
* 9. rankAll: ordina tutti i candidati per score decrescente
* 10. scoreProfile: score > 0 per profilo compatibile con request
* 11. scoreProfile: score molto negativo per vision req su provider senza vision
* 12. getProfile / getAllProfiles: dati statici corretti
*
* DI pattern: _setTestDeps / _resetDeps — zero vi.mock su moduli ESM
* Modulo puro: nessun side-effect, nessuna dipendenza da browser API
*
* SESSIONE: S38 — Capability Router
* S342: aggiornato 12b per includere cerebras (8° provider, aggiunto in S325)
*/
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import {
route,
rankAll,
scoreProfile,
getProfile,
getAllProfiles,
buildReason,
CAPABILITY_PROFILES,
_setTestDeps,
_resetDeps,
} from "@/lib/capabilityRouter";
// ─── Setup / Teardown ─────────────────────────────────────────────────────────
beforeEach(() => {
// Per default tutti i provider disponibili
_setTestDeps({ isAvailable: () => true });
});
afterEach(() => {
_resetDeps();
});
// ─── route ────────────────────────────────────────────────────────────────────
describe("route", () => {
it("1. reasoning_depth=high → gemini o openai (reasoning_score >= 9)", () => {
const result = route({ reasoning_depth: "high" });
expect(result).not.toBeNull();
expect(result!.profile.reasoning_score).toBeGreaterThanOrEqual(9);
expect(["gemini", "openai"]).toContain(result!.provider);
});
it("2. speed=true → groq (speed_score 10/10)", () => {
const result = route({ speed: true });
expect(result).not.toBeNull();
expect(result!.provider).toBe("groq");
expect(result!.profile.speed_score).toBe(10);
});
it("3. vision=true → provider con vision:true (gemini, openai, openrouter)", () => {
const result = route({ vision: true });
expect(result).not.toBeNull();
expect(result!.profile.vision).toBe(true);
expect(["gemini", "openai", "openrouter"]).toContain(result!.provider);
});
it("4. requires_tools=true → provider con tools:true", () => {
const result = route({ requires_tools: true });
expect(result).not.toBeNull();
expect(result!.profile.tools).toBe(true);
});
it("5. prefer_free=true → provider con free:true", () => {
const result = route({ prefer_free: true });
expect(result).not.toBeNull();
expect(result!.profile.free).toBe(true);
});
it("6. long_context=true → provider con max_context >= 100k", () => {
const result = route({ long_context: true });
expect(result).not.toBeNull();
expect(result!.profile.max_context).toBeGreaterThanOrEqual(100_000);
});
it("7. nessun provider disponibile → null", () => {
_setTestDeps({ isAvailable: () => false });
const result = route({ speed: true });
expect(result).toBeNull();
});
it("8. DI isAvailable filtra i provider — solo groq disponibile → route = groq", () => {
_setTestDeps({ isAvailable: (id) => id === "groq" });
const result = route({ reasoning_depth: "high" });
expect(result).not.toBeNull();
expect(result!.provider).toBe("groq");
});
});
// ─── rankAll ─────────────────────────────────────────────────────────────────
describe("rankAll", () => {
it("9. ordina tutti i candidati per score decrescente", () => {
const ranked = rankAll({ speed: true });
expect(ranked.length).toBeGreaterThan(1);
for (let i = 0; i < ranked.length - 1; i++) {
expect(ranked[i].score).toBeGreaterThanOrEqual(ranked[i + 1].score);
}
});
});
// ─── scoreProfile ─────────────────────────────────────────────────────────────
describe("scoreProfile", () => {
it("10. score > 0 per profilo compatibile con request", () => {
const gemini = CAPABILITY_PROFILES["gemini"];
const score = scoreProfile(gemini, { reasoning_depth: "high", code: true });
expect(score).toBeGreaterThan(0);
});
it("11. score molto negativo per vision req su provider senza vision (groq)", () => {
const groq = CAPABILITY_PROFILES["groq"];
const score = scoreProfile(groq, { vision: true });
expect(score).toBeLessThan(-50);
});
it("11b. score molto negativo per requires_tools su provider senza tools (browser-llm)", () => {
const browserLlm = CAPABILITY_PROFILES["browser-llm"];
const score = scoreProfile(browserLlm, { requires_tools: true });
expect(score).toBeLessThan(-50);
});
});
// ─── getProfile / getAllProfiles ───────────────────────────────────────────────
describe("getProfile / getAllProfiles", () => {
it("12. getProfile ritorna profilo valido per ogni ProviderId", () => {
// S342: incluso cerebras (aggiunto in S325 — 2000+ tok/s, fast-chat senza tools)
const ids = ["gemini", "openai", "groq", "openrouter", "huggingface", "together", "browser-llm", "cerebras", "sambanova"] as const;
for (const id of ids) {
const p = getProfile(id);
expect(p.id).toBe(id);
expect(p.reasoning_score).toBeGreaterThanOrEqual(0);
expect(p.reasoning_score).toBeLessThanOrEqual(10);
expect(p.speed_score).toBeGreaterThanOrEqual(0);
expect(p.speed_score).toBeLessThanOrEqual(10);
expect(p.code_score).toBeGreaterThanOrEqual(0);
expect(p.code_score).toBeLessThanOrEqual(10);
expect(p.max_context).toBeGreaterThan(0);
expect(typeof p.vision).toBe("boolean");
expect(typeof p.free).toBe("boolean");
expect(typeof p.tools).toBe("boolean");
}
});
it("12b. getAllProfiles ritorna 9 profili (uno per provider, incluso cerebras S325 + sambanova S422)", () => {
const profiles = getAllProfiles();
// S342: 9 provider = gemini, openai, groq, openrouter, huggingface, together, browser-llm, cerebras, sambanova (S422/S502-FIX)
expect(profiles).toHaveLength(9);
const ids = profiles.map(p => p.id);
const unique = new Set(ids);
expect(unique.size).toBe(8);
// Invariante: cerebras deve essere presente (aggiunto S325)
expect(ids).toContain("cerebras");
// Invariante: sambanova deve essere presente (aggiunto S422, inserito in ALL_PROVIDERS in S502-FIX)
expect(ids).toContain("sambanova");
});
});
// ─── buildReason ──────────────────────────────────────────────────────────────
describe("buildReason", () => {
it("buildReason include dettagli rilevanti per il request", () => {
const gemini = CAPABILITY_PROFILES["gemini"];
const reason = buildReason(gemini, { reasoning_depth: "high", vision: true });
expect(reason).toContain("gemini");
expect(reason).toContain("reasoning");
expect(reason).toContain("vision");
});
it("buildReason funziona anche con request vuoto", () => {
const groq = CAPABILITY_PROFILES["groq"];
const reason = buildReason(groq, {});
expect(typeof reason).toBe("string");
expect(reason.length).toBeGreaterThan(0);
});
});