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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ab5777a57e
commit
16059e7846
+23
-2
@@ -575,7 +575,7 @@ describe.runIf(datenbankVerfuegbar)("Vorlagen-Versionierung", () => {
|
||||
const { vorlage } = await veroeffentlichteVorlage(benutzer.id);
|
||||
const entwurf = await entwurfHolenOderErzeugen(vorlage.id, benutzer.id);
|
||||
|
||||
await Promise.allSettled([
|
||||
const [veroeffentlichung, speichern] = await Promise.allSettled([
|
||||
versionVeroeffentlichen(entwurf.id, "Veröffentlicht", benutzer.id),
|
||||
entwurfSpeichern(entwurf.id, {
|
||||
variablen: [],
|
||||
@@ -584,9 +584,30 @@ describe.runIf(datenbankVerfuegbar)("Vorlagen-Versionierung", () => {
|
||||
]);
|
||||
|
||||
const danach = await strukturLesen(entwurf.id);
|
||||
/*
|
||||
* Welcher der beiden zuerst an die Zeile kommt, entscheidet die Datenbank —
|
||||
* **beide Ausgaenge** werden hier geprueft. Vorher stand die Erwartung in
|
||||
* einem `if`: gewann das Speichern das Rennen, lief der Test durch, ohne
|
||||
* etwas zu belegen, und die eigentliche Zusage waere unbemerkt gefallen
|
||||
* (Reviewbefund zu #016).
|
||||
*/
|
||||
expect(danach.status === "VEROEFFENTLICHT" || danach.status === "ENTWURF").toBe(true);
|
||||
if (danach.status === "VEROEFFENTLICHT") {
|
||||
// Wurde veroeffentlicht, darf der nachträgliche Aufbau nicht drinstehen.
|
||||
expect(veroeffentlichung.status).toBe("fulfilled");
|
||||
// Veroeffentlicht heisst unveraenderlich: der nachtraegliche Aufbau darf
|
||||
// nicht drinstehen, und das Speichern muss abgewiesen worden sein.
|
||||
expect(danach.abschnitte.map((a) => a.titel)).not.toContain("NACHTRAEGLICH");
|
||||
expect(speichern.status).toBe("rejected");
|
||||
} else {
|
||||
// Das Speichern war zuerst da. Dann darf nicht veroeffentlicht worden sein
|
||||
// – sonst trueege die veroeffentlichte Version genau diesen Abschnitt.
|
||||
expect(speichern.status).toBe("fulfilled");
|
||||
expect(veroeffentlichung.status).toBe("rejected");
|
||||
expect(
|
||||
await testPrisma.protokoll.count({
|
||||
where: { entitaetId: entwurf.id, aktion: "Version veröffentlicht" },
|
||||
}),
|
||||
).toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user