- Add 02-04-SUMMARY.md: signup form + handler + integration tests - Update STATE.md: plan 04 complete (4/7 in phase 2), plan 05 next - Update ROADMAP.md: 02-04 checked off, phase 2 progress 4/7
8.2 KiB
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | duration | completed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02-authentication | 04 | auth |
|
|
|
|
|
|
|
|
|
25min | 2026-05-14 |
Phase 2 Plan 04: Signup Vertical Slice Summary
Signup flow end-to-end: templ form + POST handler + argon2id hash + InsertUser + Store.Create + session cookie + 303 redirect wired into chi with ResolveSession (D-24)
Performance
- Duration: ~25 min
- Started: 2026-05-14T22:16:00Z
- Completed: 2026-05-14T22:18:30Z
- Tasks: 2
- Files modified: 11
Accomplishments
- Signup form (full page + HTMX fragment) renders with email/password fields; email round-trips; password never echoed
- POST /signup handler validates input, hashes with argon2id DefaultParams, inserts user, creates session, sets cookie, redirects
- HTMX fast-path returns 200 + HX-Redirect:/; non-JS path returns 303 See Other
- Duplicate email (pgconn 23505) renders "already in use" inline; no second row inserted
- Authed users hitting GET /signup bounce to / (RedirectIfAuthed)
- ResolveSession wired at position 5 in middleware stack per D-24
- 8 integration tests all pass against real Postgres; Phase 1 unit tests unaffected
Task Commits
- Task 1: Signup templates + render smoke tests -
73935ed(feat) - Task 2: SignupPostHandler + router wiring + integration tests -
efdc16b(feat)
Files Created/Modified
backend/templates/auth_forms.go- SignupForm and SignupErrors types (in templates pkg to avoid import cycle)backend/templates/auth_form_errors.templ- FieldError and GeneralError templ primitivesbackend/templates/auth_signup.templ- SignupPage (full) + SignupFormFragment (HTMX swap target)backend/templates/auth_signup_test.go- 3 render smoke tests (form renders, error renders, password not echoed)backend/internal/web/handlers_auth.go- SignupPageHandler, SignupPostHandler, AuthDeps, renderSignupErrorbackend/internal/web/handlers_auth_test.go- 8 TestSignup_* integration testsbackend/internal/web/testdb_test.go- setupTestDB helper (duplicated from auth package)backend/internal/web/router.go- NewRouter updated: accepts AuthDeps, mounts ResolveSession + signup routesbackend/internal/web/handlers_test.go- Pass AuthDeps{} to NewRouter (Phase 1 tests updated)backend/cmd/web/main.go- Build AuthDeps (sqlc.Queries + auth.Store + secure flag), pass to NewRouterbackend/internal/auth/middleware.go- nil-Store guard in ResolveSession for Phase 1 test compatibility
Decisions Made
Where SignupForm/SignupErrors live: Templates package (backend/templates/auth_forms.go). The handler file imports the templates package; defining types in templates avoids a circular dependency (templates importing internal/web). Handlers reference these types as templates.SignupForm, templates.SignupErrors.
Test helper strategy: Duplicated setupTestDB (~100 LOC) into backend/internal/web/testdb_test.go. Go does not allow importing _test.go files across packages. The duplication is minimal and keeps test infra out of production code paths. Alternative (shared internal/testhelpers package) would put infra in a non-test file.
POST /signup outside RedirectIfAuthed group: The GET /signup route is inside the RedirectIfAuthed group. POST /signup is intentionally outside because an authed user directly submitting the form should get a proper error response rather than a silent 303 that could confuse tooling.
nil-Store guard in ResolveSession: Phase 1 unit tests in handlers_test.go call NewRouter(stubPinger{}, "./static", AuthDeps{}) with a zero-value AuthDeps (nil Store). Without the nil guard, ResolveSession would panic on cookie lookup. The guard is a no-op pass-through when Store == nil.
Deviations from Plan
Auto-fixed Issues
1. [Rule 2 - Missing Critical] nil-Store guard added to auth.ResolveSession
- Found during: Task 2 (updating handlers_test.go to pass AuthDeps{})
- Issue: Phase 1 tests pass
AuthDeps{}zero value; ResolveSession would panic onstore.Lookup()call if any cookie was present (or if future tests hit that path) - Fix: Added
if store == nil { next.ServeHTTP(w, r); return }at the top of the returned handler closure - Files modified: backend/internal/auth/middleware.go
- Verification: Phase 1 tests pass with
AuthDeps{}zero value; all Phase 2 signup tests pass with real store - Committed in:
efdc16b
Total deviations: 1 auto-fixed (Rule 2 — missing critical nil guard) Impact on plan: Essential for backward compatibility with Phase 1 unit tests. No scope creep.
Issues Encountered
None — plan executed cleanly.
Known Stubs
None — the signup form is fully wired end-to-end with real DB operations.
Next Phase Readiness
- Plan 05 (login handler) can now build on the same
AuthDepspattern and templ primitives SignupFormFragmentandFieldError/GeneralErrorprimitives are ready for reuse in the login formResolveSessionis live in the middleware stack;RequireAuthis implemented (Plan 03) and ready for Plan 06 protected routesauth.TestParams+ directInsertUserpre-seeding pattern is established for future tests
Phase: 02-authentication Completed: 2026-05-14