Files
felhom-controller/controller/internal/web/r521_storage_disconnected_option_test.go
T
admin fd28d70bff v0.308.0: controller batch 2026-10-12 — clear-address warning (R-922), secret sweep over every page (R-255), read_failed (R-266), no NVMe heat band (R-333), share globe (R-577), Hungarian dates and numbers (R-562), lost-drive option pinned (R-521)
Operator rulings 2026-10-12, 09 §3 decisions 196, 204-207, 223, 227. MinAgent 0.131.0 unchanged.
R-255's sweep found one leak, fixed here: an optional-config secret_input was rendered into its
settings page; it is now revealed on demand. Hungarian parity fixtures re-captured once for R-562.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-12 15:13:59 +02:00

70 lines
2.5 KiB
Go

package web
import (
"encoding/json"
"net/url"
"strings"
"testing"
)
// R-521 controller half (operator ruling 2026-10-12, S32, `09` §3 decision 227): the household's own
// notification settings carry a lost-drive option (`storage_disconnected`), saved and sent to the hub in
// `enabled_events` exactly like the others — and ONLY when the household has it on. The hub mails a lost
// drive only when this setting carries it.
//
// RED-PROOF (REPORT.md): drop "storage_disconnected" from the single-event list in
// settingsNotificationsHandler → the "on" half fails (the option is silently not saved and not sent).
func TestR521_StorageDisconnectedOptionRoundTrips(t *testing.T) {
eventsOf := func(t *testing.T, body map[string]json.RawMessage) []string {
t.Helper()
var ev []string
if err := json.Unmarshal(body["enabled_events"], &ev); err != nil {
t.Fatalf("enabled_events is not a list: %s", body["enabled_events"])
}
return ev
}
has := func(list []string, e string) bool {
for _, x := range list {
if x == e {
return true
}
}
return false
}
// ON: saved, sent, and the page shows it ticked.
s, sett, hub := r922Server(t)
s.loadTemplates()
rr := postNotifications(t, s, url.Values{
"notification_email": {"household@example.hu"},
"cooldown_hours": {"6"},
"event_backup_failed": {"on"},
"event_storage_disconnected": {"on"},
})
if p := sett.GetNotificationPrefs(); !has(p.EnabledEvents, "storage_disconnected") {
t.Fatalf("on: storage_disconnected not saved (stored %v)", p.EnabledEvents)
}
if ev := eventsOf(t, hub.last(t)); !has(ev, "storage_disconnected") {
t.Fatalf("on: storage_disconnected not sent to the hub (enabled_events %v)", ev)
}
if !strings.Contains(rr.Body.String(), `name="event_storage_disconnected" checked`) {
t.Error("on: the saved option does not render ticked")
}
// OFF: the same household switches it off — not saved, not sent, not ticked.
rr = postNotifications(t, s, url.Values{
"notification_email": {"household@example.hu"},
"cooldown_hours": {"6"},
"event_backup_failed": {"on"},
})
if p := sett.GetNotificationPrefs(); has(p.EnabledEvents, "storage_disconnected") {
t.Fatalf("off: storage_disconnected still saved (stored %v)", p.EnabledEvents)
}
if ev := eventsOf(t, hub.last(t)); has(ev, "storage_disconnected") {
t.Fatalf("off: storage_disconnected still sent to the hub (enabled_events %v)", ev)
}
if strings.Contains(rr.Body.String(), `name="event_storage_disconnected" checked`) {
t.Error("off: the option still renders ticked")
}
}