feat(platzhalter): Aufloesung, Namensregeln und Shell-Maskierung (#26)
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s

This commit was merged in pull request #26.
This commit is contained in:
2026-09-25 13:30:31 +00:00
parent f85bc87324
commit eec5de404a
21 changed files with 4069 additions and 166 deletions
+175
View File
@@ -0,0 +1,175 @@
import { describe, expect, it } from "vitest";
import { PASSWORT_IM_DRUCK } from "@/lib/passwort";
import { platzhalterErsetzen, platzhalterErsetzenRoh } from "@/lib/platzhalter-werte";
/**
* Das Ersetzen der Platzhalter (#012).
*
* Hier entsteht der Text, den ein Techniker kopiert und auf einem
* Domaenencontroller ausfuehrt. Entsprechend dicht ist die Abdeckung: jede
* Regel, die der Auftrag nennt, hat einen Test, und jeder Weg, auf dem ein Wert
* einen Befehl zerbrechen koennte, auch.
*/
const WERTE = {
vorname: "Maria",
nachname: "Huber",
mitarbeiter: "Maria Huber",
abteilung: "Buchhaltung",
hostname: "PC-001",
};
describe("Ersetzen", () => {
it("ersetzt einen bekannten Platzhalter", () => {
expect(platzhalterErsetzen("Hallo {{vorname}}", WERTE).text).toBe("Hallo Maria");
});
it("erlaubt Leerzeichen in den Klammern", () => {
expect(platzhalterErsetzen("{{ vorname }} {{ nachname }}", WERTE).text).toBe("Maria Huber");
});
it("ersetzt denselben Platzhalter mehrfach", () => {
expect(platzhalterErsetzen("{{vorname}} und nochmal {{vorname}}", WERTE).text).toBe(
"Maria und nochmal Maria",
);
});
it("lässt einen unbekannten Platzhalter stehen und meldet ihn", () => {
// Akzeptanzkriterium: ein Tippfehler bleibt sichtbar. Eine leere Stelle im
// Befehl faellt niemandem auf, `{{tipfehler}}` schon.
const ergebnis = platzhalterErsetzen("New-ADUser -Name {{tipfehler}}", WERTE);
expect(ergebnis.text).toBe("New-ADUser -Name {{tipfehler}}");
expect(ergebnis.unbekannt).toEqual(["tipfehler"]);
});
it("macht einen bekannten Platzhalter ohne Wert leer", () => {
// Der Unterschied zum unbekannten: „hier gibt es das nicht“ ist etwas
// anderes als „falsch geschrieben“. `{{kunde}}` im Modus Intern ist leer.
const ergebnis = platzhalterErsetzen("Kunde: {{kunde}}!", WERTE);
expect(ergebnis.text).toBe("Kunde: !");
expect(ergebnis.unbekannt).toEqual([]);
});
it("rührt Text ohne Platzhalter nicht an", () => {
expect(platzhalterErsetzen("Nur Text { kein } Platzhalter", WERTE).text).toBe(
"Nur Text { kein } Platzhalter",
);
});
it("lässt einen falsch geschriebenen Platzhalter stehen und meldet ihn", () => {
/*
* Dieselbe Schluesselregel wie bei den Variablen (docs/vorlagen-format.md).
* Gemeldet wird er trotzdem: ein falsch geschriebener Platzhalter ist
* gefaehrlicher als ein unbekannter, weil er richtig aussieht. Bis zum
* Review von #012 blieb er still stehen.
*/
const text = "{{Vorname}} {{vor-name}} {{2te}}";
const ergebnis = platzhalterErsetzen(text, WERTE);
expect(ergebnis.text).toBe(text);
expect([...ergebnis.unbekannt].sort()).toEqual(["2te", "Vorname", "vor-name"]);
});
it("stolpert nicht über eine einzelne geschweifte Klammer", () => {
const text = "Pfad {C:\\} und {{vorname}}";
expect(platzhalterErsetzen(text, WERTE).text).toBe("Pfad {C:\\} und Maria");
});
it("setzt einen Wert mit $-Zeichen wörtlich ein", () => {
// `String.replace` deutet `$&`, `` $` ``, `$'` und `$1` im Ersatztext als
// Rueckverweise. Ein Nachname „A$&B“ haette sonst den gefundenen
// Platzhalter wieder eingesetzt.
for (const wert of ["A$&B", "vor $` danach", "A $' B", "Gruppe $1"]) {
expect(platzhalterErsetzenRoh("Name: {{nachname}}", { nachname: wert }).text, wert).toBe(
`Name: ${wert}`,
);
}
});
});
describe("Maskierung für einen Befehl", () => {
it("reicht jeden Wert durch die mitgegebene Maskierung", () => {
const ergebnis = platzhalterErsetzen(
'New-ADUser -Name "{{mitarbeiter}}"',
{ mitarbeiter: 'Anna "Anni" Meier' },
{ art: "befehl", maskieren: (wert) => wert.replaceAll('"', '""') },
);
expect(ergebnis.text).toBe('New-ADUser -Name "Anna ""Anni"" Meier"');
});
it("maskiert im Befehl nicht zusätzlich für HTML", () => {
// Ein `&` in einem PowerShell-Befehl waere schlicht falsch.
const ergebnis = platzhalterErsetzen(
"Set-Name {{abteilung}}",
{ abteilung: "Forschung & Entwicklung" },
{ art: "befehl", maskieren: (wert) => wert },
);
expect(ergebnis.text).toBe("Set-Name Forschung & Entwicklung");
});
it("lässt einen Wert für die Anzeige unverändert", () => {
/*
* Hier wird **nicht** HTML-maskiert: React maskiert Textknoten selbst, und
* eine zweite Maskierung machte aus „Forschung & Entwicklung“ sichtbar
* „Forschung & Entwicklung“. Dass die Maskierung wirklich
* stattfindet, beweist tests/platzhalter-anzeige.test.tsx am gerenderten
* HTML — nicht dieser Test hier.
*/
expect(platzhalterErsetzen("{{abteilung}}", { abteilung: "Forschung & Entwicklung" }).text).toBe(
"Forschung & Entwicklung",
);
});
});
describe("Generiertes Passwort", () => {
const BEFEHL = '-AccountPassword "{{generiertes_passwort}}"';
it("erscheint im angezeigten Befehl", () => {
const ergebnis = platzhalterErsetzen(BEFEHL, {});
expect(ergebnis.mitPasswort).toBe(true);
expect(ergebnis.text).not.toContain("{{generiertes_passwort}}");
expect(ergebnis.text).toMatch(/-AccountPassword "\S{20}"/u);
});
it("ist bei zwei Aufrufen verschieden", () => {
// Akzeptanzkriterium: je Anzeige neu. Dasselbe Passwort zweimal waere eine
// stillschweigende Speicherung.
const erster = platzhalterErsetzen(BEFEHL, {}).text;
const zweiter = platzhalterErsetzen(BEFEHL, {}).text;
expect(erster).not.toBe(zweiter);
});
it("steht in der Druckansicht als Hinweis statt als Passwort", () => {
// Ein Ausdruck liegt herum – und das Passwort ist nirgends gespeichert,
// also stuende dort ein Geheimnis, das niemand mehr zuordnen kann.
const ergebnis = platzhalterErsetzen(BEFEHL, {}, { art: "druck" });
expect(ergebnis.text).toBe(`-AccountPassword "${PASSWORT_IM_DRUCK}"`);
expect(ergebnis.mitPasswort).toBe(true);
});
it("gibt das Passwort nicht neben dem Text zurück", () => {
/*
* Die harte Zusage des Meilensteins: es gibt **keinen** Weg, das erzeugte
* Passwort ausser im Text zu bekommen. Gaebe es ein Feld dafuer, waere die
* Versuchung gross, es „nur fuers Protokoll“ mitzuschreiben.
*/
const ergebnis = platzhalterErsetzen(BEFEHL, {});
expect(Object.keys(ergebnis).sort()).toEqual(["mitPasswort", "text", "unbekannt"]);
expect(JSON.stringify(ergebnis)).not.toMatch(/passwort":\s*"[A-Za-z0-9]/u);
});
it("meldet kein Passwort, wenn keines vorkam", () => {
expect(platzhalterErsetzen("Hallo {{vorname}}", WERTE).mitPasswort).toBe(false);
});
it("erzeugt für zwei Vorkommen im selben Text zwei verschiedene Passwörter", () => {
/*
* Fachlich fragwuerdig, aber die ehrliche Folge der Regel „bei jeder Anzeige
* neu“: wer den Platzhalter zweimal in einen Befehl schreibt, meint
* vermutlich dasselbe Passwort. Dass es zwei sind, faellt beim Lesen auf —
* eines still zu speichern und wiederzuverwenden fiele nicht auf.
*/
const ergebnis = platzhalterErsetzen("{{generiertes_passwort}} und {{generiertes_passwort}}", {});
const [erstes, zweites] = ergebnis.text.split(" und ");
expect(erstes).not.toBe(zweites);
});
});