Spaces:
Running
Running
| /** | |
| * 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: "<html>test</html>" }, | |
| { 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"); | |
| }); | |
| }); | |