@@ -826,3 +826,46 @@ def test_api2_idempotence_period(journalist_app):
826826 """
827827
828828 assert journalist_app .config ["SESSION_LIFETIME" ] <= api2 .events .IDEMPOTENCE_PERIOD
829+
830+
831+ def test_api2_event_ordering (journalist_app , journalist_api_token , test_files ):
832+ """
833+ If two `item_deleted` events for the same item arrive out of order, the
834+ numerically later event must observe that the item is already gone by the
835+ time it's processed.
836+ """
837+ with journalist_app .test_client () as app :
838+ index = app .get (
839+ url_for ("api2.index" ),
840+ headers = get_api_headers (journalist_api_token ),
841+ )
842+ assert index .status_code == 200
843+
844+ submission_uuid = test_files ["submissions" ][0 ].uuid
845+ item_version = index .json ["items" ][submission_uuid ]
846+
847+ # Two `item_deleted` events targeting the same item:
848+ e2 = Event (
849+ id = "3419026047977394171" ,
850+ target = ItemTarget (item_uuid = submission_uuid , version = item_version ),
851+ type = EventType .ITEM_DELETED ,
852+ )
853+ e1 = Event (
854+ id = "3419026047977394170" , # client sends as string; server orders as integer
855+ target = ItemTarget (item_uuid = submission_uuid , version = item_version ),
856+ type = EventType .ITEM_DELETED ,
857+ )
858+
859+ # Send them out of order:
860+ resp = app .post (
861+ url_for ("api2.data" ),
862+ json = {"events" : [asdict (e2 ), asdict (e1 )]},
863+ headers = get_api_headers (journalist_api_token ),
864+ )
865+ assert resp .status_code == 200
866+
867+ # Event `1` (sent second, processed first) deletes the item.
868+ assert resp .json ["events" ]["3419026047977394170" ] == [200 , None ]
869+
870+ # Event "2" (sent first, processed second) finds it missing.
871+ assert resp .json ["events" ]["3419026047977394171" ][0 ] == 410
0 commit comments