Files
RenewCMS/features/cancel_pending_registration.feature

113 lines
6.8 KiB
Gherkin

Feature: A logged-in, unverified user cancels their pending registration
As the maintainer of RenewCMS
I want a logged-in user who has not yet verified their account to be able to delete that
account and clear their session in one call, while a user who is already verified cannot use
this route to delete their own account, and a token that carries no usable user_id is answered
with 401 instead of a panic
So that someone who registered by mistake, or changed their mind before verifying, can free their
username and email immediately (features/unverified_users_and_reregistration.feature), without
exposing a general self-delete for verified accounts
Background:
Given this slice adds DELETE /v1/auth/register, mounted in api/router.go inside the same
protected group NewAuthRouter already applies to POST /v1/auth/verify: jwtauth.Verifier then
jwtauth.Authenticator, so a request with no jwt cookie, an expired jwt, or a jwt with a bad
signature never reaches the handler and is answered 401 by jwtauth.Authenticator itself
And the handler extracts user_id from the already-verified token's claims using the same safe
pattern auth.Handler.Verify already established (features/account_verification.feature):
401 with apperr.UnauthorizedErrorMessage whenever the "user_id" claim is missing, is not a
number, or does not match any user in the repository, never a panic and never trusting the
raw claim value
And a new use case, CancelRegistrationUseCase (internal/infrastructure/useCases/CancelRegistration.go),
is injected into auth.Handler as CancelRegistrationUseCase; it depends only on user.Repository
and exposes CancelRegistration(userId uint32) error
And CancelRegistrationUseCase.CancelRegistration loads the user by id, returns the existing
sentinel user.ErrUserNotFound when no such user exists, refuses with a new sentinel
user.ErrAlreadyVerified when the loaded user's IsVerified is already true, and otherwise calls
user.Repository.Delete(userId), whose GORM implementation removes the row entirely rather than
soft-deleting it (features/unverified_users_and_reregistration.feature), so the same username
and email are immediately free for a new registration
And the handler maps user.ErrUserNotFound to 401 with apperr.UnauthorizedErrorMessage, the same
status and body Verify already uses for an unresolvable user_id, and maps
user.ErrAlreadyVerified to 403 with a new apperr.AlreadyVerifiedErrorMessage constant
And on success the handler clears the "jwt" cookie exactly the way auth.Handler.Logout already
does (Max-Age -1, empty value, Path "/") and responds 200 with {"message": "..."}
And on any error path the handler returns before touching the "jwt" cookie, so an unresolved or
already-verified request leaves the caller's existing session untouched
Scenario: An unverified user cancels their pending registration
Given a user repository containing user id 7, username "alice", email "alice@example.com",
is_verified false
And a valid jwt cookie for user id 7
When the client sends DELETE /v1/auth/register with that cookie
Then the response status is 200
And the response body is {"message": "..."}
And the response clears the "jwt" cookie
And the user repository no longer contains user id 7; Get(7) returns user.ErrUserNotFound
Scenario: Cancelling frees the username and email for a new registration
Given a user repository containing user id 7, username "alice", email "alice@example.com",
is_verified false
And a valid jwt cookie for user id 7
When the client sends DELETE /v1/auth/register with that cookie
Then the response status is 200
When the client sends POST /v1/auth/register with username "alice", password
"hunter2example12", email "alice@example.com"
Then the response status is 200
And a jwt cookie is set
And the newly created user's id is different from 7
Scenario: An already-verified user cannot cancel their account this way
Given a user repository containing user id 7, username "alice", email "alice@example.com",
is_verified true
And a valid jwt cookie for user id 7
When the client sends DELETE /v1/auth/register with that cookie
Then the response status is 403
And the response body is the new apperr.AlreadyVerifiedErrorMessage text
And the response does not clear the "jwt" cookie
And the user repository's record for id 7 is unchanged: is_verified true, the row still exists
Scenario Outline: A token without a valid user_id returns 401 instead of crashing
Given a user repository containing user id 7, username "alice", is_verified false
And <token description>
When the client sends DELETE /v1/auth/register with that cookie
Then the response status is 401
And the response body is the apperr.UnauthorizedErrorMessage text
And the handler does not panic
And the response does not clear the "jwt" cookie
And the user repository's record for id 7 still exists
Examples:
| token description |
| a validly signed jwt cookie whose claims carry no "user_id" key at all |
| a validly signed jwt cookie whose "user_id" claim is the string "seven" |
| a validly signed jwt cookie whose "user_id" claim is the number 999, which does not match any user |
Scenario: A request with no jwt cookie never reaches the handler
Given a user repository containing user id 7, username "alice", is_verified false
And no jwt cookie is sent
When the client sends DELETE /v1/auth/register without a cookie
Then the response status is 401
And the user repository's record for id 7 still exists
Scenario: The use case deletes an unverified user, independently of any handler
Given a user.Repository fake containing user id 3, is_verified false
And a CancelRegistrationUseCase built directly over that fake
When CancelRegistration is called with userId 3
Then the call returns nil
And the fake repository no longer contains user id 3
Scenario: The use case refuses to delete an already-verified user, independently of any handler
Given a user.Repository fake containing user id 3, is_verified true
And a CancelRegistrationUseCase built directly over that fake
When CancelRegistration is called with userId 3
Then the call returns user.ErrAlreadyVerified, asserted with errors.Is
And the fake repository still contains user id 3
Scenario: The use case reports an unknown user, independently of any handler
Given a user.Repository fake containing no user with id 42
And a CancelRegistrationUseCase built directly over that fake
When CancelRegistration is called with userId 42
Then the call returns user.ErrUserNotFound, asserted with errors.Is