Vivek Jalondhara. All writing

Backend

Seven Failure Cases for a Booking API

By Vivek Jalondhara · 5 min read
Published

A practical acceptance checklist for reservation boundaries: permissions, concurrency, retries, cancellation, time zones, and uncertain provider effects.

A booking endpoint can pass its happy-path check while still creating a confusing reservation experience. Failure cases become more useful when they describe an observable outcome: which reservation exists, who can see it, and what the caller should do next.

The following seven cases are an educational acceptance plan connected to the Booking Management System domain. They are not a claim that these bugs exist in that project, and the scenarios below have not been executed against its private implementation. Use synthetic tenants, resources, customers, and provider responses.

1. The caller loses permission

Arrange a user who can book a resource, then revoke that access according to the application’s documented revocation policy. Submit a new reservation after the revocation takes effect. Also try supplying another tenant’s resource identifier with the original caller’s credentials.

Expect the write to fail without exposing protected resource details. Check the database state, not only the status code. OWASP recommends enforcing authorisation at each API endpoint; a calendar page that once displayed the resource is not evidence that the later write remains permitted.

2. Two customers choose the same available slot

Give two callers the same apparently available, canonical capacity-one slot. Pause both requests before the reservation write and release them together. A stale calendar is expected under concurrency; accepting both reservations is the behaviour this case must prevent.

Assert that one active reservation owns the slot and the other caller receives the documented conflict outcome. Test through separate database connections when exercising the real constraint. A sequential test that books the slot and then makes a second request does not cover the same race.

3. The response disappears after commit

Complete the reservation transaction, then simulate losing the HTTP response before the caller receives it. Retry the same logical operation using the same scoped idempotency key and unchanged accepted input. The caller must be able to recover the original reservation identity under the documented replay contract.

Inspect reservation rows and any recorded downstream work. This acceptance case requires durable replay behaviour; it is not satisfied by disabling a button. Specify the key retention window so the test does not accidentally assert unlimited replay guarantees.

4. A retry key is reused for a different request

Repeat an accepted request’s idempotency key while changing the selected slot or customer. Expect an explicit mismatch response instead of silently returning a reservation for the old meaning or creating another reservation. Scope the key to the caller context used by the API.

Define which validated fields form the operation’s meaning before writing this test. Harmless differences in JSON formatting should not create a new intent, but a changed resource should. This case protects the user from an accidental client-side key reuse as well as deliberate misuse.

5. Cancellation races with queued follow-up work

Queue a reminder for a confirmed reservation, cancel it, and then release the reminder worker. Decide what happens if cancellation commits before the worker claims the work, and separately what happens if a provider request has already started. These are different boundaries.

The expected result should reflect the product’s achievable rule. A state check can suppress work that has not been dispatched, but cannot retract an email already accepted by an external service. Verify reservation history and slot release separately from the documented handling of an in-flight reminder.

6. A local timestamp has no unique meaning

Submit a booking time without a required offset, and include a synthetic local time inside a daylight-saving clock change for a supported zone. Do not let the server machine’s default zone decide which instant the customer intended. The API should reject ambiguous input or require the missing disambiguation.

PostgreSQL distinguishes timestamps with and without time-zone handling; timestamptz does not preserve the original named zone. If the booking experience needs that zone for later display or recurrence, model it explicitly. Test the documented input contract rather than assuming a parseable string is a valid reservation time.

7. A provider times out after receiving the request

Simulate a provider accepting a payment-related request while the booking service receives a timeout. AWS’s retry guidance explains why timeouts do not prove that side effects failed. Expect an unresolved or reconciling state when the outcome is genuinely unknown, rather than an invented success or failure.

Use the provider’s supported idempotency and lookup mechanisms before deciding whether to retry. Confirm that a booking identifier remains available to the user while reconciliation proceeds. The test should prove the chosen recovery rule; it cannot prove exactly-once external effects merely by counting requests made by one worker.

Specify the lost-response acceptance case

Illustrative JSON test brief. A harness must inject the failure after the transaction commits; dropping the request before submission exercises a different condition.

Illustrative example · JSON · Simplified for explanation

{
  "case": "reply-lost-after-commit",
  "input": {"resourceId": "room-demo", "slotId": "slot-demo"},
  "idempotencyKey": "booking-intent-demo-1",
  "fault": "drop-response-after-database-commit",
  "retry": "same-scope-key-and-input",
  "expect": {
    "activeReservationCount": 1,
    "reservationIdentity": "same-as-first-commit"
  }
}
How the example fits together
  1. Create isolated synthetic fixtures
  2. Inject the specified boundary failure
  3. Retry or reconcile under the contract
  4. Assert stored state and user-visible outcome

Conceptual flow for the example above.

Tradeoff to keep in mind

This is an acceptance specification, not an executable integration test. Provider effects need their own assertions and reconciliation strategy.

Related project: Booking Management System →

References & further reading

Related posts