A tenant command trace follows token validation, scope, owner rule, versioned SQL and transaction commit.
Spring exercise: trace tenant command replay, denial and rollback
Predict eight outcomes
Start with tenant-east and tenant-west holding R-41 at version 1. Submit accepted with version 1 and key accept-41 using an east write token. Repeat it unchanged. Reuse the key with held, then aim the east token at west, then send only receipt.read scope, then omit tenant_id, then use an unused key with expected version 3. For each request, record the status, east and west states, outbox count and replay count. Do not infer row state from HTTP status alone.
The first request returns version 2 and adds one event. The replay returns version 2 without another event. Changed-command reuse and stale version return 409; cross-tenant and wrong-scope requests return 403; missing claim returns 401. The replay lesson explains the first conflict. The version lesson explains the second.
Prove rollback
Run the test-only service failure after the outbox insert. Inspect all three tables, not just the exception. The source kit has no hosted judge, so the exercise runs with Maven on your machine. Add one new test for a second receipt ID sharing the same key and explain why the fingerprint must include that ID. The current fixture checks that conflict.
Checked source
mvn -q -Dtest=TenantReceiptCommandFlowTest testVerification boundary
TenantReceiptCommandFlowTest runs the accepted, replay, conflict, denial, stale-version and rollback cases in the downloadable Spring source kit. The excerpt is shortened; the kit contains the complete test.
Costs and limits
The trace is backed by a local MockMvc/H2 context with fixed claims and sequential requests. It cannot grade online submissions or prove multi-worker races. The command body is small; a real API must bound request bytes and validate every state transition.
Common Mistakes
- Do not stop at a status-code assertion.
- Do not assume replay repeats a database update.
- Do not confuse the test-only injected failure with a client API option.
Read next
Spring tenant command transaction: keep state, event and replay record together, Spring POST idempotency keys: bind replay to tenant and command, Spring JDBC versioned tenant update: inspect the affected row count, Spring command rollback test: inspect state after an injected failure.
