initial commit
This commit is contained in:
164
packages/summarizer/privacy.test.ts
Normal file
164
packages/summarizer/privacy.test.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
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.");
|
||||
});
|
||||
Reference in New Issue
Block a user