"""Tests for the sticky-Cancelled merge semantics in the PO email processor. These exercise the atomic conditional write in ``_merge_update`` that makes "Cancelled" a sticky, authoritative status: once a PO is Cancelled, a later new_po or revision may enrich its other fields but can never move it back to a non-cancelled status. The invariant is enforced server-side with a DynamoDB ConditionExpression, so it holds even under concurrent email processing — there is no read-then-write TOCTOU window. A real in-memory purchase-orders table is stood up with moto so the tests exercise the actual DynamoDB conditional-write and ConditionalCheckFailedException fallback path, not a stub. """ import boto3 import pytest from moto import mock_aws @pytest.fixture def po_table(po_persistence): """Stand up a mock purchase-orders table matching the real key schema. Phase 5 moved PO_TABLE onto persistence.py; key the mock table on ``po_persistence.PO_TABLE`` so the fixture and the save_* code under test resolve the same name from one source rather than a duplicated literal. """ with mock_aws(): resource = boto3.resource("dynamodb", region_name="us-east-1") table = resource.create_table( TableName=po_persistence.PO_TABLE, KeySchema=[{"AttributeName": "po_number", "KeyType": "HASH"}], AttributeDefinitions=[ {"AttributeName": "po_number", "AttributeType": "S"}, ], BillingMode="PAY_PER_REQUEST", ) table.wait_until_exists() yield table def _item(table, po_number): return table.get_item(Key={"po_number": po_number}).get("Item") def test_revision_cannot_uncancel_but_enriches(po_handler, po_table): """(a) Revision carrying a live status onto a Cancelled PO keeps it Cancelled while still writing the other revision fields (no resurrection).""" po_table.put_item( Item={ "po_number": "PO-A", "po_status": "Cancelled", "cancelled_at": "2026-07-01T00:00:00", } ) po_handler.save_revision( { "po_number": "PO-A", "po_status": "Issued", "total_amount": 500, "supplier": {"name": "Acme"}, } ) item = _item(po_table, "PO-A") assert item["po_status"] == "Cancelled" # not resurrected assert item["cancelled_at"] == "2026-07-01T00:00:00" # marker preserved assert item["total_amount"] == 500 # other fields still enriched assert item["supplier"] == {"name": "Acme"} def test_revision_without_status_merges_on_cancelled(po_handler, po_table): """(b) Revision with NO po_status onto a Cancelled PO merges cleanly, no exception, status stays Cancelled.""" po_table.put_item(Item={"po_number": "PO-B", "po_status": "Cancelled"}) po_handler.save_revision({"po_number": "PO-B", "total_amount": 250}) item = _item(po_table, "PO-B") assert item["po_status"] == "Cancelled" assert item["total_amount"] == 250 def test_revision_updates_status_on_non_cancelled(po_handler, po_table): """(c) Issue B: a legitimate status update on a NON-cancelled PO is applied, never dropped.""" po_table.put_item(Item={"po_number": "PO-C", "po_status": "Issued"}) po_handler.save_revision( {"po_number": "PO-C", "po_status": "Revised", "total_amount": 999} ) item = _item(po_table, "PO-C") assert item["po_status"] == "Revised" assert item["total_amount"] == 999 def test_new_po_backfills_cancelled_skeleton(po_handler, po_table): """(d) new_po onto a Cancelled skeleton backfills its fields but keeps the Cancelled status and cancellation marker.""" po_table.put_item( Item={ "po_number": "PO-D", "po_status": "Cancelled", "cancelled_at": "2026-07-02T00:00:00", } ) po_handler.save_new_po( { "po_number": "PO-D", "po_status": "Issued", "supplier": {"name": "Beta"}, "total_amount": 1200, } ) item = _item(po_table, "PO-D") assert item["po_status"] == "Cancelled" assert item["cancelled_at"] == "2026-07-02T00:00:00" assert item["supplier"] == {"name": "Beta"} assert item["total_amount"] == 1200 def test_new_po_creates_fresh(po_handler, po_table): """(e) new_po on a fresh PO creates it normally with its status.""" po_handler.save_new_po( {"po_number": "PO-E", "po_status": "Issued", "total_amount": 42} ) item = _item(po_table, "PO-E") assert item["po_status"] == "Issued" assert item["total_amount"] == 42 def test_revision_creates_fresh_merge(po_handler, po_table): """(e) revision on a fresh PO merges/creates normally with its status.""" po_handler.save_revision( {"po_number": "PO-F", "po_status": "Revised", "total_amount": 77} ) item = _item(po_table, "PO-F") assert item["po_status"] == "Revised" assert item["total_amount"] == 77 def test_save_cancellation_is_authoritative(po_handler, po_table): """Cancellation always wins: save_cancellation writes Cancelled over a live status while leaving previously-established data intact.""" po_table.put_item( Item={"po_number": "PO-G", "po_status": "Issued", "total_amount": 300} ) po_handler.save_cancellation( { "po_number": "PO-G", "processed_at": "2026-07-03T00:00:00", "raw_s3_key": "s3://bucket/key", } ) item = _item(po_table, "PO-G") assert item["po_status"] == "Cancelled" assert item["cancelled_at"] == "2026-07-03T00:00:00" assert item["total_amount"] == 300 # pre-existing data retained