/** * adaptiveSolver.test.ts — Unit test AdaptiveSolver (S50) * * Copre: * - detectBlockade: 9 tipi di blocco + unknown * - strategiesFor: listing per kind * - solve: DI mock, ogni strategia testata in isolamento * * Zero vi.mock su Dexie/vfs — usa _setTestDeps() per DI pura. */ import { describe, it, expect, beforeEach } from "vitest"; import { detectBlockade, adaptiveSolver, _setTestDeps } from "../adaptiveSolver"; // ─── detectBlockade ─────────────────────────────────────────────────────────── describe("detectBlockade", () => { it("rileva cors", () => { expect(detectBlockade("CORS error: blocked by CORS policy").kind).toBe("cors"); }); it("rileva not_found da 404", () => { expect(detectBlockade("404 not found: /pages/app.html").kind).toBe("not_found"); }); it("rileva not_found da ENOENT", () => { expect(detectBlockade("ENOENT: no such file or directory").kind).toBe("not_found"); }); it("rileva rate_limit", () => { expect(detectBlockade("429 Too Many Requests").kind).toBe("rate_limit"); }); it("rileva permission da 403", () => { expect(detectBlockade("403 Forbidden: access denied").kind).toBe("permission"); }); it("rileva timeout", () => { expect(detectBlockade("Request timed out after 5000ms").kind).toBe("timeout"); }); it("rileva quota", () => { expect(detectBlockade("QuotaExceededError: storage full").kind).toBe("quota"); }); it("rileva syntax", () => { expect(detectBlockade("SyntaxError: Unexpected token {").kind).toBe("syntax"); }); it("rileva network", () => { expect(detectBlockade("TypeError: Failed to fetch").kind).toBe("network"); }); it("unknown per errori non classificati", () => { expect(detectBlockade("something weird happened xyz").kind).toBe("unknown"); }); it("include il messaggio originale e hint leggibile", () => { const b = detectBlockade("CORS blocked by browser"); expect(b.message).toContain("CORS"); expect(b.hint.length).toBeGreaterThan(0); }); it("tronca messaggi molto lunghi a 300 chars", () => { const long = "network error " + "x".repeat(500); expect(detectBlockade(long).message.length).toBeLessThanOrEqual(300); }); }); // ─── strategiesFor ──────────────────────────────────────────────────────────── describe("adaptiveSolver.strategiesFor", () => { it("restituisce strategie per cors", () => { expect(adaptiveSolver.strategiesFor("cors").length).toBeGreaterThan(0); }); it("restituisce strategie per not_found", () => { expect(adaptiveSolver.strategiesFor("not_found").length).toBeGreaterThan(0); }); it("restituisce strategie per permission", () => { expect(adaptiveSolver.strategiesFor("permission").length).toBeGreaterThan(0); }); it("restituisce array vuoto per unknown", () => { expect(adaptiveSolver.strategiesFor("unknown").length).toBe(0); }); }); // ─── solve ──────────────────────────────────────────────────────────────────── describe("adaptiveSolver.solve", () => { beforeEach(() => { _setTestDeps({ vfsListFn: () => [ { path: "/pages/app.html", content: "test" }, { path: "/scripts/main.js", content: 'console.log("hello")' }, ], memorySetFn: () => {}, fetchFn: async (url: string) => { if (url.includes("allorigins")) { return new Response( JSON.stringify({ contents: "proxied content ok — risposta dal proxy CORS automatico" }), { status: 200 }, ); } return new Response("ok response from server", { status: 200 }); }, waitFn: async () => {}, }); }); it("unknown error → strategy none, success false", async () => { const r = await adaptiveSolver.solve("something totally unrecognized xyz 123"); expect(r.success).toBe(false); expect(r.strategy).toBe("none"); }); it("CORS error → cors_proxy → success true", async () => { const r = await adaptiveSolver.solve( "CORS blocked", 'fetch("https://api.example.com/data")', ); expect(r.strategy).toBe("cors_proxy"); expect(r.success).toBe(true); expect(r.output).toContain("proxy CORS"); }); it("not_found → file_fuzzy trova file esistente", async () => { const r = await adaptiveSolver.solve( "404 not found", 'read_file("/pages/app.html")', ); expect(r.strategy).toBe("file_fuzzy"); expect(r.output).toContain("trovato"); expect(r.success).toBe(true); }); it("not_found → file non esistente → elenca disponibili", async () => { const r = await adaptiveSolver.solve( "404 not found", 'read_file("/missing/ghost.ts")', ); expect(r.strategy).toBe("file_fuzzy"); expect(r.output).toContain("File disponibili"); expect(r.success).toBe(false); }); it("permission + contesto push → permission_rest con hint REST", async () => { const r = await adaptiveSolver.solve( "403 Forbidden: access denied", "push to remote", "push bloccato", ); expect(r.strategy).toBe("permission_rest"); expect(r.output).toContain("REST"); }); it("syntax error → syntax_hint con analisi riga", async () => { const r = await adaptiveSolver.solve( "SyntaxError: Unexpected token { at line 3", "line1\nline2\nlet { broken", ); expect(r.strategy).toBe("syntax_hint"); expect(r.output).toContain("sintattico"); }); it("quota + input piccolo → chunk_payload non applicabile → exhausted", async () => { const r = await adaptiveSolver.solve("QuotaExceededError: storage full", "small input"); expect(r.strategy).toBe("exhausted"); }); it("quota + input grande → chunk_payload applicabile", async () => { const bigInput = "x".repeat(6000); const r = await adaptiveSolver.solve("QuotaExceededError: storage full", bigInput); expect(r.strategy).toBe("chunk_payload"); expect(r.output).toContain("chunk"); }); it("description sempre leggibile con freccia", async () => { const r = await adaptiveSolver.solve( "CORS blocked", 'fetch("https://x.com")', ); expect(r.description).toContain("→"); expect(r.description.length).toBeGreaterThan(10); }); it("CORS senza URL → nessun proxy possibile → exhausted", async () => { const r = await adaptiveSolver.solve("CORS blocked", "nessun url qui"); expect(r.strategy).toBe("exhausted"); }); });