package web import ( "net/http" "strings" "testing" "time" ) // R-304 option C (decision 183): the operator is mailed when a household's code opens (422) or may open (424) an older // sealed package — once per day per box, never on a wrong code, and never with the code in it. // RED-PROOF: remove the two notifyRecoveryOlderPackage calls from recoveryUnlockHandler → no event → the first // assertion FAILS; drop the day check → the second unlock sends a second event → FAILS. type olderEvent struct{ typ, sev, msg, class string } func olderFixture(t *testing.T, now *time.Time) (*recoveryFixture, *[]olderEvent) { t.Helper() f := newRecoveryFixture(t) var evs []olderEvent f.s.recoveryOlderPushFn = func(typ, sev, msg string, d map[string]string) { evs = append(evs, olderEvent{typ, sev, msg, d["class"]}) } f.s.SetRecoveryClock(func() time.Time { return *now }) return f, &evs } func TestR304_OlderPackageMail_OncePerDay(t *testing.T) { now := time.Date(2026, 10, 8, 10, 0, 0, 0, time.UTC) f, evs := olderFixture(t, &now) f.rec.failWith = refusal(422, "the recovery code is correct, but it belongs to an EARLIER sealed package") postUnlockWith(t, f.s, testRecoveryCode) if len(*evs) != 1 || (*evs)[0].typ != "recovery_older_package" || (*evs)[0].sev != "warning" || (*evs)[0].class != "code-opens-retained" { t.Fatalf("events = %+v, want one warning recovery_older_package (code-opens-retained)", *evs) } if strings.Contains((*evs)[0].msg, testRecoveryCode) { t.Fatal("the operator mail must never carry the recovery code") } now = now.Add(3 * time.Hour) postUnlockWith(t, f.s, testRecoveryCode) if len(*evs) != 1 { t.Fatalf("a second try the same day sent %d events, want still 1", len(*evs)) } // A controller restart the same day: a new Server over the same data dir must not mail again. f.s.recoveryOlderLastDay = "" postUnlockWith(t, f.s, testRecoveryCode) if len(*evs) != 1 { t.Fatalf("after a restart the same day: %d events, want still 1 (the day is on disk)", len(*evs)) } now = now.Add(24 * time.Hour) f.rec.failWith = refusal(http.StatusFailedDependency, "not all checked") postUnlockWith(t, f.s, testRecoveryCode) if len(*evs) != 2 || (*evs)[1].class != "older-unchecked" { t.Fatalf("next day, a 424: events = %+v, want a second one (older-unchecked)", *evs) } } func TestR304_OlderPackageMail_NotOnAWrongCode(t *testing.T) { now := time.Date(2026, 10, 8, 10, 0, 0, 0, time.UTC) f, evs := olderFixture(t, &now) f.rec.failWith = refusal(400, "the recovery code did not open the sealed bundle") postUnlockWith(t, f.s, testRecoveryCode) if len(*evs) != 0 { t.Fatalf("a plain wrong code must not mail the operator: %+v", *evs) } }