Files
Laufzettel/docker-compose.test.yml
Florian WildeandClaude Opus 5 a417426f43
Prüfung / pruefung (pull_request) Canceled after 0s
Prüfung / e2e (pull_request) Canceled after 0s
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s
feat(meldungen): Benachrichtigungen per Mail und Webhook (#54)
Ein zugewiesener Lauf fiel bisher nur auf, wenn jemand die Uebersicht
oeffnete; ein ueberfaelliger ueberhaupt nicht. Vier Anlaesse gehen jetzt
hinaus — zugewiesen, ueberfaellig, abgeschlossen und Verzeichnisabgleich
fehlgeschlagen —, je Person abschaltbar. Niemand bekommt Post ueber die
eigene Tat: das ist der haeufigste Grund, Benachrichtigungen ganz
abzuschalten, und deshalb faellt der Handelnde immer aus der
Empfaengerliste.

Eingereiht wird im Anfrage-Pfad, gesendet wird es nicht. Ein haengender
Mailserver darf kein Abhaken blockieren, also schreibt der Dienst eine
Zeile in `Zustellung` und ein eigener Zeitplan schickt sie. Betreff und
Rumpf stehen fertig darin: eine Meldung sagt, was *damals* war.

In keiner Nachricht steht ein Schrittinhalt, und damit auch kein
erzeugtes Passwort. Darueber wacht src/lib/meldungen.ts — die Funktionen
dort nehmen nur Kopfdaten, der Typ laesst nichts anderes zu. Geprueft
wird es zusaetzlich ueber alle Rumpftexte, die wirklich verschickt
wurden, so wie #012 es fuer die Druckansicht tat.

Zwei Dinge haetten ohne Datenbank nicht gehalten. Die Einmaligkeit der
Ueberfaelligkeitsmeldung liegt in einem partiellen eindeutigen Index:
der Zeitplan sieht jede Minute nach, und ohne ihn bekaeme der
Zustaendige fuer einen Lauf, der ueber das Wochenende steht, 2880
Mails — jede fuer sich korrekt, kein Test rot. Und die Sperre der
Warteschlange liegt in derselben Tabelle wie die des Abgleichs, denn
hinter einem Lastverteiler laufen zwei Instanzen.

Eine stille Warteschlange ist schlimmer als keine: Verwaltung →
Zustellungen zeigt mit Grund, was hinausging und was nicht.

Geprueft gegen einen echten SMTP-Faenger (mailpit) statt gegen eine
abgefangene Funktion — und `mailpit` gehoert damit ins Tor vor dem
Commit, sonst ueberspringt die Suite 3 Tests und sieht gruen aus.

Closes #54

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 22:52:36 +02:00

88 lines
3.7 KiB
YAML

# Dienste fuer die Testsuite. Nicht fuer den Betrieb.
name: laufzettel-test
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_USER: laufzettel
POSTGRES_PASSWORD: laufzettel
POSTGRES_DB: laufzettel_test
ports:
- "127.0.0.1:5433:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U laufzettel"]
interval: 5s
timeout: 5s
retries: 10
# Der Mailfaenger fuer die Benachrichtigungen (#034). Mailpit nimmt SMTP auf
# 1025 an und zeigt die Nachrichten ueber eine HTTP-Schnittstelle auf 8025 —
# darueber liest der Test, was wirklich ankam.
#
# **Ein echter SMTP-Dialog, keine Attrappe.** Eine abgefangene
# `sendMail`-Funktion haette in #034 nichts bewiesen: dass Betreff und
# Empfaenger stimmen, zeigt erst eine Nachricht, die durch Verbindungsaufbau,
# EHLO und DATA gegangen ist. Ohne Anmeldung und ohne TLS, weil der Test den
# Dialog prueft und nicht die Verschluesselung.
mailpit:
image: axllent/mailpit:latest
environment:
MP_SMTP_AUTH_ACCEPT_ANY: "true"
MP_SMTP_AUTH_ALLOW_INSECURE: "true"
ports:
- "127.0.0.1:1025:1025"
- "127.0.0.1:8025:8025"
healthcheck:
# Mailpit bringt kein curl mit; `/livez` ueber den eigenen Client.
test: ["CMD", "/mailpit", "readyz"]
interval: 5s
timeout: 5s
retries: 20
# Das Verzeichnis fuer den Abgleich (#016). OpenLDAP mit einem AD-aehnlichen
# Schema: sAMAccountName, userPrincipalName, binaeres objectGUID,
# userAccountControl, objectCategory und department bringt OpenLDAP nicht mit,
# sie stehen deshalb in tests/fixtures/ldap/schema/ad-fixture.ldif.
#
# LDAP_TLS ist aus, und das ist eine Einschraenkung mit Ansage: das Zertifikat,
# das osixia sonst selbst erzeugt, laeutet auf den Containernamen, waehrend der
# Test von aussen 127.0.0.1 anspricht -- die Pruefung des Hostnamens schlaege
# fehl. Ein Schalter, der sie abstellt, waere ein Schalter zu viel im
# Produktionscode. Der Container wird deshalb im Klartext angesprochen; die
# TLS-Optionen (ldaps, StartTLS, eigenes CA-Zertifikat, minVersion) sind als
# reine Funktion `tlsOptionenBauen` in tests/verzeichnis.test.ts geprueft.
ldap:
image: osixia/openldap:1.5.0
# Ohne `--copy-service` spielt der Startskript die Bootstrap-Dateien nicht
# ein, wenn sie nur eingehaengt sind.
command: ["--copy-service"]
environment:
LDAP_ORGANISATION: Firma Test
LDAP_DOMAIN: firma.test
LDAP_ADMIN_PASSWORD: ldap-test-geheim
LDAP_TLS: "false"
ports:
- "127.0.0.1:1389:389"
volumes:
# Schema und Daten liegen in getrennten Verzeichnissen, weil osixia die
# Daten-LDIFs mit `sed -i` nach Platzhaltern durchsucht. Eingehaengt wird
# deshalb das Verzeichnis und nicht die Datei: `sed -i` schreibt ueber eine
# Umbenennung, und die scheitert auf einer einzeln eingehaengten Datei --
# das Startskript laeuft mit `bash -e` und der Container waere tot.
- ./tests/fixtures/ldap/schema:/container/service/slapd/assets/config/bootstrap/schema/custom:ro
- ./tests/fixtures/ldap/daten:/container/service/slapd/assets/config/bootstrap/ldif/custom
healthcheck:
# Die Suche laeuft ueber den authentifizierten Bind, weil anonymes Lesen
# abgeschaltet ist. Gesund ist der Dienst erst, wenn die Fixture-Personen
# wirklich da sind -- sonst liest der Test gegen ein leeres Verzeichnis.
test:
[
"CMD-SHELL",
"ldapsearch -x -H ldap://localhost:389 -D cn=admin,dc=firma,dc=test -w ldap-test-geheim -b ou=Benutzer,dc=firma,dc=test -s sub '(sAMAccountName=mhuber)' dn | grep -q 'cn=Maria Huber'",
]
interval: 5s
timeout: 5s
retries: 20