import { expect, it } from "bun:test"; import { canonicalPerson, filenameScanText, groupReviewHits, makeTextBatches, mappingForSelection, mergePeople, spansBySegment, withoutPlaceholderHits, withoutWhitelistedHits, withoutWhitelistedRules, } from "../src/lib/project-review"; it("liest Unterstriche in Namen als Leerzeichen und bewahrt bekannte Codebegriffe", () => { expect(filenameScanText("Max_Mustermann_Abgabe", [])) .toBe("Max Mustermann Abgabe"); expect(filenameScanText("Max_Mustermann_KundenSuite_X", ["KundenSuite_X"])) .toBe("Max Mustermann KundenSuite_X"); expect(filenameScanText("😀_Max_Mustermann", [])) .toBe("😀 Max Mustermann"); expect(filenameScanText("PERSON_01_Max_Mustermann", ["PERSON_01"])) .toBe("PERSON_01 Max Mustermann"); }); it("ordnet gebündelte API-Treffer nach Unicode-Positionen den richtigen Dateien zu", () => { const [batch] = makeTextBatches([ { key: "eins.txt", text: "😀 Max" }, { key: "zwei.txt", text: "Max und Max" }, ]); expect(batch.segments).toHaveLength(2); const second = batch.segments[1]; const spans = spansBySegment(batch, [ { start: 2, end: 5, original: "Max" }, { start: second.start, end: second.start + 3, original: "Max" }, { start: batch.segments[0].end, end: second.start + 1, original: "Grenztreffer" }, ]); expect(spans.get("eins.txt")).toEqual([{ start: 2, end: 5, original: "Max" }]); expect(spans.get("zwei.txt")).toEqual([{ start: 0, end: 3, original: "Max" }]); expect(makeTextBatches([{ key: "a", text: "abc" }, { key: "b", text: "def" }], 5)).toHaveLength(2); }); it("zeigt gleiche Zuordnungen einmal mit der Anzahl ihrer Vorkommen", () => { const hits = [ { id: "a", type: "PERSON", original: "Max", placeholder: "PERSON_01", file: "a.txt" }, { id: "b", type: "PERSON", original: "Max", placeholder: "PERSON_01", file: "b.txt" }, { id: "c", type: "PERSON", original: "Max", placeholder: "PERSON_02", file: "c.txt" }, { id: "d", type: "ORT", original: "Max", placeholder: "ORT_01", file: "a.txt" }, ]; const groups = groupReviewHits(hits); expect(groups).toHaveLength(3); expect(groups.find((group) => group.placeholder === "PERSON_01")?.hits).toHaveLength(2); expect(groups.find((group) => group.placeholder === "PERSON_01")?.hits.map((hit) => hit.id)).toEqual(["a", "b"]); }); it("unterdrückt projektweit nur exakt abgewählte Begriffe samt erzwungener Regel", () => { const whitelist = [{ type: "PERSON", original: " Max Muster " }]; const hits = [ { id: "1", type: "PERSON", original: "max muster", placeholder: "PERSON_01" }, { id: "2", type: "ORG", original: "Max Muster", placeholder: "ORG_01" }, { id: "3", type: "PERSON", original: "Max Mustermann", placeholder: "PERSON_02" }, ]; expect(withoutWhitelistedHits(hits, whitelist).map((hit) => hit.id)).toEqual(["2", "3"]); expect(withoutWhitelistedRules([ { type: "PERSON", value: "Max Muster" }, { type: "ORG", value: "Max Muster" }, ], whitelist)).toEqual([{ type: "ORG", value: "Max Muster" }]); }); it("zeigt beim erneuten Lauf keine Treffer, die einen bestehenden Platzhalter einschließen", () => { const hits = [ { type: "PERSON", original: "PERSON_39, Roman" }, { type: "KUNDE", original: "KUNDE_100005" }, { type: "PERSON", original: "Roman" }, { type: "PERSON", original: "Anlage44" }, { type: "PASSWORT", original: "ORT_08!" }, ]; expect(withoutPlaceholderHits(hits)).toEqual(hits.slice(2)); expect(withoutPlaceholderHits([ { type: "PERSON", original: "KUNDE_100005Service" }, { type: "PERSON", original: "Roman" }, ], ["KUNDE_100005"])).toEqual([{ type: "PERSON", original: "Roman" }]); }); it("fasst ausgewählte Personen zu einem kanonischen Platzhalter zusammen und bewahrt alte Zuordnungen", () => { let merges = mergePeople({}, "PERSON_02", "PERSON_01"); merges = mergePeople(merges, "PERSON_03", "PERSON_02"); expect(canonicalPerson("PERSON_03", merges)).toBe("PERSON_01"); const previous = { PERSON_09: { original: "Alt", type: "PERSON" } }; const preview = { ...previous, PERSON_01: { original: "Franz Fuchs", type: "PERSON" }, PERSON_02: { original: "Franz", type: "PERSON" }, PERSON_03: { original: "Hr. Fuchs", type: "PERSON" }, PERSON_04: { original: "Fehlalarm", type: "PERSON" }, }; const accepted = ["PERSON_01", "PERSON_02", "PERSON_03"].map((placeholder) => ({ id: placeholder, type: "PERSON", original: preview[placeholder as keyof typeof preview].original, placeholder, })); const mapping = mappingForSelection(preview, previous, accepted, merges); expect(mapping.PERSON_02.merged_into).toBe("PERSON_01"); expect(mapping.PERSON_03.merged_into).toBe("PERSON_01"); expect(mapping.PERSON_09.original).toBe("Alt"); expect(mapping.PERSON_04).toBeUndefined(); });