"""Unit tests for source event parsing logic.""" from uuid import UUID from src.logic import source_event class TestParseValidEvent: """Tests for parsing fully valid Kafka events.""" def test_parse_valid_event(self, raw_valid_event): """Should decode Kafka payloads into structured envelopes without invalids.""" valid_messages, invalid_messages = source_event.parse(raw_valid_event) assert len(valid_messages) == 1 assert not invalid_messages envelope = valid_messages[0] assert envelope.id == UUID('0634b63f-5b16-4a0c-a790-64977dc06e5d') assert envelope.crm_id == 'a0OTy00000MQAT1MAP' assert envelope.updated_at.isoformat() == '2025-11-06T14:28:45.604612+00:00' assert len(envelope.updated_subscriptions) == 1 subscription = envelope.updated_subscriptions[0] assert subscription.id == 'ed76811d-86b1-49b8-aa27-07db1e1eab87' assert subscription.crm_id == 'a0VTy00000H4JxKMAV' assert subscription.mailing_list_id == UUID('08abc9fb-c4ad-4363-9d7c-8ddc7e3ca778') assert subscription.mailing_list_crm_id == 'a0S61000000ZhGyEAK' assert subscription.email_campaign_id == UUID('edec7008-8147-46ac-b348-8fb430dd3d3e') assert subscription.old_value is True assert subscription.new_value is False def test_parse_event_with_null_fields(self): """Should parse messages with null emailCampaignId and other optional fields.""" from src.models.preference_change import PreferenceChangeEnvelope # Real-world message format with null emailCampaignId message_dict = { 'id': '6272031f-21aa-45f9-8591-c7561bd3c236', 'crmId': 'a0OTy00000JS2brMAD', 'updatedSubscriptions': [ { 'id': 'ff270466-b147-4aa4-bde4-27d688ecd93d', 'crmId': None, 'mailingListId': None, 'mailingListCrmId': None, 'emailCampaignId': None, 'oldValue': True, 'newValue': False } ], 'updatedAt': '2026-01-14T17:54:35.372774Z' } envelope = PreferenceChangeEnvelope.model_validate(message_dict) assert envelope.id == UUID('6272031f-21aa-45f9-8591-c7561bd3c236') assert envelope.crm_id == 'a0OTy00000JS2brMAD' assert len(envelope.updated_subscriptions) == 1 subscription = envelope.updated_subscriptions[0] assert subscription.id == 'ff270466-b147-4aa4-bde4-27d688ecd93d' assert subscription.crm_id is None assert subscription.mailing_list_id is None assert subscription.mailing_list_crm_id is None assert subscription.email_campaign_id is None assert subscription.old_value is True assert subscription.new_value is False class TestParseInvalidEvent: """Tests for parsing events containing invalid payloads.""" def test_returns_invalid_messages_when_validation_fails(self, raw_invalid_event): """Should capture validation errors as InvalidPreferenceMessage payloads.""" valid_messages, invalid_messages = source_event.parse(raw_invalid_event) assert not valid_messages assert len(invalid_messages) == 1 invalid_message = invalid_messages[0] assert invalid_message.message_key != '' # matches encoded key from fixture assert '"foo1"' in invalid_message.message_value assert invalid_message.error_type == 'ValidationError' assert 'PreferenceChangeEnvelope' in invalid_message.error_message def test_returns_empty_collections_when_event_missing(self): """Should gracefully handle None events.""" valid_messages, invalid_messages = source_event.parse(None) assert valid_messages == [] assert invalid_messages == [] class TestFlattenEvents: """Tests for flatten_events helper.""" def test_deduplicates_and_keeps_latest_timestamp(self, duplicate_subscription_envelopes): """Should retain the most recent subscription update per subscription id.""" result = source_event.flatten_events(duplicate_subscription_envelopes) assert 'sub-1' in result subscription = result['sub-1'] assert subscription.new_value is False assert subscription.updated_at == duplicate_subscription_envelopes[1].updated_at