Files
RenewCMS/features/user_response_field_hygiene.qa.md
T

63 lines
3.7 KiB
Markdown

# QA procedure: user_response_field_hygiene
Package: `test` (black-box), extending `test/account_verification_http_test.go`'s
`newAccountVerificationRouter` / `newAccountVerificationTestUserRepo` helpers and
`test/user_test.go`'s `mockUserRepo` / `stubHasher`.
## Setup shared by every case
1. Call `validator.InitValidator()` once.
2. Reuse `newAccountVerificationRouter(userRepo, mailRepo, token)` from
`test/account_verification_http_test.go`, which wires `auth.NewHandler(...)` with fakes over
a fresh `chi.NewRouter()` mirroring `api/router.go`'s `/v1/auth` group (public routes plus the
`jwtauth.Verifier` + `Authenticator`-wrapped `verify` route). No live server, no real SQLite,
no real SMTP.
3. `newAccountVerificationTestUserRepo()` seeds user id 7 with a non-empty `VerificationCode`
(`"hashed:abc123"`) and `VerificationExpiration` one hour out, matching the existing fixture.
## Response-shape check for POST /v1/auth/verify (the fix)
4. Successful verify: `POST /v1/auth/verify` with `{"code":"abc123"}` and a valid jwt cookie for
user id 7. Decode the response body into `map[string]json.RawMessage` (not into `user.User`)
and assert:
- the key set is exactly `{"id", "username", "email", "is_verified", "created_at",
"updated_at", "display_name", "role"}`;
- none of `password`, `password_reset_code`, `verification_code`,
`verification_expiration`, `password_reset_expiration`, `token_version` is present;
- `is_verified` unmarshals to `true`;
- `display_name` unmarshals to `""`;
- `role` unmarshals to `"owner"` (seeded user 7 has `Role: owner`).
5. Rejected verify: same seed, `{"code":"wrong-code"}` → assert status `403` and that the raw
response body bytes, treated as a string, contain none of the substrings `password`,
`verification_code`, `password_reset_code`, `verification_expiration`,
`password_reset_expiration` (the body is plain text from `http.Error`, so this is a substring
check, not a JSON-key check).
## Registration check (its own case, correctly gated)
6. `POST /v1/auth/register` against an **empty** `mockUserRepo` (the registration gate only
allows register while no verified account exists) with
`{"username":"bob","password":"hunter2example12","email":"bob@example.com"}` → assert status
`200` and that the raw response body string contains none of the six sensitive substrings
from step 5.
## Table-driven sweep of the other auth endpoints (regression guard, not the fix)
7. For each of `POST /v1/auth/login`, `/logout`, `/password-reset`, `/password-reset/confirm`,
seed a `mockUserRepo`/`mockMailRepo` pair with a verified user "alice" (`IsVerified: true`,
matching the Scenario Outline's Given, since these endpoints don't depend on the registration
gate), send the request, and assert the raw response body string contains none of the six
sensitive substrings from step 5. This should already pass today since these handlers only
ever write a `{"message": ...}` body; the point of the test is to pin that fact down so a
future change can't quietly regress it.
## Manual verification (not exercised by the Go test)
- The SPA's post-verification screen (`web/src/`) only ever needs `username`/`is_verified` to
render; confirm by hand that no SPA code reads `password`, `verification_code` or
`password_reset_code` off the verify response, since removing them from the wire format could
otherwise silently break a screen that isn't covered by this Go test suite.
- Confirm the public response type lives in the shared package under `api/pkg` (not a change to
`internal/domain/user/userDomain.go`'s json tags), per the project's stated preference to keep
the domain struct's tags intact and shape the public response in the handler layer instead.