AUDIT / src /lib /__tests__ /proceduralMemory.test.ts
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 2)
cc11e77 verified
Raw
History Blame Contribute Delete
4.26 kB
import { describe, it, expect, beforeEach } from "vitest";
// Step4: Procedural Memory — getStrategyFor() unit tests
// Tests against a minimal mock of SelfLearningEngine to isolate the logic.
interface Lesson {
q: string; a: string; kw: string[]; uses: number; score: number;
toolSequence?: string[];
}
const STOPWORDS = new Set(["il","la","di","a","e","che","non","per","con","in","un","una","dei"]);
function extractKeywords(text: string): string[] {
return text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/)
.filter(w => w.length > 2 && !STOPWORDS.has(w)).slice(0, 40);
}
function keywordSimilarity(kw1: string[], kw2: string[]): number {
if (!kw1.length || !kw2.length) return 0;
const set1 = new Set(kw1);
const intersection = kw2.filter(k => set1.has(k)).length;
return intersection / Math.max(set1.size, kw2.length);
}
function getStrategyFor(goal: string, lessons: Lesson[]): string | null {
const kw = extractKeywords(goal);
const withSeq = lessons.filter(l => l.toolSequence && l.toolSequence.length > 0);
if (!withSeq.length) return null;
const matches = withSeq
.map(l => ({ l, sim: keywordSimilarity(kw, l.kw) }))
.filter(x => x.sim > 0.4)
.sort((a, b) => b.sim * b.l.score - a.sim * a.l.score);
if (!matches.length) return null;
const { l } = matches[0];
return `[STRATEGIA SUGGERITA (validata ${l.uses}x, score ${Math.round(l.score*100)}%): usa sequenza ${l.toolSequence!.join(" → ")}]`;
}
const BASE_LESSONS: Lesson[] = [
{ q: "push file su github", a: "ok", kw: ["push","file","github"], uses: 5, score: 0.9, toolSequence: ["git_push","check_deploy"] },
{ q: "aggiorna readme markdown", a: "ok", kw: ["aggiorna","readme","markdown"], uses: 3, score: 0.8, toolSequence: ["write_file","git_push"] },
{ q: "test vitest unit", a: "ok", kw: ["test","vitest","unit"], uses: 2, score: 0.7, toolSequence: ["run_tests"] },
{ q: "cerca notizie web", a: "ok", kw: ["cerca","notizie","web"], uses: 4, score: 0.85 }, // no toolSequence
];
describe("getStrategyFor (Step4 – Procedural Memory)", () => {
it("returns null when no lessons have toolSequence", () => {
const lessons = [{ q:"q", a:"a", kw:["x"], uses:1, score:0.8 }];
expect(getStrategyFor("test", lessons)).toBeNull();
});
it("returns null when no lessons match the goal (sim < 0.4)", () => {
expect(getStrategyFor("cucina italiana pasta", BASE_LESSONS)).toBeNull();
});
it("returns strategy hint for matching goal (github push)", () => {
const hint = getStrategyFor("push file su github repo", BASE_LESSONS);
expect(hint).toMatch(/git_push/);
expect(hint).toMatch(/check_deploy/);
});
it("returns null for lesson without toolSequence even if keyword match is high", () => {
const lessons: Lesson[] = [
{ q:"cerca notizie web", a:"ok", kw:["cerca","notizie","web"], uses:10, score:0.95 },
];
expect(getStrategyFor("cerca notizie web", lessons)).toBeNull();
});
it("hint includes usage count", () => {
const hint = getStrategyFor("push file github", BASE_LESSONS);
expect(hint).toContain("5x");
});
it("hint includes score percentage", () => {
const hint = getStrategyFor("push file github", BASE_LESSONS);
expect(hint).toContain("90%");
});
it("prefers higher-scoring match over lower-scoring when both qualify", () => {
const lessons: Lesson[] = [
{ q:"github push", a:"a", kw:["github","push"], uses:1, score:0.5, toolSequence:["A","B"] },
{ q:"github push best", a:"b", kw:["github","push"], uses:1, score:0.95, toolSequence:["C","D"] },
];
const hint = getStrategyFor("github push", lessons);
expect(hint).toContain("C → D");
});
it("uses → separator in tool sequence", () => {
const hint = getStrategyFor("push github", BASE_LESSONS);
expect(hint).toContain("→");
});
it("returns null for empty lessons array", () => {
expect(getStrategyFor("anything", [])).toBeNull();
});
it("returns null for empty goal string", () => {
expect(getStrategyFor("", BASE_LESSONS)).toBeNull();
});
it("formats hint with STRATEGIA SUGGERITA prefix", () => {
const hint = getStrategyFor("push file github", BASE_LESSONS);
expect(hint).toContain("[STRATEGIA SUGGERITA");
});
});