12552acabd
gates / gates (push) Successful in 6m45s
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
44 lines
2.0 KiB
Go
44 lines
2.0 KiB
Go
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")
|
||
}
|
||
// R-1049 (2026-10-15): the return now mails too — 2 losses + 1 return.
|
||
if got := len(mailsFor(*sent, "house-on@example.com")); got != 3 {
|
||
t.Fatalf("household that switched it on: %d mails, want 3 (one per loss + the return, R-1049)", 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 != 6 {
|
||
t.Fatalf("operator: %d mails, want 6 (two losses + one return, × two customers; R-1049)", got)
|
||
}
|
||
}
|