RET-07: fristablauf-benachrichtigungen

- archive/internal/notifyclient: HTTP-Client fuer Core CFG-05 (gleiches
  Muster wie rbacclient/RET-08 fuer RBAC-06)
- archive/internal/retentionnotify.Run: ermittelt faellige Objekte ueber
  dieselbe Funktion wie RET-02-Job/RET-06-API-Preview, filtert je
  Klasse nach Vorlauf+Ein-Aus-Schalter, Postgres-persistente Dedupe
  (retention_notifications), Fehlschlag wird protokolliert statt
  verworfen (kein Eintrag -> Retry beim naechsten Durchlauf)
- archive/cmd/retention-notify-job: systemd-Timer-CLI, analog scrub-cli
- Abweichung vom urspruenglichen Ticket-Text dokumentiert: CFG-05 statt
  direktem CFG-02-Import (Modul-Trennung), konfigurierte zustaendige
  Rolle statt Objekt-Owner (RET-01 fuehrt keinen)
- real deployed auf 131 (timer taeglich 07:00 UTC), end-zu-ende
  bewiesen: echte notification_jobs-Zeile in Core-DB, zweiter
  Dienststart ohne Doppelversand

Pruefungen siehe archive/docs/RET-07-PRUEFPROTOKOLL.md
This commit is contained in:
sysops
2026-08-30 09:13:01 +02:00
parent 36d1cf5e7f
commit 0db8fa1377
9 changed files with 652 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
# RET-07 Prüfprotokoll: Fristablauf-Benachrichtigungen
Voraussetzung RET-02, CFG-05 beide bereits Fertig.
## Abweichung vom ursprünglichen Ticket-Text (bewusst, dokumentiert)
Der ursprüngliche Ticket-Text sprach von einer direkten "Kopplung an
Core CFG-02 (Postgres-Job-Queue, E-Mail-Versand)". Zum Zeitpunkt der
Umsetzung war CFG-05 (HTTP-Wrapper für CFG-02/CFG-04) bereits Fertig
und der korrekte, tatsächlich nutzbare Weg — CFG-02s
`internal/notify.Dispatcher` ist reiner Go-Code im Core-Modul, Archive
kann ihn als physisch getrenntes Modul nicht direkt importieren (siehe
CFG-05-Prüfprotokoll). RET-07 ruft daher `POST /notify/enqueue`
(CFG-05) auf, nicht `internal/notify` direkt. Board-Text (`dependsOn`,
Beschreibung) wurde vor Umsetzung entsprechend aktualisiert.
**Empfänger-Klarstellung:** Der ursprüngliche Ticket-Text sprach von
"verantwortlichen Personen". `retention_objects` (RET-01) führt bewusst
KEINE Objekt-Owner-Beziehung. Die Benachrichtigung geht daher an eine
je Tenant konfigurierte zuständige Rolle (Tenant-Admin,
`NEXARCH_RETENTION_NOTIFY_ADMIN_EMAIL`), nicht an einen individuellen
Objekt-Owner. Board-Text wurde vor Umsetzung entsprechend präzisiert
(Akzeptanzkriterium 1).
## Umsetzung
- `archive/migrations/0006_retention_notify.up/down.sql`
`retention_class_rules.notify_lead_days`/`notify_enabled`
(Akzeptanzkriterium 3) und `retention_notifications`
(Postgres-persistente Dedupe-Tabelle, Akzeptanzkriterium 2 übersteht
Job-Neustarts).
- `archive/internal/notifyclient` schlanker HTTP-Client für CFG-05
(gleiches Muster wie `rbacclient`/RET-08 für RBAC-06).
- `archive/internal/retentionnotify.Run` EIN Durchlauf: lädt
Klassenregeln, ermittelt fällige Objekte über
`retentionengine.ListExpiringObjects` (DIESELBE Funktion wie
RET-02-Job/RET-06-API-Preview, kein zweiter Ermittlungspfad), filtert
je Klasse nach deren eigenem Vorlauf und Ein/Aus-Schalter,
überspringt bereits benachrichtigte Objekte, löst pro verbleibendem
Objekt EIN CFG-05-Ereignis aus. Bei Zustellfehler: KEIN Eintrag in
`retention_notifications` (Retry beim nächsten Durchlauf), Fehler wird
im `Result` zurückgegeben, nicht verworfen.
- `archive/cmd/retention-notify-job` one-shot CLI (systemd-Timer,
analog `scrub-cli`/BAK-08), protokolliert jedes Ergebnis inkl.
Fehler über `log.Printf`.
- `deploy/systemd/nexarch-archive-retention-notify.{service,timer}.tmpl`.
## Prüfungen
| # | Prüfung | Ergebnis |
|---|---|---|
| 1 | Testfrist mit kurzem Vorlauf löst genau eine Benachrichtigung aus | **bestanden** `TestRun_ShortLeadTimeTriggersExactlyOneNotification`: fake-CFG-05-Server zählt Aufrufe, genau 1; real auf 131: Testobjekt mit 1-Tage-Frist/1-Tage-Vorlauf, Job manuell gestartet, `journalctl` zeigt genau eine Benachrichtigung mit echter `job_id`, echte Zeile in Core-`notification_jobs` (Status `pending`) bestätigt |
| 2 | Deaktivierte Benachrichtigung verschickt nachweislich nichts | **bestanden** `TestRun_DisabledNotificationSendsNothing`: `notify_enabled=false`, 0 Ergebnisse, 0 CFG-05-Aufrufe (Zähler geprüft, nicht nur "kein Fehler") |
| 3 | Fehlgeschlagener Versand wird protokolliert und nicht stillschweigend verworfen | **bestanden** `TestRun_FailedDeliveryIsReportedNotSwallowed`: fake-CFG-05-Server liefert 500, `Result.Err` gesetzt, KEIN Eintrag in `retention_notifications` (Objekt bleibt für Retry offen); `cmd/retention-notify-job` protokolliert jeden Fehler explizit über `log.Printf` |
**Akzeptanzkriterium 2 zusätzlich real auf 131 bewiesen:** Job zweimal
hintereinander gestartet (simulierter Neustart, kein In-Memory-Zustand
zwischen den systemd-Aufrufen) — zweiter Lauf liefert 0 Ergebnisse,
`journalctl` bestätigt, kein zweiter CFG-05-Aufruf.
## Echte Verdrahtung auf 192.168.1.131
- Migration `0006_retention_notify` real auf `dms_tenant_test`
angewendet.
- `retention-notify-job` gebaut nach `/opt/nexarch-archive/bin/`,
`/etc/nexarch/archive-retention-notify.env` (0600).
- `nexarch-archive-retention-notify.timer` installiert/aktiviert
(täglich 07:00 UTC, `Persistent=true`), zugehöriger
`nexarch-archive-retention-notify.service` (`Type=oneshot`).
- Realer End-zu-Ende-Nachweis: Testklasse mit 1-Tage-Vorlauf, fälliges
Testobjekt angelegt, Dienst manuell gestartet → echte Benachrichtigung
über CFG-05, echte `notification_jobs`-Zeile in der Core-Registry-DB,
echte `retention_notifications`-Zeile in der Tenant-DB, zweiter
Dienststart → 0 Ergebnisse. Alle Testdaten anschließend entfernt.
## Build/Test-Ergebnis (192.168.1.131)
```
go build ./... -> clean
go vet ./... -> clean
golangci-lint run ./... -> 0 issues
go test ./... -p 1 -> alle Archive-Pakete bestanden (inkl. retentionnotify, objectbackup, restoretest)
```
## Gesamtergebnis
**Bestanden.** Alle drei Akzeptanzkriterien und alle drei
Pflichtprüfungen real erfüllt, inklusive echtem systemd-Timer-Deploy
und End-zu-Ende-Nachweis über zwei physisch getrennte Module (Archive
→ CFG-05 → Core-Queue) sowie eines simulierten Job-Neustarts ohne
Doppelversand.