Files
summercms/.planning/phases/07-user-plugin-and-authentication/07-VALIDATION.md
2026-09-22 02:43:29 +02:00

85 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 7
slug: user-plugin-and-authentication
status: draft
nyquist_compliant: false
wave_0_complete: false
created: 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 ./... -short` in the repo the task touched
- **After every plan wave:** Run `go test ./... -race` in both modules + `summer parity:replay` against 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 | ⬜ pending |
| 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 | ⬜ pending |
| 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-01
- [ ] `summercms.go/bouncer/mint_test.go`, `refresh_test.go`, `blacklist_test.go` — AUTH-01 refresh/blacklist algorithm isolated from HTTP
- [ ] `fonoteka.go/plugins/golem15/fonoteka/controllers/api/token_api_controller_test.go` — AUTH-03
- [ ] `summercms.go/surf/locale_from_principal_test.go` — I18N-02
- [ ] `fonoteka.go/parity/fixtures/routes/_user-api-v1-*.yaml` — recorded via `tide` against the isolated PHP instance (D-11/D-12)
- [ ] Framework install: none — `testcontainers-go` and `testify` already present; only `golang.org/x/crypto` is 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: true` set in frontmatter
**Approval:** pending