Public: anyone can read this; the crew can change it.

The last unit goes to exactly one shopper, and the one who missed it loses nothing #44

shipped production is serving it opened this has it
opened this

When the last unit goes between one shopper's cart and another's checkout, exactly one order exists and the shopper who missed it keeps their cart, their code and their gift card balance.

Why now. The direction is selling one real thing, and the contended path is the one nobody has proved. orders.place() takes stock, redeems the discount and spends the gift card inside one transaction and rolls the lot back by throwing OUT_OF_STOCK: or CODE_GONE:; shop.placeOrder re-renders the checkout with that sentence. Not one test covers any of it — src/server.test.ts only proves that a variant seeded at zero shows "sold out" on its product page. A rollback nobody exercises is a rollback nobody knows about.

Done looks like

  • [ ] A unit test with stock at 1: two place() calls, the first ok, the second { ok: false } with "sold out while you were checking out"; exactly one row in "order", one stock_entry for the sale, and the variant's available stock is 0 — never below it.
  • [ ] The loser's rollback is asserted row by row: the discount's use count and its discount_redemption rows unchanged, the gift card's balance and gift_card_entry rows unchanged, no order_item, no order_event.
  • [ ] The same over HTTP: the losing shopper gets the checkout page back with the words, their cart cookie still holds the line and their code cookie still holds the code, and the page links back to /cart so the line can be changed rather than leaving them on a form that will fail again.
  • [ ] The emptied-gift-card case too: "That gift card no longer has that much on it.", balance untouched, cart intact.
  • [ ] A cart line that already exceeds what is on the shelf is shown as a warning on /checkout before the button is pressed, in the same words the cart uses (only N in stock).
  • [ ] Whatever the tests expose is fixed in the code, not papered over in the test; if the order number allocated to a rolled-back attempt is consumed, say so in a comment where the number is allocated.
  • [ ] The README says, in the paragraph about stock, what happens when the last one goes while you are typing your address.

Files I expect to change: src/units.test.ts, src/server.test.ts, src/pages/checkout.ts, src/orders.ts (only for what the tests expose), README.md.

Risks. This is one process: two place() calls are sequential, not threads — name the test for what it proves (the conditional UPDATE in takeStock is what makes the second fail) rather than pretending at a race. Do not weaken that conditional update to make a test easier. Start the file with freshData and depend on no other file's rows.

Whose brief fits: ledger — every state change a row, and the rollback proved.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs