feat: Wiedervorlage und Katalog-Benachrichtigung (Schritt 7 der Baureihenfolge)

GET /wiedervorlage (Ebene 3) zeigt aktive Genehmigungen, die erneut
geprüft werden sollten: Ablaufdatum erreicht oder innerhalb von 30
Tagen, das zugesagte Werkzeug wurde aus dem Katalog entfernt, oder der
aktuelle Katalogeintrag weicht bei AVV/Training-Standard/
Verarbeitungsort vom zum Entscheidungszeitpunkt eingefrorenen Snapshot
ab. Keine E-Mail-Infrastruktur vorhanden — Benachrichtigung ist bewusst
eine In-App-Liste. Damit ist die ursprüngliche Baureihenfolge
(Schritt 1-7) vollständig umgesetzt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
noroot
2026-08-29 14:12:44 +02:00
parent 22f748d66c
commit 5f6502bb41
6 changed files with 251 additions and 5 deletions

View File

@@ -0,0 +1,95 @@
package web_test
import (
"net/http"
"strings"
"testing"
"time"
)
func TestWiedervorlageZeigtAbgelaufeneGenehmigung(t *testing.T) {
fs := newFakeStore()
s := newServer(t, fs)
accountID := genehmigeFall(t, fs, s)
var antragID string
for id, a := range fs.antraege {
if a.AccountID == accountID {
antragID = id
}
}
es := fs.entscheidungen[antragID]
abgelaufen := time.Now().Add(-24 * time.Hour)
es[len(es)-1].GueltigBis = &abgelaufen
fs.entscheidungen[antragID] = es
prueferCookie := seedUserInAccount(t, fs, accountID, "reviewer@example.com", "pruefer")
resp := getWithCookie(t, s, prueferCookie, "/wiedervorlage")
if resp.Code != http.StatusOK {
t.Fatalf("status = %d, body: %s", resp.Code, resp.Body.String())
}
if !strings.Contains(resp.Body.String(), "abgelaufen") {
t.Errorf("expected the abgelaufene Genehmigung to appear, got: %s", resp.Body.String())
}
}
func TestWiedervorlageZeigtGeaenderteWerkzeugeigenschaft(t *testing.T) {
fs := newFakeStore()
s := newServer(t, fs)
accountID := genehmigeFall(t, fs, s)
wz := fs.werkzeuge["werkzeug-ok"]
wz.AVVVerfuegbar = false // Katalog hat sich seit der Genehmigung geändert
fs.werkzeuge["werkzeug-ok"] = wz
prueferCookie := seedUserInAccount(t, fs, accountID, "reviewer@example.com", "pruefer")
resp := getWithCookie(t, s, prueferCookie, "/wiedervorlage")
if resp.Code != http.StatusOK {
t.Fatalf("status = %d, body: %s", resp.Code, resp.Body.String())
}
if !strings.Contains(resp.Body.String(), "AVV verfügbar") {
t.Errorf("expected the changed AVV property to be flagged, got: %s", resp.Body.String())
}
}
func TestWiedervorlageZeigtEntferntesWerkzeug(t *testing.T) {
fs := newFakeStore()
s := newServer(t, fs)
accountID := genehmigeFall(t, fs, s)
delete(fs.werkzeuge, "werkzeug-ok")
prueferCookie := seedUserInAccount(t, fs, accountID, "reviewer@example.com", "pruefer")
resp := getWithCookie(t, s, prueferCookie, "/wiedervorlage")
if resp.Code != http.StatusOK {
t.Fatalf("status = %d, body: %s", resp.Code, resp.Body.String())
}
if !strings.Contains(resp.Body.String(), "aus dem Katalog entfernt") {
t.Errorf("expected the removed werkzeug to be flagged, got: %s", resp.Body.String())
}
}
func TestWiedervorlageIstLeerOhneAenderung(t *testing.T) {
fs := newFakeStore()
s := newServer(t, fs)
accountID := genehmigeFall(t, fs, s)
prueferCookie := seedUserInAccount(t, fs, accountID, "reviewer@example.com", "pruefer")
resp := getWithCookie(t, s, prueferCookie, "/wiedervorlage")
if resp.Code != http.StatusOK {
t.Fatalf("status = %d, body: %s", resp.Code, resp.Body.String())
}
if !strings.Contains(resp.Body.String(), "Nichts zur Wiedervorlage") {
t.Errorf("expected no entries when nothing changed, got: %s", resp.Body.String())
}
}
func TestMitarbeiterCannotAccessWiedervorlage(t *testing.T) {
fs := newFakeStore()
s := newServer(t, fs)
cookie := seedAccountWithRole(t, fs, "Test-Mandant", "mitarbeiter@example.com", "mitarbeiter")
resp := getWithCookie(t, s, cookie, "/wiedervorlage")
if resp.Code != http.StatusNotFound {
t.Fatalf("status = %d, want 404 for role mitarbeiter", resp.Code)
}
}