Background
The Last Mile team at a large online retailer lets customers change where an order is delivered while it is still at the delivery station, and leave instructions for the driver. A Spring Boot service owns these rules; the dispatch system also uses it to move orders along. Since the latest release, the address-change flow has had problems in testing.
How the system works
Callers
- Customers send
X-Customer-Id. A customer may only see or change their own orders. - The dispatch system sends
X-Role: dispatcher(compared case-insensitively, after trimming). - A missing
X-Customer-Idon a customer endpoint is401with errorSIGN_IN_REQUIRED.
Errors
Every error response is { "error": "<CODE>", "message": "<detail>" }.
| Status | Codes |
|---|---|
| 400 | INVALID_ADDRESS, INVALID_ORDER, TOO_LONG, STATUS_REQUIRED, BAD_REQUEST (unreadable body) |
| 401 | SIGN_IN_REQUIRED |
| 403 | NOT_YOUR_ORDER, DISPATCHERS_ONLY |
| 404 | ORDER_NOT_FOUND |
| 409 | ORDER_EXISTS, TOO_LATE, CHANGE_LIMIT, OUTSIDE_ZONE, SAME_ADDRESS, BAD_TRANSITION |
Addresses
Addresses travel as one line of text: line1 | line2 | city | PIN.
- Exactly four parts separated by
|; each part is trimmed. line1andcityare required;line2may be empty. Each part is at most 80 characters.PINis the 6-digit postal code and doesn't start with0. Its first 3 digits are the delivery zone (560034is zone560).- Anything else is
400 INVALID_ADDRESS. - In JSON responses an address is
{ "line1", "line2", "city", "pin" }.
Order statuses
PLACED → PACKED → OUT_FOR_DELIVERY → DELIVERED, plus CANCELLED. The dispatch system
moves an order one step forward at a time, or cancels it while it is PLACED or PACKED; any
other move (including anything out of CANCELLED) is 409 BAD_TRANSITION.
Endpoints
POST /api/orders (dispatch only, else 403 DISPATCHERS_ONLY): body
{ "id", "customerId", "address" } (address in the one-line format). id and customerId are
trimmed and required (400 INVALID_ORDER); an existing id is 409 ORDER_EXISTS. The order starts
PLACED. Returns 201 with the order.
An order in JSON: { "id", "status", "address", "deliveryNote", "safePlace", "changesUsed", "changeFeesCents" }
(deliveryNote and safePlace start as "").
GET /api/orders/{id}: the customer's order (404 ORDER_NOT_FOUND, or 403 NOT_YOUR_ORDER for
someone else's).
POST /api/orders/{id}/address-changes: body { "address" }. Checked in this order:
- The order exists (
404) and is the caller's (403). - The order is
PLACEDorPACKED; later it is409 TOO_LATE. - At most 2 changes per order; a third is
409 CHANGE_LIMIT. - The address is valid (
400 INVALID_ADDRESS). - The new PIN is in the same delivery zone as the current address (
409 OUTSIDE_ZONE). - The new address differs from the current one (
409 SAME_ADDRESS).
The first change is free; the second costs 4900 cents (₹49), added to the order's changeFeesCents.
Returns 201 with { "orderId", "changeNumber", "address", "feeCents", "totalChangeFeesCents" }.
PATCH /api/orders/{id}/instructions: the customer's driver instructions. Body fields
deliveryNote (at most 200 characters) and safePlace (at most 60) are trimmed; a field that is
left out keeps its value, and a longer value is 400 TOO_LONG. Only those two fields are used.
Allowed until the order is DELIVERED or
CANCELLED (409 TOO_LATE). Returns 200 with the order.
PATCH /api/orders/{id}/status (dispatch only): body { "status" }. A missing status is
400 STATUS_REQUIRED; the move must follow the status rules above. Returns 200 with the order.
Project structure
src/main/java/com/bugnik/addresses/
AddressesApplication.java Spring Boot entry point
model/
Address.java an address and its delivery zone
AddressChange.java the result of an address change
Order.java an order on its way to the customer
OrderStatus.java statuses and the allowed moves
OrderUpdate.java PATCH request body
repository/
OrderRepository.java in-memory orders
service/
AddressChangeService.java address changes, instructions, status moves
DeliveryException.java an error with a status and a code
util/
AddressFormatException.java the address text is malformed
AddressParser.java the one-line address format
web/
OrderController.java the endpoints and their JSON shapes
ApiError.java the error body
ApiExceptionHandler.java errors → status + body
src/test/java/com/bugnik/addresses/
util/AddressParserTest.java address format
service/AddressChangeServiceTest.java service rules
service/Fixtures.java shared test data
web/OrderControllerTest.java endpoints (MockMvc)
Your task
The QA team reports:
- Customers are told their new address is outside their delivery zone when it is just down the road.
- Some customers say their delivery label looks wrong.
- Some orders' tracking pages jumped ahead of where the parcel really was.
Find and fix the defects so that all tests pass.
- Do not modify
src/test/orpom.xml. - Keep your changes minimal. Fix the defects; don't rewrite modules.
- Additional hidden tests check the behaviour described in this README.