"""Unit tests for the triggered_sends module.""" import json from unittest.mock import Mock import pytest import requests from src.marketing_cloud_api.triggered_sends import submit_triggered_send from src.marketing_cloud_api.triggered_sends import triggered_send_response_validation from src.models import APIResponse from src.models import Recipient from src.models import TriggeredSend @pytest.fixture def mock_token_manager(mocker): """Mock the TokenManager.get_token method.""" token_manager_mock = mocker.patch('src.marketing_cloud_api.triggered_sends.TokenManager') token_instance = token_manager_mock.return_value token_instance.get_token.return_value = 'fake-access-token' return token_instance @pytest.fixture def mock_requests_post(mocker): """Mock requests.post.""" return mocker.patch('src.marketing_cloud_api.triggered_sends.requests.post') @pytest.fixture def triggered_send_info(): """Sample TriggeredSend object for testing.""" return TriggeredSend( business_unit_id='6399646', triggered_send_definition_key='test-definition-key', recipients=[ Recipient( email_address='test@example.com', profile_uuid='test-profile-uuid', source_event_id='test-event-id', first_name='Test', subscriber_key='test-subscriber-key', last_name='User', postal_code='12345' ) ], ) @pytest.fixture def successful_response(): """Create a successful response object.""" mock_response = Mock(spec=requests.Response) mock_response.status_code = 200 mock_response.json.return_value = { 'requestId': 'test-request-id', 'batchHasErrors': False, 'responses': [ { 'hasErrors': False, 'messages': ['Queued'], 'requestId': 'test-request-id', 'recipientSendId': 'test-recipient-id' } ] } return mock_response @pytest.fixture def successful_response_invalid_status(): """Create a successful response object with invalid status.""" mock_response = Mock(spec=requests.Response) mock_response.status_code = 200 mock_response.json.return_value = { 'requestId': 'test-request-id', 'batchHasErrors': False, 'responses': [ { 'hasErrors': False, 'messages': ['NotQueued'], # Not the expected 'Queued' status 'requestId': 'test-request-id', 'recipientSendId': 'test-recipient-id' } ] } return mock_response @pytest.fixture def successful_response_with_errors(): """Create a successful response object with errors.""" mock_response = Mock(spec=requests.Response) mock_response.status_code = 200 mock_response.json.return_value = { 'requestId': 'test-request-id', 'batchHasErrors': True, 'responses': [ { 'hasErrors': True, 'messages': ['Error processing request'], 'requestId': 'test-request-id', 'recipientSendId': 'test-recipient-id' } ] } return mock_response @pytest.fixture def failed_response(): """Create a failed response object.""" mock_response = Mock(spec=requests.Response) mock_response.status_code = 400 mock_response.raise_for_status.side_effect = requests.HTTPError('400 Client Error') return mock_response class TestSubmitTriggeredSend: """Tests for the submit_triggered_send function.""" @pytest.fixture def is_triggered_send_enabled(self, mocker): """Fixture for is_triggered_send_enabled_for_business_unit_id.""" mock = mocker.patch( 'src.marketing_cloud_api.triggered_sends.utils.is_triggered_send_enabled_for_business_unit_id', return_value=True) yield mock def test_submit_triggered_send_success( self, mock_token_manager, mock_requests_post, triggered_send_info, successful_response, is_triggered_send_enabled): """Test successful submission of a triggered send.""" # Setup mock_requests_post.return_value = successful_response # Execute response, error = submit_triggered_send(triggered_send_info) # Verify assert response == successful_response assert error is None def test_submit_triggered_send_token_error( self, mock_token_manager, triggered_send_info, is_triggered_send_enabled): """Test handling of token retrieval error.""" # Setup mock_token_manager.get_token.side_effect = Exception('Token error') # Execute response, error = submit_triggered_send(triggered_send_info) # Verify assert response is None assert 'Token error' in error def test_submit_triggered_send_request_error( self, mock_token_manager, mock_requests_post, triggered_send_info, is_triggered_send_enabled): """Test handling of request error.""" # Setup mock_requests_post.side_effect = Exception('Request error') # Execute response, error = submit_triggered_send(triggered_send_info) # Verify assert response is None assert 'Request error' in error def test_submit_triggered_send_unsupported_business_unit( self, mock_token_manager, triggered_send_info, is_triggered_send_enabled): """Test handling of unsupported business unit ID.""" # Setup triggered_send_info.business_unit_id = 'unsupported-id' is_triggered_send_enabled.return_value = False # Execute response, error = submit_triggered_send(triggered_send_info) # Verify assert response is None assert 'Unsupported business unit ID' in error def test_submit_triggered_send_supported_business_unit( self, mock_token_manager, mock_requests_post, triggered_send_info, is_triggered_send_enabled, successful_response): """Test handling of supported business unit ID via feature flag.""" mock_requests_post.return_value = successful_response # Execute response, error = submit_triggered_send(triggered_send_info) # Verify assert response == successful_response assert error is None class TestTriggeredSendResponseValidation: """Tests for the triggered_send_response_validation function.""" def test_response_validation_success(self, successful_response): """Test successful validation of a response.""" # Execute response_payload, error = triggered_send_response_validation(successful_response) # Verify assert isinstance(response_payload, APIResponse) assert error is None assert len(response_payload.responses) == 1 assert response_payload.responses[0].has_errors is False assert response_payload.responses[0].messages == ['Queued'] def test_response_validation_http_error(self, failed_response): """Test handling of HTTP error in response validation.""" # Execute response_payload, error = triggered_send_response_validation(failed_response) # Verify assert response_payload is None assert '400 Client Error' in error def test_response_validation_invalid_status(self, successful_response_invalid_status): """Test validation of a response with invalid status.""" # Execute response_payload, error = triggered_send_response_validation(successful_response_invalid_status) # Verify assert isinstance(response_payload, APIResponse) assert 'Response validation failed' in error def test_response_validation_has_errors(self, successful_response_with_errors): """Test validation of a response with errors.""" # Execute response_payload, error = triggered_send_response_validation(successful_response_with_errors) # Verify assert isinstance(response_payload, APIResponse) assert 'Response validation failed' in error def test_response_validation_json_error(self, successful_response): """Test handling of JSON parsing error.""" # Setup successful_response.json.side_effect = json.JSONDecodeError('Invalid JSON', '', 0) # Execute response_payload, error = triggered_send_response_validation(successful_response) # Verify assert response_payload is None assert 'Invalid JSON' in error