"""Unit tests for AbacusOutbox schema.""" from datetime import datetime, timezone import pytest from pydantic import ValidationError from src.schemas import AbacusOutbox class TestAbacusOutboxSchema: """Test AbacusOutbox Pydantic model.""" def test_valid_outbox_event(self): """Test creating a valid outbox event.""" event = AbacusOutbox( abacus_outbox_id=1, target_type='order', target_id=100, event_type='order.created', correlation_id='abc-123', details={'foo': 'bar'}, status='pending', retry_count=0, max_retries=3, created_at=datetime(2023, 1, 1, 10, 0, 0, tzinfo=timezone.utc), processed_at=None, ) assert event.abacus_outbox_id == 1 assert event.created_at == datetime(2023, 1, 1, 10, 0, 0, tzinfo=timezone.utc) assert event.processed_at is None def test_created_at_required(self): """Test that created_at is a required field.""" with pytest.raises(ValidationError) as exc_info: AbacusOutbox( abacus_outbox_id=1, target_type='order', target_id=100, event_type='order.created', correlation_id='abc-123', details=None, status='pending', retry_count=0, max_retries=3, # Missing created_at processed_at=None, ) errors = exc_info.value.errors() assert len(errors) == 1 assert errors[0]['loc'] == ('created_at',) assert errors[0]['type'] == 'missing' def test_processed_at_optional(self): """Test that processed_at is optional.""" event = AbacusOutbox( abacus_outbox_id=1, target_type='order', target_id=100, event_type='order.created', correlation_id='abc-123', details=None, status='pending', retry_count=0, max_retries=3, created_at=datetime(2023, 1, 1, 10, 0, 0, tzinfo=timezone.utc), # processed_at not provided ) assert event.processed_at is None def test_processed_at_can_be_set(self): """Test that processed_at can be set to a datetime.""" event = AbacusOutbox( abacus_outbox_id=1, target_type='order', target_id=100, event_type='order.created', correlation_id='abc-123', details=None, status='pending', retry_count=0, max_retries=3, created_at=datetime(2023, 1, 1, 10, 0, 0, tzinfo=timezone.utc), processed_at=datetime(2023, 1, 1, 10, 5, 0, tzinfo=timezone.utc), ) assert event.processed_at == datetime(2023, 1, 1, 10, 5, 0, tzinfo=timezone.utc) def test_timezone_aware_timestamps(self): """Test that timezone-aware datetimes are preserved.""" created = datetime(2023, 6, 15, 14, 30, 0, tzinfo=timezone.utc) processed = datetime(2023, 6, 15, 14, 35, 0, tzinfo=timezone.utc) event = AbacusOutbox( abacus_outbox_id=1, target_type='order', target_id=100, event_type='order.created', correlation_id='abc-123', details=None, status='pending', retry_count=0, max_retries=3, created_at=created, processed_at=processed, ) assert event.created_at.tzinfo == timezone.utc assert event.processed_at.tzinfo == timezone.utc def test_datetime_string_parsing(self): """Test that datetime strings are automatically parsed.""" event = AbacusOutbox( abacus_outbox_id=1, target_type='order', target_id=100, event_type='order.created', correlation_id='abc-123', details=None, status='pending', retry_count=0, max_retries=3, created_at='2023-01-01T10:00:00+00:00', processed_at='2023-01-01T10:05:00+00:00', ) assert isinstance(event.created_at, datetime) assert isinstance(event.processed_at, datetime) assert event.created_at.year == 2023 assert event.created_at.month == 1 def test_extra_fields_ignored(self): """Test that extra fields are ignored (config: extra='ignore').""" event = AbacusOutbox( abacus_outbox_id=1, target_type='order', target_id=100, event_type='order.created', correlation_id='abc-123', details=None, status='pending', retry_count=0, max_retries=3, created_at=datetime(2023, 1, 1, 10, 0, 0, tzinfo=timezone.utc), processed_at=None, unknown_field='should be ignored', # Extra field another_field=123, # Extra field ) # Should not raise error, extra fields ignored assert event.abacus_outbox_id == 1 assert not hasattr(event, 'unknown_field')