Files
bmp-project/packages/summarizer/privacy.test.ts
syntaxbullet 99569d2cf3 initial commit
2026-05-13 17:26:13 +02:00

165 lines
5.6 KiB
TypeScript

import { expect, test } from "bun:test";
import {
buildLlmDossier,
createPseudonymizer,
reversePseudonymsInText,
reversePseudonymsInDossier,
} from "./privacy";
import type { LlmDossier, SummaryData } from "./privacy";
const sample: SummaryData = {
kontakt: {
unternehmen: {
name: "Muster GmbH",
adresse: "Hauptstrasse 1",
plz: "80331 Muenchen",
telefon: "+49 89 123456",
email: "kontakt@muster.de",
web: "www.muster.de",
},
ansprechpartner: {
name: "Max Mustermann",
telefon: "0170 1234567",
email: "max.mustermann@muster.de",
},
},
unternehmen: {
branche: "Maschinenbau",
gruendungsjahr: "1998",
anzahl_mitarbeiter: "120",
interne_notiz: "Nicht fuer LLM",
},
fragen: {
frage1: {
label: "Frage 1",
antwort: "Max Mustermann beschreibt die Entwicklung der Muster GmbH. Muster investiert weiter. Kontakt: max.mustermann@muster.de.",
fragen: [
{
id: "frage1_1",
text: "Was macht das Unternehmen aus?",
antwort: "Die Muster GmbH ist unter +49 89 123456 erreichbar.",
},
],
},
},
kriterium: {
robustheit_resilienz: {
label: "Robustheit",
antwort: "Die Muster GmbH hat ein Risikomanagement etabliert. Muster nutzt Fruehwarnprozesse.",
},
},
};
test("builds a minimized dossier without contact data or unknown company fields", () => {
const dossier = buildLlmDossier(sample);
const serialized = JSON.stringify(dossier);
expect(dossier.unternehmen).toEqual({
branche: "Maschinenbau",
gruendungsjahr: "1998",
anzahl_mitarbeiter: "120",
});
expect(serialized).not.toContain("kontakt");
expect(serialized).not.toContain("interne_notiz");
expect(serialized).not.toContain("Hauptstrasse");
});
test("pseudonymizes known personal and contact values in LLM-bound strings", () => {
const privacy = createPseudonymizer(sample);
const prepared = privacy.pseudonymizeDossier(buildLlmDossier(sample));
const serialized = JSON.stringify(prepared.safe);
expect(serialized).toContain("[PERSON_1]");
expect(serialized).toContain("[UNTERNEHMEN]");
expect(serialized).toContain("[EMAIL_2]");
expect(serialized).toContain("[TELEFON_1]");
expect(serialized).not.toContain("Max Mustermann");
expect(serialized).not.toContain("Muster GmbH");
expect(serialized).not.toContain("Muster investiert");
expect(serialized).not.toContain("Muster nutzt");
expect(serialized).not.toContain("max.mustermann@muster.de");
expect(prepared.audit.mode).toBe("minimized+pseudonymized");
expect(prepared.audit.companyName).toBe("Muster GmbH");
expect(prepared.audit.replacements.length).toBeGreaterThan(0);
});
test("reversePseudonymsInText restores original values from known replacements", () => {
const text = "[PERSON_1] von [UNTERNEHMEN] nutzt [EMAIL_2].";
const reversed = reversePseudonymsInText(text, [
{ value: "Max Mustermann", type: "person", placeholder: "[PERSON_1]" },
{ value: "Muster GmbH", type: "company", placeholder: "[UNTERNEHMEN]" },
{ value: "max.mustermann@muster.de", type: "email", placeholder: "[EMAIL_2]" },
]);
expect(reversed).toBe("Max Mustermann von Muster GmbH nutzt max.mustermann@muster.de.");
});
test("reversePseudonyms works via the Pseudonymizer API", () => {
const privacy = createPseudonymizer(sample);
const reversed = privacy.reversePseudonyms("[PERSON_1] arbeitet bei [UNTERNEHMEN].");
expect(reversed).toContain("Max Mustermann");
expect(reversed).toContain("Muster GmbH");
});
test("reversePseudonymsInText handles text without placeholders", () => {
const text = "Kein Platzhalter hier.";
const reversed = reversePseudonymsInText(text, [
{ value: "Max Mustermann", type: "person", placeholder: "[PERSON_1]" },
]);
expect(reversed).toBe(text);
});
test("reversePseudonymsInDossier reverses nested text fields including zusammenfassung", () => {
const dossier: LlmDossier = {
fragen: {
frage1: {
label: "[PERSON_1] von [UNTERNEHMEN] beantwortet",
zusammenfassung: "[PERSON_1] arbeitet bei [UNTERNEHMEN] und nutzt [EMAIL_2].",
fragen: [
{ id: "f1", text: "Was macht [UNTERNEHMEN]?", antwort: "[PERSON_1] sagt [EMAIL_2]." },
],
},
},
kriterium: {
robustheit: {
label: "Robustheit von [PERSON_1]",
zusammenfassung: "[UNTERNEHMEN] ist robust.",
},
},
unternehmen: {
referenzen: "Referenz von [PERSON_1] bei [UNTERNEHMEN].",
},
};
const reversed = reversePseudonymsInDossier(dossier, [
{ value: "Max Mustermann", type: "person", placeholder: "[PERSON_1]" },
{ value: "Muster GmbH", type: "company", placeholder: "[UNTERNEHMEN]" },
{ value: "max@muster.de", type: "email", placeholder: "[EMAIL_2]" },
]);
expect(reversed.fragen?.frage1?.zusammenfassung).toBe(
"Max Mustermann arbeitet bei Muster GmbH und nutzt max@muster.de.",
);
expect(reversed.fragen?.frage1?.fragen?.[0]?.antwort).toBe(
"Max Mustermann sagt max@muster.de.",
);
expect(reversed.fragen?.frage1?.label).toBe(
"Max Mustermann von Muster GmbH beantwortet",
);
expect(reversed.kriterium?.robustheit?.zusammenfassung).toBe(
"Muster GmbH ist robust.",
);
expect(reversed.unternehmen?.referenzen).toBe(
"Referenz von Max Mustermann bei Muster GmbH.",
);
});
test("reversePseudonymsInText handles edge case with numeric suffixes in text", () => {
const text = "[PERSON_1] und [PERSON_1].";
const reversed = reversePseudonymsInText(text, [
{ value: "Max Mustermann", type: "person", placeholder: "[PERSON_1]" },
]);
expect(reversed).toBe("Max Mustermann und Max Mustermann.");
});