"""Issue #23: comment_id (WorkOrderComments range key) must be unique per source email AND byte-identical across Lambda async retries of the same S3 object, with wall-clock now() kept OUT of the key.""" import handler def _parsed(wo="11144580730", comment_time="2026-04-27T23:51:48"): return { "work_order_id": wo, "email_type": "comment", "commenter": "jdoe", "comment_text": "a comment", "comment_time": comment_time, } def _comment_ids(table): return [item["comment_id"] for item in table.puts] def test_same_object_key_retry_is_idempotent(fake_dynamo): parsed = _parsed() handler.save_event(parsed, "s3://bucket/inbound/obj-a", "inbound/obj-a") handler.save_event(parsed, "s3://bucket/inbound/obj-a", "inbound/obj-a") table = fake_dynamo.tables[handler.COMMENTS_TABLE] ids = _comment_ids(table) # Two put_item calls with an identical key -> one logical row (overwrite). assert ids[0] == ids[1] assert len(table.store) == 1 def test_distinct_emails_same_wo_and_time_are_distinct_rows(fake_dynamo): parsed = _parsed() handler.save_event(parsed, "s3://bucket/inbound/obj-a", "inbound/obj-a") handler.save_event(parsed, "s3://bucket/inbound/obj-b", "inbound/obj-b") table = fake_dynamo.tables[handler.COMMENTS_TABLE] ids = _comment_ids(table) assert ids[0] != ids[1] assert len(table.store) == 2 def test_absent_comment_time_is_stable_across_retries(fake_dynamo): parsed = _parsed(comment_time=None) handler.save_event(parsed, "s3://bucket/inbound/obj-c", "inbound/obj-c") handler.save_event(parsed, "s3://bucket/inbound/obj-c", "inbound/obj-c") table = fake_dynamo.tables[handler.COMMENTS_TABLE] ids = _comment_ids(table) assert ids[0] == ids[1] # 'nocomment' segment, not now() assert "#nocomment#" in ids[0] assert len(table.store) == 1 def test_event_id_independent_of_now(fake_dynamo, monkeypatch): """Freezing / advancing the clock must not change the range key.""" parsed = _parsed(comment_time=None) class FrozenDT: offset = 0 @classmethod def now(cls, tz=None): import datetime as _dt return _dt.datetime(2020, 1, 1, tzinfo=tz) + _dt.timedelta( seconds=cls.offset ) monkeypatch.setattr(handler, "datetime", FrozenDT) handler.save_event(parsed, "s3://b/inbound/obj-d", "inbound/obj-d") FrozenDT.offset = 999999 # simulate a later retry handler.save_event(parsed, "s3://b/inbound/obj-d", "inbound/obj-d") table = fake_dynamo.tables[handler.COMMENTS_TABLE] ids = _comment_ids(table) assert ids[0] == ids[1] # created_at (display) may differ, but the KEY must not. assert len(table.store) == 1 def test_comment_id_format_shape(fake_dynamo): parsed = _parsed() handler.save_event(parsed, "s3://bucket/inbound/obj-e", "inbound/obj-e") table = fake_dynamo.tables[handler.COMMENTS_TABLE] cid = _comment_ids(table)[0] wo, time_part, suffix = cid.split("#") assert wo == "11144580730" assert time_part == "2026-04-27T23:51:48" assert len(suffix) == 12 and all(c in "0123456789abcdef" for c in suffix)