Files
felhom.eu/hub/internal/notify/r1049_drive_back_test.go
T

56 lines
3.1 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"
)
// TestR1049_DriveBackMailsBothEdges pins the CONSEQUENCE of R-1049: a drive that is pulled and comes back mails the
// operator BOTH edges, twice in a row inside the hour (the 2026-10-14 night: pulls at 22:55 and 23:21), and the household
// hears the return only if it heard the loss. Measured before the fix: the hub received `storage_reconnected` and
// `health_recovered` and logged no notification at all (audits/media-drive-events-2026-10-14/K8-hub-mails.txt).
//
// COMPANION RED-PROOF (2026-10-15): remove the two R-1049 entries from recoveredPairedDownTypes → the operator gets 4
// mails, not 8, and the household 1, not 2; this test fails. Restored.
func TestR1049_DriveBackMailsBothEdges(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)
// One pull per customer for the household leg: the customer pairing compares SECOND-granular send times and a tie
// is „no mail" by design (flap-safe), so a second pull in the same test second would be skipped for a reason that
// has nothing to do with R-1049. The operator leg has no pairing: two pulls inside the hour, both edges each time.
for _, c := range []string{"on", "off"} {
d.ProcessEvent(c, "storage_disconnected", "error", "Meghajtó váratlanul leválasztva: Media B", `{"label":"Media B"}`, "controller")
d.ProcessEvent(c, "storage_reconnected", "info", "Meghajtó újra csatlakoztatva: Media B", `{"label":"Media B"}`, "controller")
}
if got := len(mailsFor(*sent, "house-on@example.com")); got != 2 {
t.Fatalf("household that hears losses: %d mails, want 2 (the loss and the return)", got)
}
for _, c := range []string{"on", "off"} {
d.ProcessEvent(c, "storage_disconnected", "error", "Meghajtó váratlanul leválasztva: Media C", `{"label":"Media C"}`, "controller")
d.ProcessEvent(c, "storage_reconnected", "info", "Meghajtó újra csatlakoztatva: Media C", `{"label":"Media C"}`, "controller")
}
if got := len(mailsFor(*sent, "op@felhom.eu")); got != 8 {
t.Fatalf("operator: %d mails, want 8 (two customers × two pulls × loss + return)", got)
}
if got := len(mailsFor(*sent, "house-off@example.com")); got != 0 {
t.Fatalf("household that hears no losses: %d mails, want 0 — a return it was never told about is noise", got)
}
// health_recovered pairs with health_degraded for the operator the same way.
d.ProcessEvent("on", "health_degraded", "warning", "Rendszer állapot romlott (volt: ok)", `{}`, "controller")
before := len(mailsFor(*sent, "op@felhom.eu"))
d.ProcessEvent("on", "health_recovered", "info", "Rendszer állapot helyreállt: ok (volt: warn)", `{}`, "controller")
if got := len(mailsFor(*sent, "op@felhom.eu")); got != before+1 {
t.Fatalf("health_recovered: the operator got %d new mail(s), want 1", got-before)
}
}