Files

56 lines
3.3 KiB
Markdown

# QA procedure: user_roles
Package: `test` (black-box). Reuse `mockUserRepo`, `stubHasher`, `mockMailRepo`, `mockInviteRepo`.
No live server, no real SQLite file, no real SMTP.
## Setup shared by every case
1. Call `validator.InitValidator()` once.
2. Use cases: `NewGetUserUseCase`, `NewCreateInviteUseCase`, `NewListUsersAndInvitesUseCase`,
`NewCreateUserUseCase`, `NewAcceptInviteUseCase` over the fakes.
3. Router mirrors `NewUserRouter`: `jwtauth.Verifier` + `Authenticator` with
`jwtauth.New("HS256", []byte("test-secret"), nil)`, `RequireCurrentToken`, `RequireVerifiedUser`,
`GET /`, and a group with `RequireOwner(getUser)`, `TrustedProxyRealIP(nil)`,
`NewRateLimiter(2, time.Minute)`, `POST /invite`. A second router mirrors `NewAuthRouter` for
`/register`, `/invite/accept` and `/me`.
4. Seed: owner id 1 "marta" (role owner), admin id 2 "jonas" (role admin), both verified.
Cookies carry `user_id` and `token_version`, and never a role.
## Roles at creation
5. Empty repo, `POST /v1/auth/register` -> 200, the stored user has `Role == user.RoleOwner`.
6. Pending invite with a known code (stubHasher), `POST /v1/auth/invite/accept` -> 200, the stored
user has `Role == user.RoleAdmin`, and user 1 is still owner.
7. Use-case level: `CreateUser` returns a user with `RoleOwner`; `AcceptInvite` returns `RoleAdmin`.
## GET /v1/auth/me
8. Table over owner and admin: 200, decode into `map[string]json.RawMessage`, key `role` equals the stored role.
9. Unverified owner -> 200, `role` "owner", `is_verified` false.
10. The key set of the public user is exactly the eight fields, and `POST /v1/auth/verify` returns the same eight.
## GET /v1/user/
11. Seed three users plus a pending invite. 200; user items have exactly the keys id, kind, username,
display_name, email, role, status, created_at; email and role match the seed. The invite item has
exactly id, kind, email, status, created_at. No forbidden key appears anywhere.
12. Cookie of admin 2 -> 200.
## POST /v1/user/invite
13. Owner cookie, valid body -> 201, one invite, one "invitation" mail.
14. Admin cookie, valid body -> 403, body exactly `apperr.OwnerRequiredErrorMessage`, `Content-Type`
is not JSON-decoded (plain text), no invite, no mail.
15. Admin cookie with `{"email":"not-an-email"}` and with `not json` -> 403 both.
16. Rate limit 2: admin sends 3 requests -> 403, 403, 403.
17. Flip user 2's stored role from owner to admin between two requests with the same cookie ->
second request 403.
18. Existing auth table from invitations.qa.md (no cookie 401, unverified 403, stale token 401) is
unchanged; the unverified 403 body remains `apperr.VerificationRequiredErrorMessage`.
## Use case level
19. `RequireOwner` with a user id that no longer exists -> 401 `apperr.UnauthorizedErrorMessage`;
with an unexpected repository error -> 500.
## Manual verification (not exercised by the Go tests)
- Persistence backfill: copy a SQLite file from before this change with 3 users and no role column.
Start the app: the column exists, the lowest id is `owner`, the others `admin`. Restart: nothing
changes. Start against a fresh file: registering creates an owner.
- Layering check by review: `Role` and the sentinel/constants live in `internal/domain/user`; GORM
tags only in `persistence/models`, converted by the mapper.