Files
felhom.eu/hub/internal/notify/storage_reconnect_cooldown_test.go
T
admin 655f5f66f8
gates / gates (push) Successful in 6m0s
hub v0.147.0 (code): the operator's answers S4, S6, S10, S29, S30, S32, S33
- S4 / R-242: GoldenVouchChecker - a published golden unvouched for 2 h raises golden_not_vouched (operator only)
- S6 / R-244: the delete cascade de-references app_log_issues; one-time sweep flag -sweep-app-log-residue=count|apply
- S10 / R-266: read_failed storage entry shows "read failed", not 0 %
- S29 / R-250: host-key scan IPv4 first, ~2 min ladder, "safe to press Save again" error
- S30 / R-246: stale_at retired (reader, setter, badge)
- S32 / R-521: storage_reconnected ends the storage_disconnected cooldown on both channels
- S33 / R-402: host page "Last full check (off-site)" line; two wire-gate allowlist entries removed
Each with a test seen failing first. Not built or deployed yet (operator-attended deploy).

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

43 lines
1.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package notify
import (
"io"
"log"
"testing"
)
// R-521 F6 (S32, `09` §3 decision 227). MEASURED 2026-09-14 (BIGNIGHT): a drive was unplugged, plugged
// back (`storage_reconnected`), and unplugged again 40 minutes later — and the second loss mailed
// NOBODY: the per-key cooldown armed by the first `storage_disconnected` outlived the recovery.
// A re-plug ends the incident, so it ends that incident's cooldown, on both channels.
//
// Also pins the S32 rule: the household gets the lost-drive mail ONLY when its own list carries
// `storage_disconnected`; a household that did not switch it on gets none.
// RED-PROOF: without the reset in processEvent the second unplug gives 1 operator / 1 customer mail.
func TestDriveLostAgainAfterReplugMailsAgain(t *testing.T) {
st := newDispStore(t)
if err := st.SaveNotificationPrefs("on", "house-on@example.com", []string{"storage_disconnected"}, 6); err != nil {
t.Fatal(err)
}
if err := st.SaveNotificationPrefs("off", "house-off@example.com", []string{"backup_failed"}, 6); err != nil {
t.Fatal(err)
}
d := NewDispatcher(st, "test-key", "from@felhom.eu", "op@felhom.eu", true, log.New(io.Discard, "", 0))
sent := captureSeam(d)
for _, c := range []string{"on", "off"} {
d.ProcessEvent(c, "storage_disconnected", "error", "drive lost", `{}`, "controller")
d.ProcessEvent(c, "storage_reconnected", "info", "drive back", `{}`, "controller")
d.ProcessEvent(c, "storage_disconnected", "error", "drive lost again", `{}`, "controller")
}
if got := len(mailsFor(*sent, "house-on@example.com")); got != 2 {
t.Fatalf("household that switched it on: %d lost-drive mails, want 2 (one per loss)", got)
}
if got := len(mailsFor(*sent, "house-off@example.com")); got != 0 {
t.Fatalf("household that did not switch it on got %d mails, want 0", got)
}
if got := len(mailsFor(*sent, "op@felhom.eu")); got != 4 {
t.Fatalf("operator: %d mails, want 4 (two losses × two customers)", got)
}
}