554 lines
22 KiB
TypeScript
554 lines
22 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
||
import {
|
||
HOSTNAME_HOECHSTLAENGE,
|
||
NamensregelFehler,
|
||
NamensregelnSchema,
|
||
SAM_HOECHSTLAENGE,
|
||
UNTERSCHEIDUNG_HOECHSTWERT,
|
||
UNTERSCHEIDUNG_STELLEN,
|
||
ZIELFORMATE,
|
||
hatZaehler,
|
||
kleinGefaltet,
|
||
namenBilden,
|
||
namenEindeutigBilden,
|
||
transliterieren,
|
||
} from "@/lib/namensregeln";
|
||
|
||
/**
|
||
* Namensregeln, Transliteration und Zaehler (#012).
|
||
*
|
||
* Aus diesen Werten werden Befehle auf einem Domaenencontroller. Die Tests sind
|
||
* deshalb nicht nur auf den guten Fall gerichtet, sondern vor allem darauf, dass
|
||
* bei fehlenden Angaben **nichts** herauskommt und dass die Grenzen des Ziel-
|
||
* systems eingehalten werden, auch wenn gekuerzt werden muss.
|
||
*/
|
||
|
||
/** Der Fall aus dem Akzeptanzkriterium — er kommt in mehreren Tests wieder. */
|
||
const JOERG = { vorname: "Jörg", nachname: "Müller-Lüdenscheid" } as const;
|
||
|
||
const MIT_KUNDE = { ...JOERG, kundenkuerzel: "KND" } as const;
|
||
|
||
describe("transliterieren", () => {
|
||
it("faltet „Jörg Müller-Lüdenscheid“ und laesst den Bindestrich stehen", () => {
|
||
expect(transliterieren("Jörg Müller-Lüdenscheid")).toBe("Joerg Mueller-Luedenscheid");
|
||
expect(kleinGefaltet("Jörg Müller-Lüdenscheid")).toBe("joerg mueller-luedenscheid");
|
||
});
|
||
|
||
it.each([
|
||
["ä", "ae"],
|
||
["ö", "oe"],
|
||
["ü", "ue"],
|
||
["Ä", "Ae"],
|
||
["Ö", "Oe"],
|
||
["Ü", "Ue"],
|
||
["ß", "ss"],
|
||
["Straße", "Strasse"],
|
||
])("macht aus %s zwei Zeichen: %s", (ein, aus) => {
|
||
expect(transliterieren(ein)).toBe(aus);
|
||
});
|
||
|
||
it.each([
|
||
["é", "e"],
|
||
["è", "e"],
|
||
["ç", "c"],
|
||
["ñ", "n"],
|
||
["å", "a"],
|
||
["ě", "e"],
|
||
["ą", "a"],
|
||
["Ş", "S"],
|
||
["René Ćosić", "Rene Cosic"],
|
||
])("entfernt den Akzent von %s", (ein, aus) => {
|
||
expect(transliterieren(ein)).toBe(aus);
|
||
});
|
||
|
||
it.each([
|
||
["ł", "l"],
|
||
["Łukasz", "Lukasz"],
|
||
["ø", "o"],
|
||
["Øyvind", "Oyvind"],
|
||
["æ", "ae"],
|
||
["œ", "oe"],
|
||
["đ", "d"],
|
||
["þ", "th"],
|
||
["ı", "i"],
|
||
])("faltet %s, das Unicode nicht zerlegt, zu %s", (ein, aus) => {
|
||
// Ohne eigene Tabelle fiele dieser Buchstabe als Nicht-ASCII ganz weg, und
|
||
// aus „Kowalski“ wuerde „Kowaski“ — ein Anmeldename, der nie funktioniert.
|
||
expect(transliterieren(ein)).toBe(aus);
|
||
});
|
||
|
||
it("wirft alles uebrige Nicht-ASCII weg", () => {
|
||
expect(transliterieren("Александр")).toBe("");
|
||
expect(transliterieren("李雷")).toBe("");
|
||
expect(transliterieren("Max 🎉 Mustermann")).toBe("Max Mustermann");
|
||
});
|
||
|
||
it("wirft Steuerzeichen weg, statt sie zu Leerzeichen zu machen", () => {
|
||
// Ein Zeilenumbruch in einem Namen hat in einem Befehl nichts zu suchen.
|
||
expect(transliterieren("Max\nMustermann")).toBe("MaxMustermann");
|
||
expect(transliterieren("Max\tMustermann")).toBe("MaxMustermann");
|
||
});
|
||
|
||
it("laesst reines ASCII unveraendert", () => {
|
||
expect(transliterieren("Max Mustermann-Meier 1970")).toBe("Max Mustermann-Meier 1970");
|
||
});
|
||
|
||
it("kommt mit einer leeren Eingabe zurecht", () => {
|
||
expect(transliterieren("")).toBe("");
|
||
expect(kleinGefaltet("")).toBe("");
|
||
});
|
||
|
||
it("faltet auch die zerlegte Schreibweise eines Umlauts zu zwei Zeichen", () => {
|
||
// „ü“ kann als ein Zeichen oder als u + Trema ankommen — aus einer CSV-Datei
|
||
// eines Mac kommt regelmaessig die zweite Form. Beide muessen `mueller`
|
||
// ergeben, nicht einmal `mueller` und einmal `muller`.
|
||
// `normalize("NFD")` macht aus dem einen Zeichen die zerlegte Form. Sie hier
|
||
// als Fluchtfolge hinzuschreiben geht nicht: Prettier schreibt sie in das
|
||
// unsichtbare Zeichen zurueck, und dann steht im Test etwas, was niemand sieht.
|
||
expect(kleinGefaltet("Müller".normalize("NFD"))).toBe("mueller");
|
||
expect(kleinGefaltet("Müller")).toBe("mueller");
|
||
});
|
||
});
|
||
|
||
describe("namenBilden – die Akzeptanzkriterien aus #012", () => {
|
||
it("bildet joerg.mueller-luedenscheid@firma.de", () => {
|
||
expect(namenBilden("upn", "{vorname}.{nachname}@firma.de", JOERG)).toBe(
|
||
"joerg.mueller-luedenscheid@firma.de",
|
||
);
|
||
expect(namenBilden("mail", "{vorname}.{nachname}@firma.de", JOERG)).toBe(
|
||
"joerg.mueller-luedenscheid@firma.de",
|
||
);
|
||
});
|
||
|
||
it("bildet den sam jmuellerluedensch", () => {
|
||
const sam = namenBilden("sam", "{v}{nachname}", JOERG);
|
||
|
||
expect(sam).toBe("jmuellerluedensch");
|
||
// 17 Zeichen: 20 erlaubt das Active Directory, drei bleiben fuer die
|
||
// Unterscheidung frei, damit ein zweiter Traeger desselben Namens den ersten
|
||
// nicht umbenennt.
|
||
expect(sam).toHaveLength(SAM_HOECHSTLAENGE - UNTERSCHEIDUNG_STELLEN);
|
||
});
|
||
});
|
||
|
||
describe("namenBilden – Platzhalter", () => {
|
||
it("setzt {v} auf den ersten Buchstaben des Vornamens", () => {
|
||
expect(namenBilden("sam", "{v}", JOERG)).toBe("j");
|
||
expect(namenBilden("sam", "{v}.{nachname}", { vorname: "Ed", nachname: "Ng" })).toBe("e.ng");
|
||
});
|
||
|
||
it("nimmt fuer {v} den ersten Buchstaben, nicht das erste Zeichen", () => {
|
||
expect(namenBilden("sam", "{v}{nachname}", { vorname: "'Abd al-Rahman", nachname: "Farouk" })).toBe(
|
||
"afarouk",
|
||
);
|
||
});
|
||
|
||
it("entfernt Leerzeichen aus zusammengesetzten Vornamen", () => {
|
||
expect(namenBilden("sam", "{vorname}.{nachname}", { vorname: "Anna Lena", nachname: "Meier" })).toBe(
|
||
"annalena.meier",
|
||
);
|
||
});
|
||
|
||
it("setzt {kundenkuerzel} ein", () => {
|
||
expect(namenBilden("hostname", "{kundenkuerzel}-PC-{nnn}", MIT_KUNDE, 7)).toBe("KND-PC-007");
|
||
});
|
||
|
||
it("richtet die Stellen des Zaehlers nach der Zahl der n", () => {
|
||
expect(namenBilden("hostname", "PC{n}", MIT_KUNDE, 7)).toBe("PC7");
|
||
expect(namenBilden("hostname", "PC{nn}", MIT_KUNDE, 7)).toBe("PC07");
|
||
expect(namenBilden("hostname", "PC{nnnn}", MIT_KUNDE, 7)).toBe("PC0007");
|
||
expect(namenBilden("hostname", "PC{nnnn}", MIT_KUNDE, 1234)).toBe("PC1234");
|
||
});
|
||
|
||
it("liefert nichts, wenn die Zahl nicht in ihre Stellen passt", () => {
|
||
// `{nn}` kann keine 100 tragen; ein Hostname mit einer Stelle mehr waere
|
||
// ausserhalb der Regel, die der Admin aufgeschrieben hat.
|
||
expect(namenBilden("hostname", "PC{nn}", MIT_KUNDE, 100)).toBeUndefined();
|
||
expect(namenBilden("hostname", "PC{nn}", MIT_KUNDE, -1)).toBeUndefined();
|
||
expect(namenBilden("hostname", "PC{nn}", MIT_KUNDE, 1.5)).toBeUndefined();
|
||
});
|
||
|
||
it("liefert nichts, wenn die Regel einen Zaehler braucht, aber keiner mitkommt", () => {
|
||
// Die naechste freie Nummer kennt nur der Aufrufer. `001` zu raten hiesse
|
||
// einen Hostnamen zu liefern, der schon an einem anderen Geraet haengt.
|
||
expect(namenBilden("hostname", "{kundenkuerzel}-PC-{nnn}", MIT_KUNDE)).toBeUndefined();
|
||
});
|
||
|
||
it("liefert nichts bei zwei Zaehlern in einer Regel", () => {
|
||
expect(namenBilden("hostname", "PC{nn}-{nn}", MIT_KUNDE, 7)).toBeUndefined();
|
||
});
|
||
});
|
||
|
||
describe("namenBilden – wenn etwas fehlt, kommt nichts heraus", () => {
|
||
it.each([
|
||
["keine Regel", undefined],
|
||
["null", null],
|
||
["leere Regel", ""],
|
||
["nur Leerzeichen", " "],
|
||
])("liefert bei %s nichts", (_fall, regel) => {
|
||
expect(namenBilden("upn", regel, JOERG)).toBeUndefined();
|
||
});
|
||
|
||
it.each([
|
||
["fehlender Nachname", { vorname: "Jörg" }],
|
||
["leerer Nachname", { vorname: "Jörg", nachname: "" }],
|
||
["Nachname nur aus Leerzeichen", { vorname: "Jörg", nachname: " " }],
|
||
["fehlender Vorname", { nachname: "Müller" }],
|
||
["null als Wert", { vorname: null, nachname: "Müller" }],
|
||
])("liefert bei %s nichts", (_fall, werte) => {
|
||
expect(namenBilden("upn", "{vorname}.{nachname}@firma.de", werte)).toBeUndefined();
|
||
});
|
||
|
||
it("liefert nichts, wenn das Kundenkuerzel fehlt", () => {
|
||
// Im Modus INTERN gibt es kein Kuerzel. Ein Hostname `-PC-001` waere
|
||
// schlimmer als keiner: er sieht richtig aus.
|
||
expect(namenBilden("hostname", "{kundenkuerzel}-PC-{nnn}", JOERG, 1)).toBeUndefined();
|
||
});
|
||
|
||
it("liefert nichts bei einem Platzhalter, den es nicht gibt", () => {
|
||
expect(namenBilden("upn", "{vorname}.{abteilung}@firma.de", JOERG)).toBeUndefined();
|
||
expect(namenBilden("sam", "{titel}", JOERG)).toBeUndefined();
|
||
});
|
||
|
||
it("liefert nichts bei einem verunglueckten Platzhalter", () => {
|
||
// Sonst faellt die Klammer beim Normalisieren weg und der Text bleibt: aus
|
||
// `{Nachname}@firma.de` wuerde die Adresse `nachname@firma.de`, und die
|
||
// sieht echt aus.
|
||
expect(namenBilden("upn", "{Nachname}@firma.de", JOERG)).toBeUndefined();
|
||
expect(namenBilden("upn", "{NACHNAME}@firma.de", JOERG)).toBeUndefined();
|
||
expect(namenBilden("upn", "{vorname@firma.de", JOERG)).toBeUndefined();
|
||
expect(namenBilden("sam", "{vorname}}", JOERG)).toBeUndefined();
|
||
});
|
||
|
||
it("liefert nichts, wenn vom Namen nach der Faltung nichts bleibt", () => {
|
||
expect(namenBilden("sam", "{v}{nachname}", { vorname: "###", nachname: "###" })).toBeUndefined();
|
||
expect(namenBilden("upn", "{nachname}@firma.de", { nachname: "###" })).toBeUndefined();
|
||
expect(namenBilden("sam", "{nachname}", { nachname: "李雷" })).toBeUndefined();
|
||
expect(namenBilden("hostname", "{nachname}", { nachname: "___" })).toBeUndefined();
|
||
});
|
||
|
||
it("liefert bei jedem Zielformat nichts, wenn die Regel fehlt", () => {
|
||
for (const zielformat of ZIELFORMATE) {
|
||
expect(namenBilden(zielformat, undefined, JOERG)).toBeUndefined();
|
||
}
|
||
});
|
||
});
|
||
|
||
describe("namenBilden – Adressform fuer upn und mail", () => {
|
||
it("schreibt klein", () => {
|
||
expect(namenBilden("upn", "{vorname}@FIRMA.DE", JOERG)).toBe("joerg@firma.de");
|
||
expect(namenBilden("mail", "{NACHNAME}@firma.de", JOERG)).toBeUndefined();
|
||
});
|
||
|
||
it("laesst keine doppelten Punkte stehen", () => {
|
||
expect(namenBilden("upn", "{vorname}..{nachname}@firma.de", JOERG)).toBe(
|
||
"joerg.mueller-luedenscheid@firma.de",
|
||
);
|
||
expect(namenBilden("upn", "{vorname}@firma..de", JOERG)).toBe("joerg@firma.de");
|
||
});
|
||
|
||
it("laesst keinen Punkt vor dem @ stehen", () => {
|
||
expect(namenBilden("upn", "{vorname}.@firma.de", JOERG)).toBe("joerg@firma.de");
|
||
expect(namenBilden("upn", ".{vorname}@firma.de", JOERG)).toBe("joerg@firma.de");
|
||
});
|
||
|
||
it("verlangt genau ein @ und beide Haelften", () => {
|
||
expect(namenBilden("upn", "{vorname}.{nachname}", JOERG)).toBeUndefined();
|
||
expect(namenBilden("upn", "{vorname}@{nachname}@firma.de", JOERG)).toBeUndefined();
|
||
expect(namenBilden("upn", "{vorname}@", JOERG)).toBeUndefined();
|
||
expect(namenBilden("upn", "@firma.de", JOERG)).toBeUndefined();
|
||
});
|
||
|
||
it("weist einen Namensbereich mit unbrauchbarer Stufe ab", () => {
|
||
expect(namenBilden("upn", "{vorname}@firma.-de", JOERG)).toBeUndefined();
|
||
expect(namenBilden("upn", "{vorname}@firma.de-.x", JOERG)).toBeUndefined();
|
||
});
|
||
|
||
it("raeumt Bindestriche am Rand des Namensbereichs weg", () => {
|
||
expect(namenBilden("upn", "{vorname}@-firma.de", JOERG)).toBe("joerg@firma.de");
|
||
expect(namenBilden("upn", "{vorname}@firma.de-", JOERG)).toBe("joerg@firma.de");
|
||
});
|
||
|
||
it("wirft Zeichen weg, die in einer Adresse nicht vorkommen duerfen", () => {
|
||
// „Müller, Jörg“ aus einer CSV-Spalte, in der Vor- und Nachname zusammen
|
||
// stehen: das Komma gehoert in keine Adresse.
|
||
expect(namenBilden("mail", "{nachname}@firma.de", { nachname: "Mül,ler" })).toBe("mueller@firma.de");
|
||
});
|
||
|
||
it("kuerzt eine Adresse nicht", () => {
|
||
// Eine gekuerzte Mail-Adresse ist keine Adresse, sie ist eine falsche.
|
||
const lang = namenBilden("mail", "{vorname}.{nachname}@firma.de", {
|
||
vorname: "Jörg",
|
||
nachname: "Lüdenscheid".repeat(10),
|
||
});
|
||
|
||
expect(lang?.length).toBeGreaterThan(100);
|
||
expect(lang?.endsWith("@firma.de")).toBe(true);
|
||
});
|
||
});
|
||
|
||
describe("namenBilden – sam", () => {
|
||
it("laesst den Punkt stehen und den Bindestrich weg", () => {
|
||
expect(namenBilden("sam", "{vorname}.{nachname}", JOERG)).toBe("joerg.muellerlued");
|
||
});
|
||
|
||
it("kuerzt am Ende auf die nutzbare Laenge", () => {
|
||
const sam = namenBilden("sam", "{v}{nachname}", { vorname: "Jörg", nachname: "Lüdenscheid".repeat(20) });
|
||
|
||
expect(sam).toHaveLength(SAM_HOECHSTLAENGE - UNTERSCHEIDUNG_STELLEN);
|
||
expect(sam?.startsWith("jluedenscheid")).toBe(true);
|
||
});
|
||
|
||
it("laesst keinen Punkt am Ende stehen, auch nicht nach dem Kuerzen", () => {
|
||
// Ein `sAMAccountName` mit Punkt am Ende ist unzulaessig; beim Kuerzen kann
|
||
// er genau dort landen.
|
||
const sam = namenBilden("sam", "{nachname}.{vorname}", {
|
||
vorname: "Jörg",
|
||
nachname: "Mueller-Luedensch",
|
||
});
|
||
|
||
expect(sam).toBe("muellerluedensch");
|
||
expect(sam?.endsWith(".")).toBe(false);
|
||
});
|
||
|
||
it("nimmt einen Namen aus einem Zeichen", () => {
|
||
expect(namenBilden("sam", "{v}{nachname}", { vorname: "Ed", nachname: "Ng" })).toBe("eng");
|
||
expect(namenBilden("sam", "{vorname}", { vorname: "Ü" })).toBe("ue");
|
||
});
|
||
});
|
||
|
||
describe("namenBilden – hostname", () => {
|
||
it("schreibt gross", () => {
|
||
// Windows fuehrt den NetBIOS-Namen gross; stuende auf dem Aufkleber etwas
|
||
// anderes als das Geraet meldet, sucht der naechste Techniker einen Fehler,
|
||
// den es nicht gibt.
|
||
expect(namenBilden("hostname", "{kundenkuerzel}-nb-{nnn}", MIT_KUNDE, 3)).toBe("KND-NB-003");
|
||
expect(namenBilden("hostname", "{vorname}", JOERG)).toBe("JOERG");
|
||
});
|
||
|
||
it("laesst Unterstrich und Punkt weg", () => {
|
||
expect(namenBilden("hostname", "{kundenkuerzel}_PC_{nn}", MIT_KUNDE, 7)).toBe("KNDPC07");
|
||
expect(namenBilden("hostname", "{kundenkuerzel}.pc.{nn}", MIT_KUNDE, 7)).toBe("KNDPC07");
|
||
});
|
||
|
||
it("laesst keinen fuehrenden oder abschliessenden Bindestrich stehen", () => {
|
||
expect(namenBilden("hostname", "-{kundenkuerzel}-", MIT_KUNDE)).toBe("KND");
|
||
expect(namenBilden("hostname", "{kundenkuerzel}--", MIT_KUNDE)).toBe("KND");
|
||
});
|
||
|
||
it("haelt die 15 Zeichen des NetBIOS-Namens ein und kuerzt nie in der Nummer", () => {
|
||
// Das ist die gefaehrliche Stelle: wuerde am Ende gekuerzt, verschwaende die
|
||
// Nummer, und zwei Geraete bekaemen denselben Namen.
|
||
const hostname = namenBilden("hostname", "{nachname}-{nnn}", JOERG, 1);
|
||
|
||
expect(hostname).toBe("MUELLER-LUED001");
|
||
expect(hostname).toHaveLength(HOSTNAME_HOECHSTLAENGE);
|
||
expect(hostname?.endsWith("001")).toBe(true);
|
||
});
|
||
|
||
it("behaelt die Nummer auch bei einem sehr langen Namen", () => {
|
||
const hostname = namenBilden("hostname", "{nachname}-{nnnn}", { nachname: "Lüdenscheid".repeat(30) }, 42);
|
||
|
||
expect(hostname).toHaveLength(HOSTNAME_HOECHSTLAENGE);
|
||
expect(hostname?.endsWith("0042")).toBe(true);
|
||
});
|
||
|
||
it("behaelt die Nummer auch, wenn sie nicht am Ende der Regel steht", () => {
|
||
const hostname = namenBilden("hostname", "{nnn}-{nachname}", { nachname: "Lüdenscheid".repeat(30) }, 42);
|
||
|
||
expect(hostname).toHaveLength(HOSTNAME_HOECHSTLAENGE);
|
||
expect(hostname?.startsWith("042-")).toBe(true);
|
||
});
|
||
|
||
it("haelt ohne Zaehler in der Regel den Vorrat fuer die Unterscheidung frei", () => {
|
||
const hostname = namenBilden("hostname", "{nachname}", { nachname: "Lüdenscheid".repeat(5) });
|
||
|
||
expect(hostname).toHaveLength(HOSTNAME_HOECHSTLAENGE - UNTERSCHEIDUNG_STELLEN);
|
||
});
|
||
|
||
it("liefert nichts, wenn die Nummer allein schon zu lang ist", () => {
|
||
expect(namenBilden("hostname", "{nachname}-{nnnnnnnnnnnnnnnn}", JOERG, 1)).toBeUndefined();
|
||
});
|
||
});
|
||
|
||
describe("hatZaehler", () => {
|
||
it.each([
|
||
["{nnn}", true],
|
||
["{kundenkuerzel}-PC-{nnn}", true],
|
||
["PC{n}", true],
|
||
["{vorname}.{nachname}@firma.de", false],
|
||
["{nachname}", false],
|
||
["", false],
|
||
])("erkennt %s", (regel, erwartet) => {
|
||
expect(hatZaehler(regel)).toBe(erwartet);
|
||
});
|
||
});
|
||
|
||
describe("namenEindeutigBilden – mit Zaehler in der Regel", () => {
|
||
it("nimmt die erste Nummer, wenn sie frei ist", async () => {
|
||
const istVergeben = vi.fn(async () => false);
|
||
|
||
await expect(
|
||
namenEindeutigBilden("hostname", "{kundenkuerzel}-PC-{nnn}", MIT_KUNDE, istVergeben),
|
||
).resolves.toBe("KND-PC-001");
|
||
expect(istVergeben).toHaveBeenCalledTimes(1);
|
||
expect(istVergeben).toHaveBeenCalledWith("KND-PC-001");
|
||
});
|
||
|
||
it("zaehlt weiter, solange Nummern vergeben sind", async () => {
|
||
const vergeben = new Set(["KND-PC-001", "KND-PC-002"]);
|
||
const istVergeben = vi.fn(async (kandidat: string) => vergeben.has(kandidat));
|
||
|
||
await expect(
|
||
namenEindeutigBilden("hostname", "{kundenkuerzel}-PC-{nnn}", MIT_KUNDE, istVergeben),
|
||
).resolves.toBe("KND-PC-003");
|
||
expect(istVergeben).toHaveBeenCalledTimes(3);
|
||
});
|
||
|
||
it("wirft, wenn der Vorrat der Regel erschoepft ist", async () => {
|
||
const istVergeben = vi.fn(async () => true);
|
||
|
||
// `{n}` gibt neun Nummern her. Danach ist die Regel zu eng — das ist eine
|
||
// Einstellung, die geaendert werden muss, und keine Nummer, die man
|
||
// erfinden darf.
|
||
await expect(namenEindeutigBilden("hostname", "PC{n}", MIT_KUNDE, istVergeben)).rejects.toThrow(
|
||
NamensregelFehler,
|
||
);
|
||
expect(istVergeben).toHaveBeenCalledTimes(9);
|
||
});
|
||
|
||
it("fragt nicht nach, wenn die Regel keinen Namen ergibt", async () => {
|
||
const istVergeben = vi.fn(async () => false);
|
||
|
||
await expect(
|
||
namenEindeutigBilden("hostname", "{kundenkuerzel}-PC-{nnn}", JOERG, istVergeben),
|
||
).resolves.toBeUndefined();
|
||
expect(istVergeben).not.toHaveBeenCalled();
|
||
});
|
||
});
|
||
|
||
describe("namenEindeutigBilden – ohne Zaehler in der Regel", () => {
|
||
it("liefert den Namen unveraendert, wenn er frei ist", async () => {
|
||
const istVergeben = vi.fn(async () => false);
|
||
|
||
await expect(namenEindeutigBilden("sam", "{v}{nachname}", JOERG, istVergeben)).resolves.toBe(
|
||
"jmuellerluedensch",
|
||
);
|
||
expect(istVergeben).toHaveBeenCalledTimes(1);
|
||
});
|
||
|
||
it("haengt bei einer Kollision eine Unterscheidung an und bleibt in der Grenze", async () => {
|
||
const vergeben = new Set(["jmuellerluedensch"]);
|
||
const istVergeben = vi.fn(async (kandidat: string) => vergeben.has(kandidat));
|
||
|
||
const sam = await namenEindeutigBilden("sam", "{v}{nachname}", JOERG, istVergeben);
|
||
|
||
expect(sam).toBe("jmuellerluedensch2");
|
||
expect(sam?.length).toBeLessThanOrEqual(SAM_HOECHSTLAENGE);
|
||
});
|
||
|
||
it("bleibt auch bei dreistelliger Unterscheidung in den 20 Zeichen", async () => {
|
||
// Die drei zurueckgehaltenen Stellen sind genau dafuer da.
|
||
const istVergeben = vi.fn(async (kandidat: string) => kandidat !== "jmuellerluedensch999");
|
||
|
||
const sam = await namenEindeutigBilden("sam", "{v}{nachname}", JOERG, istVergeben);
|
||
|
||
expect(sam).toBe("jmuellerluedensch999");
|
||
expect(sam).toHaveLength(SAM_HOECHSTLAENGE);
|
||
});
|
||
|
||
it("setzt die Unterscheidung bei Adressen vor das @", async () => {
|
||
const vergeben = new Set(["joerg.mueller-luedenscheid@firma.de"]);
|
||
const istVergeben = vi.fn(async (kandidat: string) => vergeben.has(kandidat));
|
||
|
||
await expect(
|
||
namenEindeutigBilden("upn", "{vorname}.{nachname}@firma.de", JOERG, istVergeben),
|
||
).resolves.toBe("joerg.mueller-luedenscheid2@firma.de");
|
||
});
|
||
|
||
it("bleibt beim Hostnamen in den 15 Zeichen", async () => {
|
||
const istVergeben = vi.fn(async (kandidat: string) => kandidat.length < HOSTNAME_HOECHSTLAENGE);
|
||
|
||
const hostname = await namenEindeutigBilden(
|
||
"hostname",
|
||
"{nachname}",
|
||
{ nachname: "Lüdenscheid".repeat(5) },
|
||
istVergeben,
|
||
);
|
||
|
||
expect(hostname).toHaveLength(HOSTNAME_HOECHSTLAENGE);
|
||
});
|
||
|
||
it("wirft, wenn auch alle Unterscheidungen vergeben sind", async () => {
|
||
const istVergeben = vi.fn(async () => true);
|
||
|
||
await expect(namenEindeutigBilden("sam", "{v}{nachname}", JOERG, istVergeben)).rejects.toThrow(
|
||
NamensregelFehler,
|
||
);
|
||
// Der Name selbst und die Zusaetze 2 bis 999.
|
||
expect(istVergeben).toHaveBeenCalledTimes(UNTERSCHEIDUNG_HOECHSTWERT);
|
||
});
|
||
|
||
it("fragt ohne Regel nicht nach", async () => {
|
||
const istVergeben = vi.fn(async () => false);
|
||
|
||
await expect(namenEindeutigBilden("sam", undefined, JOERG, istVergeben)).resolves.toBeUndefined();
|
||
await expect(namenEindeutigBilden("sam", " ", JOERG, istVergeben)).resolves.toBeUndefined();
|
||
expect(istVergeben).not.toHaveBeenCalled();
|
||
});
|
||
|
||
it("gibt einen Fehler der Vergabepruefung unveraendert weiter", async () => {
|
||
const istVergeben = vi.fn(async () => {
|
||
throw new Error("Datenbank nicht erreichbar");
|
||
});
|
||
|
||
await expect(namenEindeutigBilden("sam", "{v}{nachname}", JOERG, istVergeben)).rejects.toThrow(
|
||
"Datenbank nicht erreichbar",
|
||
);
|
||
});
|
||
});
|
||
|
||
describe("NamensregelnSchema", () => {
|
||
it("nimmt die vier Zielformate und trimmt", () => {
|
||
const gelesen = NamensregelnSchema.parse({
|
||
upn: " {vorname}.{nachname}@firma.de ",
|
||
sam: "{v}{nachname}",
|
||
mail: "{vorname}.{nachname}@firma.de",
|
||
hostname: "{kundenkuerzel}-PC-{nnn}",
|
||
});
|
||
|
||
expect(gelesen.upn).toBe("{vorname}.{nachname}@firma.de");
|
||
expect(gelesen.sam).toBe("{v}{nachname}");
|
||
});
|
||
|
||
it("nimmt ein leeres Objekt", () => {
|
||
expect(NamensregelnSchema.parse({})).toEqual({});
|
||
});
|
||
|
||
it("weist eine zu lange Regel ab", () => {
|
||
expect(NamensregelnSchema.safeParse({ upn: "x".repeat(201) }).success).toBe(false);
|
||
});
|
||
});
|
||
|
||
describe("Hostnamen, die Windows ablehnt", () => {
|
||
it("schlägt keinen reservierten Gerätenamen vor", () => {
|
||
/*
|
||
* `CON`, `PRN`, `NUL` und Geschwister sind die alten Geraetenamen von
|
||
* MS-DOS. Ein Nachname „Con“ reicht, damit die Regel sie vorschlaegt — und
|
||
* Windows nimmt den Computernamen dann nicht an. Gefunden im Review zu #012.
|
||
*/
|
||
for (const name of ["con", "prn", "aux", "nul", "com1", "lpt9"]) {
|
||
expect(namenBilden("hostname", "{nachname}", { nachname: name }), name).toBeUndefined();
|
||
}
|
||
// Mit etwas davor oder dahinter ist der Name in Ordnung.
|
||
expect(namenBilden("hostname", "PC-{nachname}", { nachname: "con" })).toBe("PC-CON");
|
||
});
|
||
|
||
it("schlägt keinen rein numerischen Hostnamen vor", () => {
|
||
// Er waere von einer IP-Adresse nicht zu unterscheiden; Windows lehnt ihn ab.
|
||
expect(namenBilden("hostname", "{nachname}", { nachname: "12345" })).toBeUndefined();
|
||
expect(namenBilden("hostname", "{nnn}", {}, 1)).toBeUndefined();
|
||
expect(namenBilden("hostname", "PC-{nnn}", {}, 1)).toBe("PC-001");
|
||
});
|
||
});
|