6.0 KiB
6.0 KiB
phase, slug, status, nyquist_compliant, wave_0_complete, created
| phase | slug | status | nyquist_compliant | wave_0_complete | created |
|---|---|---|---|---|---|
| 7 | user-plugin-and-authentication | draft | false | false | 2026-09-22 |
Phase 7 — Validation Strategy
Per-phase validation contract for feedback sampling during execution.
Test Infrastructure
| Property | Value |
|---|---|
| Framework | Go stdlib testing + testify (assert/require); net/http/httptest for handler, guard, limiter and locale tests; testcontainers-go v0.44.0 (modules/postgres) only where the throttle table, jti blacklist, token CRUD and parity replay need real rows; postcard memory driver for mail assertions |
| Config file | none — plain func TestX(t *testing.T); testing.Short() gates container-backed tests (convention from lagoon/postgres_test.go, postcard/mailpit_test.go, plugins/golem15/user/updates/postgres_test.go); parity TestMain in ../fonoteka.go/parity is reused |
| Quick run command | go vet ./... && go test ./... -short (run in the repo the task writes to: summercms.go or ../fonoteka.go) |
| Full suite command | go test ./... -race in summercms.go and in ../fonoteka.go, plus summer parity:replay --manifest fonoteka.go/parity/manifest.yaml |
| Estimated runtime | ~20 s quick, ~120–180 s full |
Sampling Rate
- After every task commit: Run
go vet ./... && go test ./... -shortin the repo the task touched - After every plan wave: Run
go test ./... -racein both modules +summer parity:replayagainst the fixtures recorded so far - Before
/gsd:verify-work: Full suite green in both modules, every D-11 fixture recorded and replayed - Max feedback latency: 30 s (quick command)
Per-Task Verification Map
| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---|---|---|---|---|---|---|---|---|---|
| TBD | TBD | TBD | AUTH-01 | T-07-xx | login/register/logout/fetch/refresh return PHP-identical status+body; tokens never read from URL/body | unit + integration | go test ./plugins/golem15/user/... -run TestApiController -short |
❌ W0 | ⬜ pending |
| TBD | TBD | TBD | AUTH-01 | T-07-xx | sliding refresh accepts expired-but-within-refresh_ttl, rejects past it; logout blacklists forever; grace window honoured |
unit | `go test ./bouncer/... -run 'TestRefresh | TestBlacklist'` | ❌ W0 |
| TBD | TBD | TBD | AUTH-01 | T-07-xx | $2y$ PHP hashes verify; lower-cost hash rehashed on login; per-(user,ip) throttle suspends after 5 |
unit + integration | `go test ./plugins/golem15/user/... -run 'TestPassword | TestThrottle' -short` | ❌ W0 |
| TBD | TBD | TBD | AUTH-02 | — | fonoteka getApiArray listener adds organisation fields, must_change_password, preferred_locale; user never imports fonoteka |
unit (import-direction + payload) | go test ./plugins/golem15/... -run TestGetApiArray |
❌ W0 | ⬜ pending |
| TBD | TBD | TBD | AUTH-03 | T-07-xx | mint/list/revoke; MINTABLE_SCOPES allow-list rejects unknown scopes; InvScope 403s out-of-scope; plaintext returned once, sha256 at rest |
unit + integration | go test ./plugins/golem15/fonoteka/... -run TestTokenApi -short |
❌ W0 | ⬜ pending |
| TBD | TBD | TBD | AUTH-04 | T-07-xx | 423 on JWT-authed fonoteka surface while locked; me/locale and change-password reachable; route-table assertion |
integration | go test ./... -run TestMustChangePasswordLock -short |
❌ W0 | ⬜ pending |
| TBD | TBD | TBD | I18N-02 | — | preferred_locale → Accept-Language → app.locale, also while locked |
unit | go test ./surf/... -run TestLocaleFromPrincipal |
❌ W0 | ⬜ pending |
| TBD | TBD | TBD | AUTH-01..04 | — | every new /_user/api/v1, tokens, me/locale fixture replays byte-identical |
parity replay | summer parity:replay --manifest fonoteka.go/parity/manifest.yaml |
✅ harness / ❌ fixtures | ⬜ pending |
Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky. Task IDs are filled in by the planner once PLAN.md files exist.
Wave 0 Requirements
fonoteka.go/plugins/golem15/user/controllers/api_controller_test.go— stubs for AUTH-01summercms.go/bouncer/mint_test.go,refresh_test.go,blacklist_test.go— AUTH-01 refresh/blacklist algorithm isolated from HTTPfonoteka.go/plugins/golem15/fonoteka/controllers/api/token_api_controller_test.go— AUTH-03summercms.go/surf/locale_from_principal_test.go— I18N-02fonoteka.go/parity/fixtures/routes/_user-api-v1-*.yaml— recorded viatideagainst the isolated PHP instance (D-11/D-12)- Framework install: none —
testcontainers-goandtestifyalready present; onlygolang.org/x/cryptois promoted from indirect to direct
Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions |
|---|---|---|---|
| Recording the new parity fixtures | AUTH-01..04 | Needs the isolated PHP instance and a private 0600 vars store; no live JWT in git | Run tide record per D-11 against PHP with the DB-reading seed hook (D-12) for reset/activation codes; commit fixtures, not vars |
PHP $2y$ hash cross-check (Assumption A1) |
AUTH-01 | One-off cross-language confirmation | php -r 'echo password_hash("secret", PASSWORD_BCRYPT);', paste into a Go test that calls bcrypt.CompareHashAndPassword; keep the test afterwards |
| Throttle path confirmation (Assumption A2) | AUTH-01 | Confirms JWTAuth::attempt() reaches Winter's Auth\Manager throttle |
Record one PHP fixture of 6 rapid failed logins and assert the suspended body appears on the 6th |
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