6.2 KiB
| phase | slug | status | nyquist_compliant | wave_0_complete | created |
|---|---|---|---|---|---|
| 2 | authentication | draft | false | false | 2026-05-14 |
Phase 2 — Validation Strategy
Per-phase validation contract for feedback sampling during execution.
Test Infrastructure
| Property | Value |
|---|---|
| Framework | Go testing + go test (stdlib) |
| Config file | backend/go.mod (no separate config) |
| Quick run command | cd backend && go test ./internal/auth/... |
| Full suite command | cd backend && go test ./... |
| Estimated runtime | ~15–30 seconds (argon2 tests use reduced params) |
DB-touching tests require TEST_DATABASE_URL pointing at the compose-managed Postgres (see Phase 1 pattern in backend/internal/db/pool_test.go). Each test runs in a transaction that rolls back, OR uses a per-test schema name to avoid cross-contamination.
Sampling Rate
- After every task commit: Run
go test ./internal/auth/... - After every plan wave: Run
go test ./... - Before
/gsd-verify-work: Full suite must be green - Max feedback latency: 30 seconds
Per-Task Verification Map
Filled by planner as PLAN.md tasks are emitted. Each task with type: execute must have an <automated> verify command OR an explicit Wave 0 dependency. Tasks creating templ/HTML output may need a small Go test that renders the component and asserts the resulting HTML contains expected markers.
| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---|---|---|---|---|---|---|---|---|---|
| 01-T1 | 01 | 1 | AUTH-01,AUTH-02 | T-2-08b | Schema, sqlc gen, test harness | integration | cd backend && go build ./... && TEST_DATABASE_URL=$DATABASE_URL go test ./internal/db/... -count=1 |
⬜ | ⬜ pending |
| 02-T1 | 02 | 2 | AUTH-01 | T-2-01 | argon2id PHC Hash/Verify, malformed-PHC + version-mismatch rejection (RED→GREEN→REFACTOR) | unit (tdd) | cd backend && go test ./internal/auth -run "TestPassword_" -count=1 |
⬜ | ⬜ pending |
| 03-T1 | 03 | 2 | AUTH-02,AUTH-03,AUTH-05 | T-2-04,T-2-05,T-2-06 | Session create/lookup/rotate/extend; cookie attrs; ResolveSession/RequireAuth/RedirectIfAuthed | integration | cd backend && TEST_DATABASE_URL=$DATABASE_URL go test ./internal/auth -run "TestSession_|TestCookie_|TestResolveSession|TestRequireAuth|TestRedirectIfAuthed" -count=1 |
⬜ | ⬜ pending |
| 04-T1 | 04 | 3 | AUTH-01,AUTH-03,AUTH-05 | T-2-01,T-2-03 | Signup E2E: validate → argon2 → InsertUser → Store.Create → 303; RedirectIfAuthed wraps GET /signup | integration | cd backend && templ generate && TEST_DATABASE_URL=$DATABASE_URL go test ./internal/web -run "TestSignup_" -count=1 |
⬜ | ⬜ pending |
| 05-T1 | 05 | 4 | AUTH-02,AUTH-03,AUTH-05,AUTH-07 | T-2-02,T-2-03 | Login E2E: rate-check → Verify → Rotate → cookie → 303; generic error string; rate limiter burst+per-key+janitor with injectable clock | integration | cd backend && templ generate && TEST_DATABASE_URL=$DATABASE_URL go test ./internal/web ./internal/auth -run "TestLogin_|TestRateLimit_" -count=1 |
⬜ | ⬜ pending |
| 06-T1 | 06 | 5 | AUTH-04,AUTH-05 | T-2-07,T-2-10 | Logout deletes row + clears cookie; / now protected; Layout shows logout button only when authed | integration | cd backend && templ generate && TEST_DATABASE_URL=$DATABASE_URL go test ./internal/web ./templates -run "TestLogout_|TestProtected_|TestLayout_" -count=1 |
⬜ | ⬜ pending |
| 07-T1 | 07 | 6 | AUTH-06 | T-2-08,T-2-08a,T-2-08b,T-2-08c,T-2-08d | gorilla/csrf mounted after ResolveSession; @ui.CSRFField in every form; SESSION_SECRET from env; 403 on missing token, 200/303 on valid | integration | cd backend && templ generate && TEST_DATABASE_URL=$DATABASE_URL go test ./internal/web ./internal/auth ./cmd/web -run "TestCSRF_|TestForms_ContainCSRFField|TestRouter_CSRFMountedAfterResolveSession|TestLoadCSRFKey" -count=1 |
⬜ | ⬜ pending |
Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky
Wave 0 Requirements
backend/internal/auth/password_test.go— argon2 hash/verify round-trip with reduced test paramsbackend/internal/auth/session_test.go— token generate + SHA-256 lookup, expiry semanticsbackend/internal/auth/ratelimit_test.go— per-key bucket allow/deny with injectable clockbackend/internal/auth/middleware_test.go— ResolveSession context plumbing, RequireAuth 303 / HX-Redirect- Reuse
backend/internal/db/pool_test.goPostgres harness for users/sessions integration tests
Existing Go test infrastructure (Phase 1) covers framework setup — no new tooling install needed.
Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions |
|---|---|---|---|
Cookie is HttpOnly and Secure in non-dev environments |
AUTH-03 | Browser DevTools required to inspect cookie attributes from a live response | Run ENV=staging server, hit /login, submit valid creds, open DevTools → Application → Cookies; confirm HttpOnly=true, Secure=true, SameSite=Lax |
/login page visual layout matches Phase 1 design system |
AUTH-01 / D-69 | Visual assertion | Open http://localhost:8080/login, confirm Button/Card/Badge styling from internal/web/ui is applied; confirm /signup mirrors |
| HTMX inline form-error swap on bad credentials | AUTH-02 | Requires browser to evaluate HX-Request round-trip |
Submit /login with bad password via HTMX form; only the form fragment swaps with "Invalid email or password" — no full page reload |
| Rate-limit error fragment shows after 6th failed login | AUTH-06 | Requires browser to observe 429 + swap | Submit 6 wrong-password attempts in <60s from same IP; 6th returns 429 with "Too many attempts" inline fragment |
Validation Sign-Off
- All tasks have
<automated>verify or Wave 0 dependencies - Sampling continuity: no 3 consecutive tasks without automated verify
- Wave 0 covers all MISSING references
- No watch-mode flags
- Feedback latency < 30s
nyquist_compliant: trueset in frontmatter
Approval: pending