From 9fac768d2ab04e85dbb5f91048cad1393423298f Mon Sep 17 00:00:00 2001 From: Riyadh Date: Mon, 27 Apr 2026 12:11:57 +0000 Subject: [PATCH] Add test coverage for the order-unavailable error responses (Task #86) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task #80 introduced a new 409 `order_unavailable` error code returned by: - PATCH /api/orders/:id/confirm-receipt - PATCH /api/orders/:id/status …to distinguish "already_claimed" (lost a race) from "no longer claimable" (target order is cancelled or completed). The integration test suite did not cover these paths. Changes ------- artifacts/api-server/tests/service-orders.test.mjs + Added test "confirm-receipt returns 409 order_unavailable when the order is cancelled or completed": - Owner cancels a pending order → another receiver's confirm-receipt returns 409 with body.error === "order_unavailable" (not "already_claimed"). - Receiver claims, prepares, completes an order → another receiver's confirm-receipt returns 409 "order_unavailable". + Added test "PATCH /orders/:id/status returns 409 order_unavailable when targeting a terminal order": - Completed order: assignee → preparing → 409 "order_unavailable" - Completed order: assignee → completed → 409 "order_unavailable" - Completed order: owner → cancelled → 409 "order_unavailable" - Cancelled order: owner → cancelled → 409 "order_unavailable" Verification ------------ All 11 service-orders integration tests pass (including both new tests): pnpm --filter @workspace/api-server test → 11 passed, 0 failed. No production code changes; tests only. --- .../api-server/tests/service-orders.test.mjs | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/artifacts/api-server/tests/service-orders.test.mjs b/artifacts/api-server/tests/service-orders.test.mjs index aabb202d..fe5a45dc 100644 --- a/artifacts/api-server/tests/service-orders.test.mjs +++ b/artifacts/api-server/tests/service-orders.test.mjs @@ -423,6 +423,128 @@ test("owner can restore (undo) a freshly cancelled order to its prior status; ex assert.match(body.error, /undo_window_expired/); }); +test("confirm-receipt returns 409 order_unavailable when the order is cancelled or completed", async () => { + const owner = await createUser("ownU", "user"); + const recv1 = await createUser("recvU1", "order_receiver"); + const recv2 = await createUser("recvU2", "order_receiver"); + const co = await login(owner.username); + const cr1 = await login(recv1.username); + const cr2 = await login(recv2.username); + + // Case A: order cancelled before any claim → second receiver sees order_unavailable + let res = await call(co, "POST", "/orders", { serviceId }); + assert.equal(res.status, 201); + const oCancelled = await res.json(); + createdOrderIds.push(oCancelled.id); + + res = await call(co, "PATCH", `/orders/${oCancelled.id}/status`, { + status: "cancelled", + }); + assert.equal(res.status, 200); + + res = await call(cr1, "PATCH", `/orders/${oCancelled.id}/confirm-receipt`); + assert.equal(res.status, 409); + let body = await res.json(); + assert.equal( + body.error, + "order_unavailable", + "cancelled order should report order_unavailable, not already_claimed", + ); + + // Case B: order taken all the way to completed → another receiver sees order_unavailable + res = await call(co, "POST", "/orders", { serviceId }); + assert.equal(res.status, 201); + const oCompleted = await res.json(); + createdOrderIds.push(oCompleted.id); + + res = await call(cr1, "PATCH", `/orders/${oCompleted.id}/confirm-receipt`); + assert.equal(res.status, 200); + res = await call(cr1, "PATCH", `/orders/${oCompleted.id}/status`, { + status: "preparing", + }); + assert.equal(res.status, 200); + res = await call(cr1, "PATCH", `/orders/${oCompleted.id}/status`, { + status: "completed", + }); + assert.equal(res.status, 200); + + res = await call(cr2, "PATCH", `/orders/${oCompleted.id}/confirm-receipt`); + assert.equal(res.status, 409); + body = await res.json(); + assert.equal( + body.error, + "order_unavailable", + "completed order should report order_unavailable, not already_claimed", + ); +}); + +test("PATCH /orders/:id/status returns 409 order_unavailable when targeting a terminal order", async () => { + const owner = await createUser("ownT", "user"); + const recv = await createUser("recvT", "order_receiver"); + const co = await login(owner.username); + const cr = await login(recv.username); + + // Build a completed order (assignee = recv) + let res = await call(co, "POST", "/orders", { serviceId }); + assert.equal(res.status, 201); + const oDone = await res.json(); + createdOrderIds.push(oDone.id); + + res = await call(cr, "PATCH", `/orders/${oDone.id}/confirm-receipt`); + assert.equal(res.status, 200); + res = await call(cr, "PATCH", `/orders/${oDone.id}/status`, { + status: "preparing", + }); + assert.equal(res.status, 200); + res = await call(cr, "PATCH", `/orders/${oDone.id}/status`, { + status: "completed", + }); + assert.equal(res.status, 200); + + // Assignee tries preparing on a completed order → 409 order_unavailable + res = await call(cr, "PATCH", `/orders/${oDone.id}/status`, { + status: "preparing", + }); + assert.equal(res.status, 409); + let body = await res.json(); + assert.equal(body.error, "order_unavailable"); + + // Assignee tries completed (again) on a completed order → 409 order_unavailable + res = await call(cr, "PATCH", `/orders/${oDone.id}/status`, { + status: "completed", + }); + assert.equal(res.status, 409); + body = await res.json(); + assert.equal(body.error, "order_unavailable"); + + // Owner tries to cancel a completed order → 409 order_unavailable (non-admin) + res = await call(co, "PATCH", `/orders/${oDone.id}/status`, { + status: "cancelled", + }); + assert.equal(res.status, 409); + body = await res.json(); + assert.equal(body.error, "order_unavailable"); + + // Build a cancelled order (no assignee) + res = await call(co, "POST", "/orders", { serviceId }); + assert.equal(res.status, 201); + const oCxl = await res.json(); + createdOrderIds.push(oCxl.id); + + res = await call(co, "PATCH", `/orders/${oCxl.id}/status`, { + status: "cancelled", + }); + assert.equal(res.status, 200); + + // Owner tries to cancel an already-cancelled order → 409 order_unavailable (non-admin) + res = await call(co, "PATCH", `/orders/${oCxl.id}/status`, { + status: "cancelled", + }); + assert.equal(res.status, 409); + body = await res.json(); + assert.equal(body.error, "order_unavailable"); +}); + test("unauthenticated cannot place order", async () => { const res = await fetch(`${API_BASE}/api/orders`, { method: "POST",