a40729a698
gates / gates (push) Successful in 59s
Unreleased; ships with tomorrow's release (needs the hub of the same day). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
67 lines
2.6 KiB
Go
67 lines
2.6 KiB
Go
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)
|
|
}
|
|
}
|