Skip to content

Commit a47e701

Browse files
authored
Added test and fix for wrong time format and serialization in events v2 (#11959)
1 parent 6bc1e1e commit a47e701

File tree

5 files changed

+109
-2
lines changed

5 files changed

+109
-2
lines changed

localstack-core/localstack/aws/api/events/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ class EventSource(TypedDict, total=False):
919919

920920

921921
EventSourceList = List[EventSource]
922-
EventTime = datetime
922+
EventTime = datetime | str
923923
HeaderParametersMap = Dict[HeaderKey, HeaderValue]
924924
QueryStringParametersMap = Dict[QueryStringKey, QueryStringValue]
925925
PathParameterList = List[PathParameter]

localstack-core/localstack/services/events/utils.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,14 +181,17 @@ def format_event(
181181
message_id = message.get("original_id", str(long_uid()))
182182
region = message.get("original_region", region)
183183
account_id = message.get("original_account", account_id)
184+
# Format the datetime to ISO-8601 string
185+
event_time = get_event_time(event)
186+
formatted_time = event_time_to_time_string(event_time)
184187

185188
formatted_event = {
186189
"version": "0",
187190
"id": message_id,
188191
"detail-type": event.get("DetailType"),
189192
"source": event.get("Source"),
190193
"account": account_id,
191-
"time": get_event_time(event),
194+
"time": formatted_time,
192195
"region": region,
193196
"resources": event.get("Resources", []),
194197
"detail": json.loads(event.get("Detail", "{}")),

tests/aws/services/events/test_events.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
"""
44

55
import base64
6+
import datetime
67
import json
78
import os
9+
import re
810
import time
911
import uuid
1012

@@ -578,6 +580,68 @@ def test_put_events_with_target_delivery_failure(
578580

579581
assert len(messages) == 0, "No messages should be delivered when queue doesn't exist"
580582

583+
@markers.aws.validated
584+
@pytest.mark.skipif(is_old_provider(), reason="Test specific for v2 provider")
585+
def test_put_events_with_time_field(
586+
self, events_put_rule, create_sqs_events_target, aws_client, snapshot
587+
):
588+
"""Test that EventBridge correctly handles datetime serialization in events."""
589+
rule_name = f"test-rule-{short_uid()}"
590+
queue_url, queue_arn = create_sqs_events_target()
591+
592+
snapshot.add_transformers_list(
593+
[
594+
snapshot.transform.key_value("MD5OfBody", reference_replacement=False),
595+
*snapshot.transform.sqs_api(),
596+
]
597+
)
598+
599+
events_put_rule(
600+
Name=rule_name,
601+
EventPattern=json.dumps(
602+
{"source": ["test-source"], "detail-type": ["test-detail-type"]}
603+
),
604+
)
605+
606+
aws_client.events.put_targets(Rule=rule_name, Targets=[{"Id": "id1", "Arn": queue_arn}])
607+
608+
timestamp = datetime.datetime.utcnow()
609+
event = {
610+
"Source": "test-source",
611+
"DetailType": "test-detail-type",
612+
"Time": timestamp,
613+
"Detail": json.dumps({"message": "test message"}),
614+
}
615+
616+
response = aws_client.events.put_events(Entries=[event])
617+
snapshot.match("put-events", response)
618+
619+
messages = sqs_collect_messages(aws_client, queue_url, expected_events_count=1)
620+
assert len(messages) == 1
621+
snapshot.match("sqs-messages", messages)
622+
623+
received_event = json.loads(messages[0]["Body"])
624+
# Explicit assertions for time field format GH issue: https://github.com/localstack/localstack/issues/11630#issuecomment-2506187279
625+
assert "time" in received_event, "Time field missing in the event"
626+
time_str = received_event["time"]
627+
628+
# Verify ISO8601 format: YYYY-MM-DDThh:mm:ssZ
629+
# Example: "2024-11-28T13:44:36Z"
630+
assert re.match(
631+
r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$", time_str
632+
), f"Time field '{time_str}' does not match ISO8601 format (YYYY-MM-DDThh:mm:ssZ)"
633+
634+
# Verify we can parse it back to datetime
635+
datetime_obj = datetime.datetime.strptime(time_str, "%Y-%m-%dT%H:%M:%SZ")
636+
assert isinstance(
637+
datetime_obj, datetime.datetime
638+
), f"Failed to parse time string '{time_str}' back to datetime object"
639+
640+
time_difference = abs((datetime_obj - timestamp.replace(microsecond=0)).total_seconds())
641+
assert (
642+
time_difference <= 60
643+
), f"Time in event '{time_str}' differs too much from sent time '{timestamp.isoformat()}'"
644+
581645

582646
class TestEventBus:
583647
@markers.aws.validated

tests/aws/services/events/test_events.snapshot.json

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2695,5 +2695,42 @@
26952695
}
26962696
}
26972697
}
2698+
},
2699+
"tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_time_field": {
2700+
"recorded-date": "28-11-2024, 21:25:00",
2701+
"recorded-content": {
2702+
"put-events": {
2703+
"Entries": [
2704+
{
2705+
"EventId": "<uuid:1>"
2706+
}
2707+
],
2708+
"FailedEntryCount": 0,
2709+
"ResponseMetadata": {
2710+
"HTTPHeaders": {},
2711+
"HTTPStatusCode": 200
2712+
}
2713+
},
2714+
"sqs-messages": [
2715+
{
2716+
"MessageId": "<uuid:2>",
2717+
"ReceiptHandle": "<receipt-handle:1>",
2718+
"MD5OfBody": "m-d5-of-body",
2719+
"Body": {
2720+
"version": "0",
2721+
"id": "<uuid:1>",
2722+
"detail-type": "test-detail-type",
2723+
"source": "test-source",
2724+
"account": "111111111111",
2725+
"time": "date",
2726+
"region": "<region>",
2727+
"resources": [],
2728+
"detail": {
2729+
"message": "test message"
2730+
}
2731+
}
2732+
}
2733+
]
2734+
}
26982735
}
26992736
}

tests/aws/services/events/test_events.validation.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@
188188
"tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_target_delivery_failure": {
189189
"last_validated_date": "2024-11-20T17:19:19+00:00"
190190
},
191+
"tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_time_field": {
192+
"last_validated_date": "2024-11-28T21:25:00+00:00"
193+
},
191194
"tests/aws/services/events/test_events.py::TestEvents::test_put_events_without_source": {
192195
"last_validated_date": "2024-06-19T10:40:50+00:00"
193196
}

0 commit comments

Comments
 (0)