Adds 4 BDD scenarios covering the passwordless magic-link flow:
- Happy path (request -> email arrives -> consume -> JWT)
- Token cannot be consumed twice (single-use guarantee)
- Missing token returns 400
- Unknown token returns 401
Implementation:
- features/auth/magic_link.feature with the gherkin spec
- pkg/bdd/steps/magic_link_steps.go: per-scenario unique recipient
(`<scenario-key>-<8hex>@bdd.local`, ADR-0030), Mailpit-driven token
extraction, regex parse of the consume URL
- pkg/bdd/steps/scenario_state.go: 2 fields added (MagicLinkEmail,
MagicLinkToken)
- pkg/bdd/steps/steps.go: register 5 new step regexes
Bug fix exposed by the BDD run:
- pkg/user/api/magic_link_handler.go: passwordless-signup random password
was 96 hex chars (48 bytes) which overflowed bcrypt's 72-byte input
limit, breaking first-link signup. Reduced to 64 hex chars (32 bytes,
256 bits entropy).
Test infra fix:
- pkg/bdd/testserver/server.go: createTestConfig() builds the
Config literal directly (no Viper defaults), so add explicit Email +
MagicLink config so the From address is set when the handler sends
via local Mailpit.
Mistral wrote the feature file, magic_link_steps.go, scenario_state.go
edit, and steps.go edit autonomously in a worktree workspace. Claude
fixed the bcrypt overflow + the test-config gap exposed during verification.
Most authoring by Mistral Vibe (mistral-vibe-cli-latest).
Per-package isolated Postgres schema with migrations. Local benchmark: 12.87s sequential → 4.51s parallel = 2.85x. ADR-0025 status to Implemented. CI uses BDD_SCHEMA_ISOLATION=true.
Co-authored-by: Gabriel Radureau <arcodange@gmail.com>
Co-committed-by: Gabriel Radureau <arcodange@gmail.com>
NewPostgresRepositoryFromDSN factory + BuildSchemaIsolatedDSN helper + integration test proving per-schema isolation works at repo level. Foundation for T12. Wiring into testserver is stage 2/2.
Co-authored-by: Gabriel Radureau <arcodange@gmail.com>
Co-committed-by: Gabriel Radureau <arcodange@gmail.com>