R-304 option C: operator mail when a household's code opens or may open an older package (once a day); R-298 side fix (no eject/format on a backup-target drive); R-717 comments
gates / gates (push) Successful in 59s
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
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user