69 lines
5.1 KiB
Markdown
69 lines
5.1 KiB
Markdown
# QA procedure: registration_gate_and_verification_email
|
|
|
|
Package: `test` (black-box), extending `test/user_test.go` (mockUserRepo, stubHasher) and
|
|
`test/mail_test.go` (mockMailRepo, recordedMail, failSwitch).
|
|
|
|
## Setup shared by every case
|
|
1. Call `validator.InitValidator()` once.
|
|
2. Use-case-level checks build:
|
|
`useCases.NewCreateUserUseCase(repo, &stubHasher{}, mailRepo, "https://cms.example.com")`
|
|
where `repo` is a `mockUserRepo` and `mailRepo` is a `mockMailRepo`. Use `mailRepo.fail.failNext("Send")`
|
|
to force the next `Send` call to fail for the rollback cases.
|
|
3. HTTP-level checks wire `mockUserRepo`, `mockMailRepo`,
|
|
`useCases.NewCreateUserUseCase(repo, &stubHasher{}, mailRepo, publicURL)`,
|
|
`useCases.NewListUsersUseCase(repo)`, `useCases.NewGetUserUseCase(repo)`,
|
|
`useCases.NewAuthenticateUserUseCase(repo, ...)` into `auth.NewHandler(...)` (which no longer takes a
|
|
mail use case or a public URL) with a test `jwtauth.New("HS256", []byte("test-secret"), nil)`. Mount
|
|
`POST /v1/auth/register` unprotected on a fresh `chi.NewRouter()` mirroring `api/router.go`. No live
|
|
server, no real SMTP. Default `publicURL` for cases that don't care about its value is
|
|
`"https://cms.example.com"`.
|
|
|
|
## Use-case-level checks (no HTTP)
|
|
4. Repository pre-populated with one verified user (`IsVerified: true`). Call `CreateUser` with a new
|
|
username/email/password. Assert `errors.Is(err, user.ErrRegistrationClosed)`. Assert the repository's
|
|
`Create` was never called (e.g. repo's user count is unchanged, or a `fail.failNext("Create")`
|
|
sentinel that would only trip if reached). Assert `mailRepo.SentEmails` is empty.
|
|
5. Repository pre-populated with two unverified users. Call `CreateUser` with a new username. Assert
|
|
`err == nil`, the returned user has `IsVerified == false`, and `mailRepo.SentEmails` has exactly one
|
|
entry for the new user's email with `Template == "mailValidation"`.
|
|
6. On an empty repository, call `CreateUser`. Assert `err == nil` and
|
|
`abs(result.VerificationExpiration.Sub(time.Now().Add(2*time.Hour))) < 5*time.Second`.
|
|
7. On an empty repository, set `mailRepo.fail.failNext("Send")`. Call `CreateUser` with
|
|
username "alice"/email "alice@example.com". Assert `err != nil` (wrapping the mail repository's
|
|
failure, e.g. `errors.Is(err, errRepositoryFailure)` per the existing `failSwitch` convention). Assert
|
|
`repo.GetByUsername("alice")` returns `user.ErrUserNotFound` (the created row was deleted). Then clear
|
|
the failure and call `CreateUser` again with the same username/email → assert `err == nil`.
|
|
|
|
## HTTP-level checks (fakes, per the shared setup above)
|
|
8. `POST /v1/auth/register` with `{"username":"alice","password":"hunter2example12","email":"alice@example.com"}`
|
|
against an empty `mockUserRepo` → assert status `200`, a `Set-Cookie: jwt=...` header present.
|
|
Assert `mailRepo.SentEmails` has exactly one entry with `Address == "alice@example.com"` and
|
|
`Template == "mailValidation"`.
|
|
9. Inspect that entry's `Data` and assert it carries the raw verification code (compare against the
|
|
`VerificationCode` field on the user returned by `CreateUser`/stored via the mock repo lookup) — not
|
|
the hashed value the repository stores.
|
|
10. Build the handler from a `CreateUserUseCase` constructed with `publicURL = "https://cms.example.com"`.
|
|
Repeat the registration call. Assert the recorded mail data's `Host` field equals
|
|
`"https://cms.example.com"`. Then set the process env var `PUBLIC_URL` to a different value and
|
|
register a second user with the same handler instance; assert the second email's `Host` is still
|
|
`"https://cms.example.com"` (construction-time value, not read again from the environment).
|
|
11. Seed `mockUserRepo` with one user having `IsVerified: true`. `POST /v1/auth/register` with a new
|
|
username/email → assert status `403`, body exactly
|
|
`"You are not allowed to create a user. Log in or reset database."` (the value of the new
|
|
`apperr` constant), `mockUserRepo` still holds only the original user, and `mailRepo.SentEmails` is
|
|
still empty.
|
|
12. On an empty `mockUserRepo`, set `mailRepo.fail.failNext("Send")`. `POST /v1/auth/register` with
|
|
`{"username":"alice","password":"hunter2example12","email":"alice@example.com"}` → assert status
|
|
`500`, no `Set-Cookie: jwt=...` header present, and `mockUserRepo` does not contain a user named
|
|
"alice". Then, without re-arming the failure, `POST /v1/auth/register` again with the same body →
|
|
assert status `200` and a `Set-Cookie: jwt=...` header present (the username and email are free
|
|
again).
|
|
|
|
## Manual verification (not exercised by the Go test)
|
|
- The real SMTP send (`internal/infrastructure/mailer.MailRepository.Send` actually dialing a server) is
|
|
never exercised automatically; verify by hand against a real or sandboxed SMTP server that the
|
|
rendered `mailValidation.html` link matches `PUBLIC_URL` from the deployed `.env` and that clicking it
|
|
reaches the SPA's `/register/validate` route.
|
|
- The SPA's handling of an expired or already-used verification code (any UI/UX around
|
|
`/register/validate?c=...`) is out of scope for this slice and not covered here.
|