Files
Laufzettel/tests/ldap-guid.test.ts
T
Florian WildeandClaude Opus 5 16059e7846
Prüfung / pruefung (push) Canceled after 0s
Prüfung / pruefung (pull_request) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s
Prüfung / e2e (pull_request) Canceled after 0s
feat(verzeichnis): LDAP-Abgleich, Zeitplan, CSV-Import und Log
Quelle anlegen, Verbindung testen, nach Zeitplan oder auf Knopfdruck
abgleichen. Gelesen wird seitenweise mit searchPaginated, objectGUID
kommt als Puffer und wird gemischt-endian zur GUID umsortiert. Wer im
Verzeichnis verschwindet, wird hier inaktiv und nie geloescht.

Drei Regeln verhindern, dass eine kaputte Eingabe wie "alle sind
gegangen" aussieht: laesst sich kein einziger Eintrag zuordnen, wird
niemand deaktiviert; uebersprungene Eintraege werden am dn erkannt und
ausgenommen; eine von Hand angelegte Person wird uebernommen statt
verdoppelt.

Der Zeitplan laeuft ueber node-cron im selben Prozess, gestartet aus
src/instrumentation.ts, und nimmt keine eigene Sperre - die liegt in der
Datenbank, damit zwei Instanzen sich einig sind.

Closes #16

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-26 02:21:08 +02:00

66 lines
3.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { GuidFehler, guidAusBytes } from "@/lib/ldap-guid";
/**
* `objectGUID` als GUID-Zeichenkette (#016).
*
* Diese Umsortierung ist der Grund, warum die Kennung ueberhaupt eine eigene
* Datei hat: schlaegt sie fehl, sieht das Ergebnis trotzdem wie eine GUID aus.
* Der Abgleich wuerde dann bei jedem Lauf dieselben Personen neu anlegen, weil
* die Kennung zwar stabil, aber gegen `Get-ADUser` nicht auffindbar waere — ein
* Fehler, der erst auffaellt, wenn jemand mit dem AD vergleicht.
*
* Die erwarteten Zeichenketten sind mit Pythons `uuid.UUID(bytes_le=…)`
* ausgerechnet, also mit einer unabhaengigen Umsetzung derselben Reihenfolge.
*/
describe("guidAusBytes", () => {
it("ordnet die ersten drei Gruppen little-endian, den Rest in Leserichtung", () => {
// Der aufschlussreichste Vektor: die Bytes 00…0f. An der Ausgabe ist die
// Umsortierung mit dem Auge nachzuvollziehen.
const bytes = Uint8Array.from([
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
]);
expect(guidAusBytes(bytes)).toBe("03020100-0504-0706-0809-0a0b0c0d0e0f");
});
it("liest den Byte-Vektor einer echten AD-Kennung", () => {
// Dieselben Bytes stehen base64-kodiert im Fixture des Testcontainers
// (tests/fixtures/ldap/daten/50-personen.ldif, „Maria Huber“).
const bytes = Uint8Array.from(Buffer.from("EEMHXzU8HUqDb6UPUV56NQ==", "base64"));
expect(guidAusBytes(bytes)).toBe("5f074310-3c35-4a1d-836f-a50f515e7a35");
});
it("liefert bei denselben Bytes immer dieselbe Zeichenkette", () => {
const bytes = Uint8Array.from(Buffer.from("nEA+HXqyyUux5vCiw9Tl9g==", "base64"));
expect(guidAusBytes(bytes)).toBe(guidAusBytes(bytes));
expect(guidAusBytes(Uint8Array.from(bytes))).toBe("1d3e409c-b27a-4bc9-b1e6-f0a2c3d4e5f6");
});
it("schreibt fuehrende Nullen mit", () => {
// Ohne padStart waere daraus "0-0-0-0-0"; die Kennung waere kuerzer als eine
// GUID und passte doch zu einem Konto.
expect(guidAusBytes(new Uint8Array(16))).toBe("00000000-0000-0000-0000-000000000000");
});
it("gibt Klein- und nicht Grossbuchstaben aus", () => {
const bytes = Uint8Array.from(Buffer.from("/+7dzLuqmYh3ZlVEMyIRAA==", "base64"));
const guid = guidAusBytes(bytes);
expect(guid).toBe("ccddeeff-aabb-8899-7766-554433221100");
expect(guid).toBe(guid.toLowerCase());
});
it("hat die Form 8-4-4-4-12", () => {
const guid = guidAusBytes(Uint8Array.from(Buffer.from("AQIDBAUGBwgJCgsMDQ4PEA==", "base64")));
expect(guid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u);
});
it("wirft bei falscher Laenge statt eine halbe Kennung zu liefern", () => {
// Genau der Fall, der ohne explicitBufferAttributes entsteht: die 16 Bytes
// wurden als UTF-8 gelesen und dabei veraendert.
expect(() => guidAusBytes(new Uint8Array(15))).toThrow(GuidFehler);
expect(() => guidAusBytes(new Uint8Array(0))).toThrow(GuidFehler);
expect(() => guidAusBytes(new Uint8Array(36))).toThrow(/16 Bytes/u);
});
});