test(setup): pin the closed-window refusal on the TOTP step

Lock in the behaviour the fix introduces (docs/specs/spec-005-setup-wizard.md
§11 D-2): once the admin's bounded enrollment window has closed (SPEC-004 §4,
A1) `admin.totp.begin` answers a clean 409 and issues no secret, rather than
propagating the flow's "no pending identity" as a server fault.
This commit is contained in:
Claus Lohmar 2026-10-15 07:08:31 +00:00
parent 820dc005b3
commit 34f8ddbcfe

View file

@ -705,3 +705,31 @@ func TestSpec005SetupHoldsMidWizard(t *testing.T) {
t.Errorf("GET /setup after finish = %d, want 404 (D-045)", rec.Code)
}
}
// TestSpec005TOTPBeginRefusesAClosedWindow: the wizard's TOTP step must refuse
// once the admin's bounded enrollment window has closed (SPEC-004 §4, A1). It
// must not issue a secret, and the refusal must be a clean 4xx — not the
// server fault a bare propagation of the flow's "no pending identity" would be.
func TestSpec005TOTPBeginRefusesAClosedWindow(t *testing.T) {
h := newSetupHarness(t)
h.beginAdmin(t)
// Backdate the identity so it sits comfortably outside the bounded window.
if _, err := h.db.Exec(
`UPDATE identities SET created_at = ? WHERE user_id = ?`,
store.FormatTime(time.Now().Add(-365*24*time.Hour)), 1,
); err != nil {
t.Fatalf("backdate the identity: %v", err)
}
if inWindow, err := h.ids.WithinEnrollmentWindow(h.ctx, 1); err != nil || inWindow {
t.Fatalf("precondition: the window is still open (inWindow=%v err=%v)", inWindow, err)
}
rec := h.post("admin.totp.begin", nil)
if rec.Code != http.StatusConflict {
t.Fatalf("admin.totp.begin past the window = %d, want 409 (A1)", rec.Code)
}
if _, err := h.ids.TOTPSecret(h.ctx, 1); err == nil {
t.Error("the wizard issued a TOTP secret for an identity outside its window (A1)")
}
}