Awesome Testing

Markdown document

Raport błędu: ponowione right-to-be-forgotten zwraca 500

Lekcja 22: Weryfikacja i raportowanie pokrycia API

Historical artifacts may name disposable training credentials and environments. Do not reuse credentials, target course systems, or execute archived prompts without authorization.

Bug: repeated right-to-be-forgotten request returns 500 with the deleted user's JWT

Summary

After a client successfully deletes its own account with DELETE /api/v1/users/{username}/right-to-be-forgotten, repeating the same request with the same JWT returns 500 Internal Server Error.

The first request returns the documented 204. The second request should be a handled authentication or missing-user response, never a server error.

Environment

  • API: https://awesome.byst.re
  • Observed: 2026-08-30
  • Authentication: JWT issued to a disposable generated ROLE_CLIENT
  • Reproducibility: 3/3 independent curl attempts, plus one earlier Playwright fixture-teardown observation

Preconditions

Create a unique disposable client and sign in to obtain its JWT. The successful first deletion removes the generated account, so the sequence leaves no user data that requires separate cleanup.

Steps to reproduce

BASE_URL="https://awesome.byst.re"
USERNAME="forget-repeat-$(uuidgen | tr '[:upper:]' '[:lower:]')"
PASSWORD="ValidPassA1!"

curl --silent --show-error \
  --header "Content-Type: application/json" \
  --data "{\"username\":\"$USERNAME\",\"email\":\"$USERNAME@example.com\",\"password\":\"$PASSWORD\",\"firstName\":\"RepeatDelete\",\"lastName\":\"Explorer\"}" \
  "$BASE_URL/api/v1/users/signup"

TOKEN="$(curl --silent --show-error \
  --header "Content-Type: application/json" \
  --data "{\"username\":\"$USERNAME\",\"password\":\"$PASSWORD\"}" \
  "$BASE_URL/api/v1/users/signin" | jq -r '.token')"

curl --include --request DELETE \
  --header "Authorization: Bearer $TOKEN" \
  "$BASE_URL/api/v1/users/$USERNAME/right-to-be-forgotten"

curl --include --request DELETE \
  --header "Authorization: Bearer $TOKEN" \
  "$BASE_URL/api/v1/users/$USERNAME/right-to-be-forgotten"

Expected result

  • First DELETE: 204 No Content.
  • Second DELETE: a handled non-5xx response. 401 Unauthorized is the preferred behavior because the JWT no longer maps to an active principal. If the API intentionally authenticates the orphaned token, the documented missing-user 404 is an acceptable contract decision.

Actual result

  • First DELETE: 204 No Content, empty body, no-store caching.
  • Second DELETE: 500 Internal Server Error.
  • Second response content type: application/json;charset=UTF-8.
  • Second response body:
{"message":"Internal server error"}

Reproduction evidence

A fresh three-user curl run on 2026-08-30 produced the same status sequence on every attempt:

AttemptSignupSigninFirst DELETERepeated DELETE
1201200204500
2201200204500
3201200204500

The L16 Playwright fixture also observed the 500 once during teardown after its self-delete assertion had already received 204. Later focused runs passed after teardown was hardened, but the direct curl sequence demonstrates that the backend defect is deterministic when the request is repeated.

Impact

  • A normal retry of a successful destructive request produces an unexpected 5xx.
  • Clients cannot distinguish an invalidated identity from a server failure.
  • Cleanup and retry logic can report false infrastructure failures after the account was already deleted successfully.
  • Server logs receive avoidable internal-error noise for a handled identity lifecycle case.

Contract comparison

api-docs.json documents 204, 401, 403, and 404 for this operation. It does not document 500.

Likely backend path

The checked backend gives a concrete investigation lead:

  1. JwtTokenProvider.getAuthentication loads the JWT subject through MyUserDetails.loadUserByUsername.
  2. After the first deletion, that lookup throws UsernameNotFoundException.
  3. JwtTokenFilter catches only CustomException specially and maps every other exception to 500 Internal server error.

This is an inference from the checked source and should be confirmed against the deployed revision and server stack trace.

Suggested fix and regression coverage

  1. Map a missing user during JWT authentication to a consistent 401 response, or explicitly choose and document 404 for this operation.
  2. Do not let UsernameNotFoundException fall into the token filter's generic 500 branch.
  3. Add a backend regression test that signs in a disposable client, forgets the account, and repeats the DELETE with the original JWT.
  4. Verify other protected endpoints with a JWT whose user was deleted; they may share the same authentication failure path.

Do not add a Playwright expectation for the 500; the frontend/API regression suite should keep asserting the intended first-delete behavior while the backend fix is pending.