Files
felhom.eu/hub/internal/notify/d53_held_reminder_test.go
T
admin 7e57e824a2
gates / gates (push) Successful in 1m58s
hub(D53): held apps reach the operator — mirror, Held apps list (host + Hosts page), reminder after the grace
app_held / app_hold_cleared allow-listed and operator-only; handleEvent mirrors them in held_apps; HeldAppsChecker
sends ONE app_held_reminder (warning, operator-only, per-app cooldown) after the grace per reason (6 h / 1 d / 3 d),
then weekly; recorded before sending; stale rows not reminded; the hub never clears a hold. Test-only
alerting.held_app_grace_override. TestD53_* in api/monitor/notify/web, red-proved by six mutations. Commit only.
2026-10-26 18:25:00 +01:00

44 lines
2.0 KiB
Go

package notify
import (
"strings"
"testing"
)
// D53 (operator ruling 2026-10-26: operator-only reminders first): the held-app reminder reaches the OPERATOR — one mail
// whose subject names the app — and never the household, even with every event switched on (the R-97c configuration).
// The box's own app_held / app_hold_cleared are info: they mail nobody on either leg.
func TestD53_TheHeldAppReminderMailsTheOperatorOnly(t *testing.T) {
st := opOnlyStore(t)
if err := st.SaveNotificationPrefs("c1", "customer@example.com", []string{"app_held_reminder", "app_held", "app_hold_cleared", "backup_failed"}, 6); err != nil {
t.Fatal(err)
}
var to, subjects []string
d := opOnlyDispatcher(t, st, &sentTo{})
d.sendEmailFn = func(addr, subject, body string, headers map[string]string) error {
to = append(to, addr)
subjects = append(subjects, subject)
return nil
}
d.ProcessBoxEvent("c1", "app_held", "info", "App kimai is held: …", "", `{"stack_name":"kimai","reason":"unhealthy_stop"}`, "controller")
d.ProcessBoxEvent("c1", "app_hold_cleared", "info", "App kimai is no longer held", "", `{"stack_name":"kimai"}`, "controller")
if len(to) != 0 {
t.Fatalf("the box's held-app events mailed %v — they must be recorded, never mailed", to)
}
d.ProcessEvent("c1", "app_held_reminder", "warning", "Customer c1: the app kimai has been held for 3 days …",
`{"stack_name":"kimai","reason":"unhealthy_stop"}`, "hub")
d.ProcessEvent("c1", "app_held_reminder", "warning", "Customer c1: the app paperless has been held for 3 days …",
`{"stack_name":"paperless","reason":"update_failed"}`, "hub")
for _, a := range to {
if a == "customer@example.com" {
t.Fatal("the household was mailed an operator-only held-app reminder")
}
}
if len(to) != 2 {
t.Fatalf("want 2 operator mails (two apps, per-app cooldown), got %d: %v", len(to), to)
}
if !strings.Contains(subjects[0], "kimai is still held") || !strings.Contains(subjects[1], "paperless is still held") {
t.Fatalf("each subject must name its app: %q", subjects)
}
}