feat(hub): v0.57.0 reinstall-of-existing-customer arc — claim/offsite/escrow continuity
F2 claim re-issue on clean-slate re-enroll (ReissueForReenroll, host-enroll mint path, single-bump, reset code; hub never stores the password so fork B). F3 offsite re-issue on re-enroll (ReissueOffsiteForCustomer, same machinery as the manual button). 2.3 escrow honesty (red-proofed): re-issuing offsite marks the escrow stale (MarkEscrowStale), withholds the mismatched restic hash from auto-confirm, DR checklist shows stale not done. Events: claim_reissued_reenroll / offsite_reissued / escrow_stale. Controller + scripts unchanged (source contradicted both premises): the controller reads escrow prereqs live from the agent; the installer can't know the descriptor-provisioned storage id. F4 root fix is agent-side -> ROADMAP R-22; demo unblocked live (Part 0 ACL grant). VALIDATION doc F2 erratum + F3/F4 dispositions. Green gate + Scenario-C red-proof pass.
This commit is contained in:
@@ -139,6 +139,56 @@ func TestResend_ClaimedGetsResetTemplateAndStaysClaimed(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// v0.57.0 (F2) — ReissueForReenroll rotates + emails a RESET code for a CLAIMED customer whose box
|
||||
// was clean-slate reinstalled (fresh box has no password), and is a NO-OP for an unclaimed customer
|
||||
// (the first-provision path, where EnsureIssued owns the first code — re-enrolling must not rotate).
|
||||
func TestReissueForReenroll(t *testing.T) {
|
||||
t.Run("claimed rotates and sends the reset template", func(t *testing.T) {
|
||||
e, st, m := newTestEngine(t)
|
||||
if _, err := e.EnsureIssued(cust()); err != nil {
|
||||
t.Fatalf("EnsureIssued: %v", err)
|
||||
}
|
||||
if err := e.MarkClaimed(cust()); err != nil {
|
||||
t.Fatalf("MarkClaimed: %v", err)
|
||||
}
|
||||
sendsBefore := len(m.sends)
|
||||
gen, reissued, err := e.ReissueForReenroll(cust())
|
||||
if err != nil {
|
||||
t.Fatalf("ReissueForReenroll: %v", err)
|
||||
}
|
||||
if !reissued {
|
||||
t.Fatal("a CLAIMED customer must re-issue a code on box re-enrollment")
|
||||
}
|
||||
cs, _ := st.GetClaim("c1")
|
||||
if gen < 2 || cs.Generation != gen {
|
||||
t.Fatalf("re-enroll must bump the generation once: gen=%d stored=%d", gen, cs.Generation)
|
||||
}
|
||||
if !cs.Claimed() {
|
||||
t.Fatal("re-issue must NEVER un-claim (reset rides rotation)")
|
||||
}
|
||||
if len(m.sends) != sendsBefore+1 || !strings.HasPrefix(m.sends[len(m.sends)-1], "reset:") {
|
||||
t.Fatalf("claimed re-enroll must send exactly one RESET email, got %v", m.sends)
|
||||
}
|
||||
})
|
||||
t.Run("unclaimed is a no-op (first-provision path)", func(t *testing.T) {
|
||||
e, _, m := newTestEngine(t)
|
||||
if _, err := e.EnsureIssued(cust()); err != nil { // issued but NOT claimed
|
||||
t.Fatalf("EnsureIssued: %v", err)
|
||||
}
|
||||
sendsBefore := len(m.sends)
|
||||
_, reissued, err := e.ReissueForReenroll(cust())
|
||||
if err != nil {
|
||||
t.Fatalf("ReissueForReenroll: %v", err)
|
||||
}
|
||||
if reissued {
|
||||
t.Fatal("an UNCLAIMED customer must NOT re-issue on re-enroll (first provision owns the code)")
|
||||
}
|
||||
if len(m.sends) != sendsBefore {
|
||||
t.Fatalf("no email may be sent on an unclaimed re-enroll, got %v", m.sends)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// RequestReset caps at 3/day per customer, hub-side.
|
||||
func TestRequestReset_DailyCap(t *testing.T) {
|
||||
e, _, m := newTestEngine(t)
|
||||
|
||||
Reference in New Issue
Block a user