procurement-ingest/tests
Adam Moussa 18e8382ef5
Make Cancelled PO status sticky via atomic write
The PO merge path read status with a get_item (_is_cancelled) and then
wrote with an unconditional update_item. Two defects followed from this:

- Race (Issue A): a cancellation landing between the read and the write
  was silently un-cancelled by a revision carrying a non-cancelled
  po_status — a TOCTOU on a table with concurrent email processing.
- Over-broad strip (Issue B): save_revision dropped po_status whenever
  the PO was Cancelled, so legitimate status updates on non-cancelled
  POs and status-less revisions were affected rather than only the true
  un-cancel transition.

Enforce the invariant server-side instead. "Cancelled" is a sticky,
authoritative status: once set, later new_po/revision emails may enrich
other fields but must never move it to a non-cancelled status. When the
payload carries a non-cancelled po_status, _merge_update issues the
update_item guarded by ConditionExpression "attribute_not_exists(po_status)
OR po_status <> :marker", evaluated atomically at write time, so a
cancellation that lands first always wins. On ConditionalCheckFailedException
the same fields are re-written without po_status/cancelled_at, enriching the
record while Cancelled sticks. Payloads with no status change, or an already
-Cancelled status, take a plain merge — the status is only ever suppressed on
a real un-cancel. This removes the non-atomic get_item from the write path;
_is_cancelled is deleted. Key schema and attribute names are unchanged, so the
cross-stack purchase-orders contract (read-only by seahaven-slack-bot) holds.

Add moto-backed tests covering un-cancel suppression with field enrichment,
status-less merge onto a Cancelled PO, legitimate status updates on
non-cancelled POs, new_po backfill of a Cancelled skeleton, fresh
create/merge, and authoritative save_cancellation.

Refs: #97
2026-07-15 19:57:07 -04:00
..
conftest.py Add pytest suite and enable tests in CI (#95) 2026-07-15 19:06:20 -04:00
requirements.txt Make Cancelled PO status sticky via atomic write 2026-07-15 19:57:07 -04:00
test_pad_zip.py Add pytest suite and enable tests in CI (#95) 2026-07-15 19:06:20 -04:00
test_parse_raw_email.py Add pytest suite and enable tests in CI (#95) 2026-07-15 19:06:20 -04:00
test_po_merge.py Make Cancelled PO status sticky via atomic write 2026-07-15 19:57:07 -04:00