feat: add Role (owner|admin) to the user domain, persistence model and mapper

This commit is contained in:
fsylvain committed 2026-09-30 00:58:58 +02:00
1 parent 162fea4afb
commit 461e93e3b5
30 files changed
+791 -48

No files matched your search

+11 -7
View File
@@ -14,11 +14,13 @@ plus a `fixedClock` only if the use case takes one. Reuse `mockUserRepo` and `st
3. Handler: `user.NewHandler(updateProfile, createInvite, listUsersAndInvites)` mounted on a fresh
`chi.NewRouter()` mirroring `NewUserRouter`: `jwtauth.Verifier` + `jwtauth.Authenticator` with
`jwtauth.New("HS256", []byte("test-secret"), nil)`, `RequireCurrentToken(getUser)`,
`RequireVerifiedUser(getUser)`, then `POST /invite` and `GET /`. For the rate-limit case
wrap only `POST /invite` in `TrustedProxyRealIP(nil)` + `NewRateLimiter(2, time.Minute)`.
`RequireVerifiedUser(getUser)`, then `POST /invite` and `GET /`. `POST /invite` is also wrapped
in `RequireOwner(getUser)`, placed before the limiter. For the rate-limit case
wrap only `POST /invite` in `RequireOwner(getUser)` + `TrustedProxyRealIP(nil)` +
`NewRateLimiter(2, time.Minute)`.
Requests via `httptest`; jwt cookie built with the `user_id` and `token_version` claims as in
`test/verified_account_required_http_test.go`. No live server, no real SQLite, no real SMTP.
4. Seed users: verified id 7 "marta"; unverified id 8 for the 403 cases. Time is real: build
4. Seed users: verified id 7 "marta" with `Role: owner`; unverified id 8 for the 403 cases. Time is real: build
invites with `ExpiresAt`/`CreatedAt` relative to `time.Now()` and compare with a 5 s tolerance.
## POST /v1/user/invite
@@ -47,7 +49,8 @@ plus a `fixedClock` only if the use case takes one. Reuse `mockUserRepo` and `st
`inviteRepo` empty. Repeat the same request without re-arming -> `201`. With a seeded expired invite and a
failing send -> `500` and no invite for that address remains.
13. Auth table (`POST`): no cookie -> `401`; cookie of unverified user 8 -> `403`; cookie with stale
`token_version` -> `401`. Each asserts no invite and no mail.
`token_version` -> `401`; cookie of a verified admin (seeded with `Role: admin`) -> `403`, plain
text body exactly `apperr.OwnerRequiredErrorMessage`. Each asserts no invite and no mail.
14. Rate limit: limiter of 2 per minute, `RemoteAddr` `203.0.113.20:1234`, three valid POSTs -> `201`,
`201`, `429`; third body is exactly `apperr.TooManyRequestsErrorMessage`, has `Retry-After`, and adds no
invite or mail. `GET /` from the same address afterwards is not `429`.
@@ -55,9 +58,10 @@ plus a `fixedClock` only if the use case takes one. Reuse `mockUserRepo` and `st
## GET /v1/user/
15. Seed per the feature scenario (three users, an expired, a pending and an accepted invite, with the
stated `CreatedAt` values). `200`; decode as `[]map[string]json.RawMessage`. Assert length 5, order
(pending sam, expired p.huber, ines, jonas, marta), key sets exactly as specified for user and invite
items, accepted invite absent, and none of the forbidden keys anywhere. Also assert the raw body string
contains no user email address.
(pending sam, expired p.huber, ines, jonas, marta), key sets exactly as specified: `id`, `kind`,
`username`, `display_name`, `email`, `role`, `status`, `created_at` for user items and `id`, `kind`,
`email`, `status`, `created_at` for invite items, accepted invite absent, and none of the forbidden
keys anywhere. Also assert the raw body string contains each accepted user's own email address.
16. Only user 7 -> array with exactly one `kind:"user"` item, status `accepted`.
17. Invite created 3 days + 1 minute ago -> `status:"expired"`; created 2 days ago -> `pending`;
with `AcceptedAt` set -> not listed.