AUDIT / src /lib /__tests__ /contextBridge.test.ts
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 2)
cc11e77 verified
Raw
History Blame Contribute Delete
19 kB
/**
* contextBridge.test.ts — Unit test S29
*
* Testa contextBridge:
* - enrichSystemPrompt: output compatto + sezioni corrette + max chars
* - enrichSystemPrompt: vuoto se brain è empty
* - enrichSystemPrompt: rilevanza pagine filtrata per userMsg
* - enrichSystemPrompt: non crasha mai (try/catch)
* - recordToolResult: read_page → pageCache.set
* - recordToolResult: fetch_url → pageCache.set
* - recordToolResult: write_file → docIndex.add con lang corretto
* - recordToolResult: ❌ errore → errorLog.record
* - recordToolResult: tool sconosciuto non crasha
* - recordToolResult: url mancante → no call a pageCache
* - recordToolResult: path mancante → no call a docIndex
* - _setTestBrain: sostituisce brain reale
*
* Mock: _setTestBrain() (DI) — zero vi.mock su moduli globali.
*/
import { describe, it, expect, vi, beforeAll, beforeEach } from "vitest";
import {
enrichSystemPrompt,
recordToolResult,
_setTestBrain,
} from "@/lib/contextBridge";
import type { agentBrain } from "@/lib/agentBrain";
// ─── Mock brain factory ────────────────────────────────────────────────────────
type BrainFacade = typeof agentBrain;
function makeMockBrain(overrides: {
resolvedWithFix?: (_lang?: string) => Array<{ message: string; fix?: string }>;
recent?: (_n?: number) => ReturnType<BrainFacade["pageCache"]["recent"]>;
docSearch?: (_q: string) => ReturnType<BrainFacade["docIndex"]["search"]>;
pageSet?: ReturnType<typeof vi.fn>;
errorRecord?: ReturnType<typeof vi.fn>;
docAdd?: ReturnType<typeof vi.fn>;
} = {}): BrainFacade {
const pageSet = overrides.pageSet ?? vi.fn();
const errorRecord = overrides.errorRecord ?? vi.fn();
const docAdd = overrides.docAdd ?? vi.fn();
return {
pageCache: {
set: pageSet,
recent: overrides.recent ?? ((_n?: number) => []),
search: (_q: string) => [],
get: (_url: string) => null,
has: (_url: string) => false,
byDomain: (_d: string) => [],
size: () => 0,
clear: () => {},
},
errorLog: {
resolvedWithFix: overrides.resolvedWithFix ?? ((_lang?: string) => []),
record: errorRecord,
markFixed: (_h: string, _f: string) => false,
findSimilar: (_m: string) => [],
unresolved: (_l?: string) => [],
recent: (_n?: number) => [],
hashOf: (_m: string) => "",
size: () => 0,
clear: () => {},
},
docIndex: {
search: overrides.docSearch ?? ((_q: string) => []),
add: docAdd,
get: (_p: string) => null,
has: (_p: string) => false,
isStale: (_p: string, _c: string) => true,
byTag: (_t: string) => [],
byLang: (_l: string) => [],
recent: (_n?: number) => [],
size: () => 0,
clear: () => {},
},
initBrain: async () => {},
} as unknown as BrainFacade;
}
// ─── Setup ────────────────────────────────────────────────────────────────────
const emptyBrain = makeMockBrain();
beforeAll(() => {
_setTestBrain(emptyBrain);
});
beforeEach(() => {
vi.clearAllMocks();
_setTestBrain(emptyBrain);
});
// ─── enrichSystemPrompt — brain vuoto ────────────────────────────────────────
describe("enrichSystemPrompt — brain vuoto", () => {
it("ritorna stringa vuota se nessun dato", () => {
expect(enrichSystemPrompt("test query")).toBe("");
});
it("non crasha mai (try/catch interno)", () => {
const brokenBrain = makeMockBrain({
resolvedWithFix: () => { throw new Error("broken"); },
});
_setTestBrain(brokenBrain);
expect(() => enrichSystemPrompt("query")).not.toThrow();
expect(enrichSystemPrompt("query")).toBe("");
});
it("stringa query vuota non crasha", () => {
expect(() => enrichSystemPrompt("")).not.toThrow();
expect(enrichSystemPrompt("")).toBe("");
});
});
// ─── enrichSystemPrompt — fix noti ───────────────────────────────────────────
describe("enrichSystemPrompt — fix noti", () => {
it("include sezione [FIX NOTI] con errori risolti", () => {
const brain = makeMockBrain({
resolvedWithFix: () => [
{ message: "TS2353: Object literal extra property", fix: "rimuovi il campo extra" },
],
});
_setTestBrain(brain);
const result = enrichSystemPrompt("debug TypeScript");
expect(result).toContain("[FIX NOTI]");
expect(result).toContain("TS2353");
expect(result).toContain("rimuovi il campo extra");
});
it("max 3 fix nel contesto (MAX_ERRORS_IN_CTX)", () => {
const manyFixes = Array.from({ length: 10 }, (_, i) => ({
message: `Error${i}`, fix: `Fix${i}`,
}));
const brain = makeMockBrain({ resolvedWithFix: () => manyFixes });
_setTestBrain(brain);
const result = enrichSystemPrompt("query");
const fixLines = result.split("\n").filter(l => l.startsWith("•"));
expect(fixLines.length).toBeLessThanOrEqual(3);
});
it("non include [FIX NOTI] se nessun fix disponibile", () => {
const brain = makeMockBrain({ resolvedWithFix: () => [] });
_setTestBrain(brain);
expect(enrichSystemPrompt("query")).not.toContain("[FIX NOTI]");
});
it("tronca messaggi lunghi a 70 chars", () => {
const longMsg = "A".repeat(200);
const brain = makeMockBrain({
resolvedWithFix: () => [{ message: longMsg, fix: "B".repeat(200) }],
});
_setTestBrain(brain);
const result = enrichSystemPrompt("query");
const fixLine = result.split("\n").find(l => l.startsWith("•"));
expect(fixLine).toBeDefined();
// message + fix are each ≤70, so line ≤ ~150 chars (plus " → ")
expect((fixLine ?? "").length).toBeLessThanOrEqual(160);
});
});
// ─── enrichSystemPrompt — pagine lette ───────────────────────────────────────
describe("enrichSystemPrompt — pagine lette", () => {
it("include [PAGINE GIÀ LETTE] con le pagine recenti", () => {
const brain = makeMockBrain({
recent: () => [{
url: "https://react.dev/docs/hooks",
title: "React Hooks",
summary: "Hooks documentation",
domain: "react.dev",
visitedAt: Date.now(),
visits: 1,
size: 100,
}],
});
_setTestBrain(brain);
const result = enrichSystemPrompt("react hooks tutorial");
expect(result).toContain("[PAGINE GIÀ LETTE]");
expect(result).toContain("react.dev");
expect(result).toContain("React Hooks");
});
it("filtra per rilevanza: python.org più rilevante per query python", () => {
const brain = makeMockBrain({
recent: () => [
{ url: "https://react.dev", title: "React docs", summary: "react documentation hooks", domain: "react.dev", visitedAt: Date.now() - 1000, visits: 1, size: 100 },
{ url: "https://python.org", title: "Python docs", summary: "python programming language", domain: "python.org", visitedAt: Date.now(), visits: 1, size: 100 },
],
});
_setTestBrain(brain);
const result = enrichSystemPrompt("python programming tutorial");
expect(result).toContain("python.org");
});
it("S97: nessun fallback cieco — pagine irrilevanti non appaiono nel prompt", () => {
const brain = makeMockBrain({
recent: () => [
{ url: "https://example.com/a", title: "Page A", summary: "content A", domain: "example.com", visitedAt: Date.now() - 2000, visits: 1, size: 100 },
{ url: "https://example.com/b", title: "Page B", summary: "content B", domain: "example.com", visitedAt: Date.now() - 1000, visits: 1, size: 100 },
],
});
_setTestBrain(brain);
const result = enrichSystemPrompt("completely unrelated zzz query");
// S97: rimosso fallback cieco (threshold 0.1 → 0.3) — pagine irrilevanti non vengono iniettate
expect(result).not.toContain("[PAGINE GIÀ LETTE]");
});
it("no [PAGINE GIÀ LETTE] se pageCache è vuoto", () => {
const brain = makeMockBrain({ recent: () => [] });
_setTestBrain(brain);
expect(enrichSystemPrompt("react")).not.toContain("[PAGINE GIÀ LETTE]");
});
});
// ─── enrichSystemPrompt — docs indicizzati ───────────────────────────────────
describe("enrichSystemPrompt — docs indicizzati", () => {
it("include [DOCS INDICIZZATI] con documenti rilevanti", () => {
const brain = makeMockBrain({
docSearch: () => [{
path: "src/lib/agentBrain",
title: "agentBrain.ts",
summary: "browser memory system",
tags: ["memory", "dexie"],
contentHash: "abc",
indexedAt: Date.now(),
lang: "typescript",
size: 500,
}],
});
_setTestBrain(brain);
const result = enrichSystemPrompt("memory dexie");
expect(result).toContain("[DOCS INDICIZZATI]");
expect(result).toContain("agentBrain");
});
it("aggiunge estensione .ts per typescript", () => {
const brain = makeMockBrain({
docSearch: () => [{
path: "src/lib/utils",
title: "utils",
summary: "utility functions",
tags: [],
contentHash: "x",
indexedAt: Date.now(),
lang: "typescript",
size: 100,
}],
});
_setTestBrain(brain);
expect(enrichSystemPrompt("utils")).toContain(".ts");
});
it("aggiunge estensione .py per python", () => {
const brain = makeMockBrain({
docSearch: () => [{
path: "scripts/runner",
title: "runner",
summary: "script",
tags: [],
contentHash: "y",
indexedAt: Date.now(),
lang: "python",
size: 100,
}],
});
_setTestBrain(brain);
expect(enrichSystemPrompt("script")).toContain(".py");
});
it("no [DOCS INDICIZZATI] se docIndex.search ritorna []", () => {
const brain = makeMockBrain({ docSearch: () => [] });
_setTestBrain(brain);
expect(enrichSystemPrompt("query")).not.toContain("[DOCS INDICIZZATI]");
});
});
// ─── enrichSystemPrompt — max chars ──────────────────────────────────────────
describe("enrichSystemPrompt — max chars", () => {
it("output ≤ 801 chars (800 + ellipsis)", () => {
const longFix = { message: "E".repeat(200), fix: "F".repeat(200) };
const brain = makeMockBrain({
resolvedWithFix: () => [longFix, longFix, longFix],
recent: () => [{
url: "https://example.com/" + "x".repeat(100),
title: "T".repeat(100),
summary: "S".repeat(500),
domain: "example.com",
visitedAt: Date.now(),
visits: 1,
size: 1000,
}],
});
_setTestBrain(brain);
const result = enrichSystemPrompt("query");
expect(result.length).toBeLessThanOrEqual(801);
});
it("aggiunge '…' se tronca", () => {
const longFix = { message: "E".repeat(300), fix: "F".repeat(300) };
const brain = makeMockBrain({ resolvedWithFix: () => [longFix, longFix, longFix] });
_setTestBrain(brain);
const result = enrichSystemPrompt("query");
if (result.length > 800) {
expect(result.endsWith("…")).toBe(true);
}
});
it("output breve se solo 1 fix corto — no troncamento", () => {
const brain = makeMockBrain({
resolvedWithFix: () => [{ message: "Short error", fix: "quick fix" }],
});
_setTestBrain(brain);
const result = enrichSystemPrompt("query");
expect(result.length).toBeLessThan(200);
expect(result.endsWith("…")).toBe(false);
});
});
// ─── recordToolResult — read_page ────────────────────────────────────────────
describe("recordToolResult — read_page", () => {
it("chiama pageCache.set con url, title estratto, summary, size", () => {
const pageSet = vi.fn();
_setTestBrain(makeMockBrain({ pageSet }));
recordToolResult("read_page", { url: "https://example.com" }, "# Hello World\nContent here");
expect(pageSet).toHaveBeenCalledOnce();
const [url, data] = pageSet.mock.calls[0]!;
expect(url).toBe("https://example.com");
expect(data.title).toBe("Hello World");
expect(data.summary).toContain("Content");
expect(data.size).toBeGreaterThan(0);
});
it("usa shortDomain come title se prima riga è vuota", () => {
const pageSet = vi.fn();
_setTestBrain(makeMockBrain({ pageSet }));
recordToolResult("read_page", { url: "https://github.com/user/repo" }, "\n\nno heading");
const [, data] = pageSet.mock.calls[0]!;
expect(data.title).toContain("github.com");
});
it("non chiama pageCache.set se url è assente", () => {
const pageSet = vi.fn();
_setTestBrain(makeMockBrain({ pageSet }));
recordToolResult("read_page", {}, "some content");
expect(pageSet).not.toHaveBeenCalled();
});
it("tronca summary a 500 chars", () => {
const pageSet = vi.fn();
_setTestBrain(makeMockBrain({ pageSet }));
recordToolResult("read_page", { url: "https://x.com" }, "T\n" + "C".repeat(1000));
const [, data] = pageSet.mock.calls[0]!;
expect(data.summary.length).toBeLessThanOrEqual(500);
});
});
// ─── recordToolResult — fetch_url ────────────────────────────────────────────
describe("recordToolResult — fetch_url", () => {
it("chiama pageCache.set come read_page", () => {
const pageSet = vi.fn();
_setTestBrain(makeMockBrain({ pageSet }));
recordToolResult("fetch_url", { url: "https://api.example.com/data" }, "json data");
expect(pageSet).toHaveBeenCalledOnce();
expect(pageSet.mock.calls[0]![0]).toBe("https://api.example.com/data");
});
});
// ─── recordToolResult — write_file ───────────────────────────────────────────
describe("recordToolResult — write_file", () => {
it("chiama docIndex.add con lang typescript per .ts", () => {
const docAdd = vi.fn();
_setTestBrain(makeMockBrain({ docAdd }));
recordToolResult(
"write_file",
{ path: "src/lib/myModule.ts", content: "export const x = 1;" },
"✅ Scritto"
);
expect(docAdd).toHaveBeenCalledOnce();
const [path, data] = docAdd.mock.calls[0]!;
expect(path).toBe("src/lib/myModule.ts");
expect(data.lang).toBe("typescript");
expect(data.title).toBe("myModule.ts");
});
it("lang python per .py", () => {
const docAdd = vi.fn();
_setTestBrain(makeMockBrain({ docAdd }));
recordToolResult("write_file", { path: "script.py", content: "print('hi')" }, "✅");
expect(docAdd.mock.calls[0]![1].lang).toBe("python");
});
it("lang javascript per .js", () => {
const docAdd = vi.fn();
_setTestBrain(makeMockBrain({ docAdd }));
recordToolResult("write_file", { path: "app.js", content: "const x = 1" }, "✅");
expect(docAdd.mock.calls[0]![1].lang).toBe("javascript");
});
it("lang typescript per .tsx", () => {
const docAdd = vi.fn();
_setTestBrain(makeMockBrain({ docAdd }));
recordToolResult("write_file", { path: "App.tsx", content: "<App/>" }, "✅");
expect(docAdd.mock.calls[0]![1].lang).toBe("typescript");
});
it("non chiama docIndex.add se path mancante", () => {
const docAdd = vi.fn();
_setTestBrain(makeMockBrain({ docAdd }));
recordToolResult("write_file", {}, "content");
expect(docAdd).not.toHaveBeenCalled();
});
it("usa nome file come title", () => {
const docAdd = vi.fn();
_setTestBrain(makeMockBrain({ docAdd }));
recordToolResult("write_file", { path: "deep/nested/file.ts", content: "x" }, "✅");
expect(docAdd.mock.calls[0]![1].title).toBe("file.ts");
});
});
// ─── recordToolResult — errori ────────────────────────────────────────────────
describe("recordToolResult — errori", () => {
it("chiama errorLog.record per result che inizia con ❌", () => {
const errorRecord = vi.fn();
_setTestBrain(makeMockBrain({ errorRecord }));
recordToolResult("web_search", { query: "test" }, "❌ Fetch failed: timeout");
expect(errorRecord).toHaveBeenCalledOnce();
const [msg, ctx] = errorRecord.mock.calls[0]!;
expect(msg).toContain("❌");
expect(ctx).toBe("tool:web_search");
});
it("run_code errore → lang=javascript", () => {
const errorRecord = vi.fn();
_setTestBrain(makeMockBrain({ errorRecord }));
recordToolResult("run_code", { code: "x()" }, "❌ ReferenceError: x is not defined");
expect(errorRecord.mock.calls[0]![2]).toBe("javascript");
});
it("execute_shell errore → lang=javascript", () => {
const errorRecord = vi.fn();
_setTestBrain(makeMockBrain({ errorRecord }));
recordToolResult("execute_shell", { command: "node x.js" }, "❌ Error: ENOENT");
expect(errorRecord.mock.calls[0]![2]).toBe("javascript");
});
it("web_search errore → lang=undefined", () => {
const errorRecord = vi.fn();
_setTestBrain(makeMockBrain({ errorRecord }));
recordToolResult("web_search", {}, "❌ Network error");
expect(errorRecord.mock.calls[0]![2]).toBeUndefined();
});
it("tool sconosciuto con successo → non crasha, non chiama nulla", () => {
const pageSet = vi.fn();
const docAdd = vi.fn();
const errorRecord = vi.fn();
_setTestBrain(makeMockBrain({ pageSet, docAdd, errorRecord }));
expect(() => recordToolResult("unknown_tool", {}, "some output")).not.toThrow();
expect(pageSet).not.toHaveBeenCalled();
expect(docAdd).not.toHaveBeenCalled();
expect(errorRecord).not.toHaveBeenCalled();
});
it("fire-and-forget: non lancia anche se brain è broken", () => {
const brokenBrain = makeMockBrain({
pageSet: vi.fn(() => { throw new Error("broken"); }),
errorRecord: vi.fn(() => { throw new Error("broken"); }),
docAdd: vi.fn(() => { throw new Error("broken"); }),
});
_setTestBrain(brokenBrain);
expect(() => recordToolResult("read_page", { url: "https://x.com" }, "content")).not.toThrow();
expect(() => recordToolResult("write_file", { path: "x.ts", content: "x" }, "✅")).not.toThrow();
expect(() => recordToolResult("web_search", {}, "❌ error")).not.toThrow();
});
});