Files
RenewCMS/features/registration_status_endpoint.qa.md
T

3.8 KiB

QA procedure: registration_status_endpoint

Package: test (black-box), extending test/user_test.go (mockUserRepo, stubHasher) and test/mail_test.go (mockMailRepo), and following the router wiring pattern in test/registration_gate_and_verification_email_http_test.go / test/get_current_user_http_test.go.

Setup shared by every case

  1. Call validator.InitValidator() once.
  2. Build createUserUseCase := useCases.NewCreateUserUseCase(repo, &stubHasher{}, mailRepo, "https://cms.example.com") where repo is a mockUserRepo and mailRepo is a mockMailRepo.
  3. Build the auth handler(s) the same way NewAuthRouter does, but only mount what each case needs: GET /v1/auth/registration unprotected, at the router's top level (no jwtauth.Verifier, no jwtauth.Authenticator, no middleware.NewRateLimiter), on a fresh chi.NewRouter(). No live server, no real SQLite file, no real SMTP.

HTTP-level checks

  1. Empty mockUserRepo. GET /v1/auth/registration with no cookie → assert status 200, Cache-Control header equals "no-store", and the decoded JSON body is exactly {"open": true} (assert there is no other top-level key by decoding into map[string]any and checking len(body) == 1).
  2. mockUserRepo seeded with one user, IsVerified: false. GET /v1/auth/registration → assert status 200 and body exactly {"open": true}.
  3. mockUserRepo seeded with one user, IsVerified: true. GET /v1/auth/registration → assert status 200, Cache-Control: no-store, and body exactly {"open": false}.
  4. Same seed as step 6. Attach a jwt cookie whose value is invalid/expired/garbage — e.g. a plain string like "jwt=not-a-real-token", or a token minted with a different signing key, or one built with token.Encode and then manually expired/truncated — deliberately NOT a cookie freshly minted with token.Encode against the router's own signing key. GET /v1/auth/registration with that cookie attached → assert status 200, body exactly {"open": false} (identical to step 6's cookie-less result), and no Set-Cookie header on the response. This demonstrates the route never parses or validates the cookie at all, since an invalid/expired one produces byte-identical output to no cookie.
  5. mockUserRepo seeded with three users (two IsVerified: true, one IsVerified: false). GET /v1/auth/registration → decode the body into map[string]any, assert its only key is "open", and assert the raw response body (as a string) does not contain any of the seeded usernames or emails, and does not contain a numeric user count anywhere but inside the boolean open value.
  6. Empty mockUserRepo. Send GET /v1/auth/registration 20 times in a row against the same router/repo (a plain loop of httptest.NewRequest + ServeHTTP, no external rate limiter middleware mounted on this route) → assert every response status is 200 and none is 429.

Use-case-level check (no HTTP)

  1. Build a user.Repository fake (mockUserRepo) containing one user with IsVerified: true. Build CreateUserUseCase over that fake, a stubHasher, and a mockMailRepo. Call the new exported method (e.g. IsRegistrationOpen()) and assert it returns false. Then call CreateUser with a new username/email/password on the same use case instance and assert errors.Is(err, user.ErrRegistrationClosed) — proving both entry points consult the same underlying check rather than two independently-maintained copies.

Manual verification (not exercised by the Go test)

  • The SPA actually calling GET /v1/auth/registration before rendering the registration form (rather than always showing it and relying on the 403 from POST /v1/auth/register) is out of scope for this slice's automated test and should be checked by hand against a running dev build.