AUDIT / src /lib /__tests__ /proofLedgerChain.test.ts
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 2)
cc11e77 verified
Raw
History Blame Contribute Delete
12.3 kB
/**
* proofLedgerChain.test.ts — Regressione GAP-TRUTH-2
*
* Verifica la correttezza della catena proof-ledger dopo la rimozione della
* doppia scrittura introdotta erroneamente in toolExecutor.ts (GAP-TRUTH-2).
*
* BUG documentato (pre-fix):
* toolExecutor.ts registrava proofLedger.record({ verified: true }) nel
* momento del [✓ VERIFIED] stamp testuale, PRIMA che verifyToolResult()
* in executeToolGatedFactory.ts potesse valutare la qualità del risultato.
* Risultato: un git_push senza SHA nel result riceveva comunque una entry
* { verified: true } nel ledger → checkClaimsAgainstLedger non emetteva
* warning → falso silenzio su claim non provate.
*
* Copertura (7 test):
* 1. BUG: doppia scrittura {true, false} → problems=[] (silenzio sbagliato)
* 2. FIXED: singola scrittura {false} → problems.length===1 (warning corretto)
* 3. Happy path: singola scrittura {true} → problems=[] (silenzio corretto)
* 4. Ledger vuoto su claim → problems "nessuna azione registrata"
* 5. Doppia categoria: file_write non provata, git_push provata → solo file_write in problems
* 6. reset() garantisce isolamento tra run agente
* 7. Tutte le categorie: ogni CATEGORY_TO_LEDGER_ACTIONS copre i tool corretti
*
* SESSIONE: GAP-TRUTH-2
*/
import { describe, it, expect, beforeEach } from "vitest";
import { proofLedger } from "@/lib/agentLoop/proofLedger";
import { checkClaimsAgainstLedger } from "@/lib/agentLoop/actionClaimDetector";
import { CATEGORY_TO_LEDGER_ACTIONS, PROOF_VERIFIED_TOOLS, TOOL_TO_CATEGORY_LABEL } from "@/lib/agentLoop/proofToolRegistry";
import type { ClaimCategory } from "@/lib/agentLoop/proofToolRegistry";
// Reset prima di ogni test — fondamentale per isolamento
beforeEach(() => {
proofLedger.reset();
});
// ─── T1: BUG scenario — documenta la regressione ─────────────────────────────
describe("T1 — BUG scenario (doppia scrittura, pre-fix)", () => {
it(
"verified:true poi verified:false → entries.some(verified)=true → problems=[] (silenzio sbagliato)",
() => {
// Simula il vecchio toolExecutor: record verified:true al momento dello stamp
proofLedger.record({
action: "git_push",
verified: true,
evidence: "✅ Push completato", // nessun SHA — risultato non verificabile
ts: Date.now(),
});
// Poi executeToolGatedFactory: verifyToolResult → SHA assente → verified:false
proofLedger.record({
action: "git_push",
verified: false,
evidence: "Push ok ma SHA non trovato nel result",
ts: Date.now(),
});
const snapshot = proofLedger.snapshot();
expect(snapshot).toHaveLength(2);
const claimed = new Set<ClaimCategory>(["git_push"]);
const problems = checkClaimsAgainstLedger(claimed, snapshot);
// BUG: entries.some(e => e.verified) trova il primo record (true)
// → warning non emesso → l'utente NON viene avvertito del push non provato
expect(problems).toHaveLength(0); // documenta il comportamento SCORRETTO
},
);
});
// ─── T2: FIXED scenario — comportamento corretto post-fix ────────────────────
describe("T2 — FIXED scenario (singola scrittura da verifier)", () => {
it(
"solo verified:false → entries.some(verified)=false → warning emesso",
() => {
// Post-fix: solo executeToolGatedFactory registra, con verità verificata
proofLedger.record({
action: "git_push",
verified: false,
evidence: "✅ Push completato — SHA non trovato nel result (proof assente)",
ts: Date.now(),
});
const snapshot = proofLedger.snapshot();
expect(snapshot).toHaveLength(1);
const claimed = new Set<ClaimCategory>(["git_push"]);
const problems = checkClaimsAgainstLedger(claimed, snapshot);
// CORRETTO: solo entry con verified:false → warning emesso
expect(problems).toHaveLength(1);
expect(problems[0]).toMatch(/commit\/push su Git/);
expect(problems[0]).toMatch(/eseguito ma non verificato/);
},
);
it(
"verified:true (SHA reale nel result) → problems=[] (silenzio corretto)",
() => {
proofLedger.record({
action: "git_push",
verified: true,
evidence: "SHA=a1b2c3d4e5f6 pushed to origin/main",
ts: Date.now(),
});
const snapshot = proofLedger.snapshot();
const claimed = new Set<ClaimCategory>(["git_push"]);
const problems = checkClaimsAgainstLedger(claimed, snapshot);
// Prova presente e verificata → silenzio corretto
expect(problems).toHaveLength(0);
},
);
it(
"ledger vuoto per categoria dichiarata → 'nessuna azione registrata'",
() => {
// Ledger vuoto (reset() già chiamato in beforeEach)
const snapshot = proofLedger.snapshot();
expect(snapshot).toHaveLength(0);
const claimed = new Set<ClaimCategory>(["deploy"]);
const problems = checkClaimsAgainstLedger(claimed, snapshot);
expect(problems).toHaveLength(1);
expect(problems[0]).toMatch(/deploy/);
expect(problems[0]).toMatch(/nessuna azione registrata/);
},
);
});
// ─── T3: Scenari multi-categoria ──────────────────────────────────────────────
describe("T3 — Multi-categoria", () => {
it(
"file_write non provata, git_push provata → solo file_write in problems",
() => {
// git_push verificato
proofLedger.record({ action: "git_push", verified: true, evidence: "sha:xyz789", ts: Date.now() });
// write_file non verificato (es. errore ENOENT)
proofLedger.record({ action: "write_file", verified: false, evidence: "❌ ENOENT", ts: Date.now() });
const snapshot = proofLedger.snapshot();
const claimed = new Set<ClaimCategory>(["git_push", "file_write"]);
const problems = checkClaimsAgainstLedger(claimed, snapshot);
expect(problems).toHaveLength(1);
expect(problems[0]).toMatch(/scrittura\/modifica file/);
expect(problems[0]).not.toMatch(/commit\/push su Git/);
},
);
it(
"apply_patch verified:true copre la categoria file_write → problems=[]",
() => {
proofLedger.record({ action: "apply_patch", verified: true, evidence: "patch applied", ts: Date.now() });
const snapshot = proofLedger.snapshot();
const claimed = new Set<ClaimCategory>(["file_write"]);
const problems = checkClaimsAgainstLedger(claimed, snapshot);
expect(problems).toHaveLength(0);
},
);
});
// ─── T4: Isolamento sessione ───────────────────────────────────────────────────
describe("T4 — Isolamento sessione via reset()", () => {
it(
"reset() garantisce che prove della sessione precedente non contaminino quella corrente",
() => {
// Sessione 1: registra prove
proofLedger.record({ action: "git_push", verified: true, evidence: "sha:aaa", ts: Date.now() });
expect(proofLedger.count()).toBe(1);
// Reset (chiamato da agentLoop prima di ogni run)
proofLedger.reset();
expect(proofLedger.count()).toBe(0);
// Sessione 2: ledger pulito — claim git_push non ha prove
const snapshot = proofLedger.snapshot();
const claimed = new Set<ClaimCategory>(["git_push"]);
const problems = checkClaimsAgainstLedger(claimed, snapshot);
expect(problems).toHaveLength(1);
expect(problems[0]).toMatch(/nessuna azione registrata/);
},
);
});
// ─── T5: Completezza CATEGORY_TO_LEDGER_ACTIONS ───────────────────────────────
describe("T5 — Completezza registry (ogni tool in PROOF_VERIFIED_TOOLS ha una categoria)", () => {
it(
"ogni tool in CATEGORY_TO_LEDGER_ACTIONS è coperto da una categoria (no orfani)",
() => {
const allMapped = Object.values(CATEGORY_TO_LEDGER_ACTIONS).flat();
const cats = Object.keys(CATEGORY_TO_LEDGER_ACTIONS) as ClaimCategory[];
for (const tool of allMapped) {
const belongsToCategory = cats.some(cat =>
CATEGORY_TO_LEDGER_ACTIONS[cat].includes(tool),
);
expect(belongsToCategory, `${tool} non appartiene a nessuna categoria`).toBe(true);
}
},
);
it(
"git_push_multiple è nella categoria git_push (verifica alias variante)",
() => {
proofLedger.record({ action: "git_push_multiple", verified: false, evidence: "auth error", ts: Date.now() });
const snapshot = proofLedger.snapshot();
const claimed = new Set<ClaimCategory>(["git_push"]);
const problems = checkClaimsAgainstLedger(claimed, snapshot);
// git_push_multiple è nelle azioni di git_push → catturato correttamente
expect(problems).toHaveLength(1);
expect(problems[0]).toMatch(/eseguito ma non verificato/);
},
);
});
// ─── T6: Regressione create_file in PROOF_VERIFIED_TOOLS ──────────────────────
// Riproduce il bug dove emitFinalText._CRIT_TOOLS locale mancava create_file.
// La 5ª copia locale aveva: write_file, apply_patch, create_project, run_code,
// execute_shell, run_python, git_push, git_push_multiple, deploy_trigger (9 tool).
// PROOF_VERIFIED_TOOLS ha 10 tool — create_file era il buco.
describe("T6 — Regressione: create_file presente in PROOF_VERIFIED_TOOLS", () => {
it(
"create_file è in PROOF_VERIFIED_TOOLS (non deve mai sparire)",
() => {
expect(PROOF_VERIFIED_TOOLS.has("create_file")).toBe(true);
},
);
it(
"create_file non verificato → warning in checkClaimsAgainstLedger",
() => {
proofLedger.record({ action: "create_file", verified: false, evidence: "write failed", ts: Date.now() });
const snapshot = proofLedger.snapshot();
const claimed = new Set<ClaimCategory>(["file_write"]);
const problems = checkClaimsAgainstLedger(claimed, snapshot);
expect(problems).toHaveLength(1);
expect(problems[0]).toMatch(/eseguito ma non verificato/);
},
);
it(
"create_file verificato → nessun warning",
() => {
proofLedger.record({ action: "create_file", verified: true, evidence: "ok:sha123", ts: Date.now() });
const snapshot = proofLedger.snapshot();
const claimed = new Set<ClaimCategory>(["file_write"]);
const problems = checkClaimsAgainstLedger(claimed, snapshot);
expect(problems).toHaveLength(0);
},
);
});
// ─── T7: Validazione TOOL_TO_CATEGORY_LABEL derived map ──────────────────────
// Verifica che TOOL_TO_CATEGORY_LABEL (la mappa derivata che ha sostituito _critLabel())
// copra tutti i tool in PROOF_VERIFIED_TOOLS e ritorni label leggibili.
describe("T7 — TOOL_TO_CATEGORY_LABEL copertura completa", () => {
it(
"ogni tool in PROOF_VERIFIED_TOOLS ha un entry in TOOL_TO_CATEGORY_LABEL",
() => {
for (const tool of PROOF_VERIFIED_TOOLS) {
const label = TOOL_TO_CATEGORY_LABEL.get(tool);
expect(label, `${tool} non ha label in TOOL_TO_CATEGORY_LABEL`).toBeDefined();
expect(label!.length, `${tool} ha label vuota`).toBeGreaterThan(0);
}
},
);
it(
"git_push → 'commit/push su Git' · write_file → 'scrittura/modifica file' · deploy_trigger → 'deploy'",
() => {
expect(TOOL_TO_CATEGORY_LABEL.get("git_push")).toBe("commit/push su Git");
expect(TOOL_TO_CATEGORY_LABEL.get("write_file")).toBe("scrittura/modifica file");
expect(TOOL_TO_CATEGORY_LABEL.get("deploy_trigger")).toBe("deploy");
expect(TOOL_TO_CATEGORY_LABEL.get("create_file")).toBe("scrittura/modifica file");
},
);
it(
"TOOL_TO_CATEGORY_LABEL dimensione = PROOF_VERIFIED_TOOLS dimensione (bijection check)",
() => {
// Ogni tool in PROOF_VERIFIED_TOOLS deve avere esattamente 1 label (no duplicati, no orfani).
expect(TOOL_TO_CATEGORY_LABEL.size).toBe(PROOF_VERIFIED_TOOLS.size);
},
);
});