import json from copy import deepcopy from typing import Any from unittest import mock from ellipticcurve.privateKey import PrivateKey from app.handlers import SendgridEventHandler from tests.unit.utils import generate_test_sendgrid_signature def test_handler_success( test_handler: SendgridEventHandler, test_request_success: dict[str, Any], s3_client_mock: mock.MagicMock, ) -> None: result = test_handler.handle(test_request_success) s3_client_mock.put_object.assert_not_called() assert result == {"statusCode": 200} def test_handler_validation_error( test_handler: SendgridEventHandler, test_request_validation_fail: dict[str, Any], s3_client_mock: mock.MagicMock, ) -> None: result = test_handler.handle(test_request_validation_fail) s3_client_mock.put_object.assert_called_once() assert result == {"statusCode": 400} def test_handler_auth_error( test_handler: SendgridEventHandler, test_request_success: dict[str, Any], s3_client_mock: mock.MagicMock, ) -> None: test_request_auth_error = deepcopy(test_request_success) test_request_auth_error["headers"]["x-twilio-email-event-webhook-signature"] = ( "wrong_signature" ) result = test_handler.handle(test_request_auth_error) s3_client_mock.put_object.assert_called_once() assert result == {"statusCode": 401} def test_handler_subaccount_fail( test_handler: SendgridEventHandler, test_request_success: dict[str, Any], s3_client_mock: mock.MagicMock, ) -> None: test_request_subaccount_fail = deepcopy(test_request_success) test_request_subaccount_fail["path"] = "wrong_path" result = test_handler.handle(test_request_subaccount_fail) s3_client_mock.put_object.assert_called_once() assert result == {"statusCode": 401} def test_handler_kafka_producer_error( test_handler: SendgridEventHandler, test_request_success: dict[str, Any], s3_client_mock: mock.MagicMock, kafka_producer_mock: mock.MagicMock, ) -> None: kafka_producer_mock.produce.side_effect = Exception("producer error") result = test_handler.handle(test_request_success) s3_client_mock.put_object.assert_called_once() assert result == {"statusCode": 500} def test_handler_fan_reply_forwarding_ignore( test_handler: SendgridEventHandler, test_request_fan_reply_forwarding: dict[str, Any], s3_client_mock: mock.MagicMock, kafka_producer_mock: mock.MagicMock, ) -> None: """Test that fan_reply_forwarding send_type events are ignored and not sent to Kafka.""" result = test_handler.handle(test_request_fan_reply_forwarding) s3_client_mock.put_object.assert_not_called() kafka_producer_mock.produce.assert_not_called() assert result == {"statusCode": 200} def test_handler_empty_events( test_handler: SendgridEventHandler, test_timestamp: int, private_key: PrivateKey, test_subuser: str, kafka_producer_mock: mock.MagicMock, s3_client_mock: mock.MagicMock, ) -> None: """Test that an empty events list returns 200 without producing to Kafka.""" empty_events: list[dict[str, Any]] = [] dumped = json.dumps(empty_events) request = { "headers": { "x-twilio-email-event-webhook-signature": generate_test_sendgrid_signature( str(test_timestamp) + dumped, private_key, ), "x-twilio-email-event-webhook-timestamp": test_timestamp, }, "path": test_subuser, "body": dumped, } result = test_handler.handle(request) kafka_producer_mock.produce.assert_not_called() s3_client_mock.put_object.assert_not_called() assert result == {"statusCode": 200} def test_handler_multiple_events( test_handler: SendgridEventHandler, test_timestamp: int, private_key: PrivateKey, test_subuser: str, kafka_producer_mock: mock.MagicMock, s3_client_mock: mock.MagicMock, ) -> None: """Test that all events in a batch are produced to Kafka.""" events = [ { "event": "processed", "email": "first@example.com", "timestamp": test_timestamp, "sg_event_id": "event1", "sg_message_id": "msg1", "email_id": "id1", "email_type": "CAMPAIGN", "trigger_id": None, "batch_id": "b1", "category": [], "is_test": False, }, { "event": "delivered", "email": "second@example.com", "timestamp": test_timestamp, "sg_event_id": "event2", "sg_message_id": "msg2", "email_id": "id2", "email_type": "AUTOMATED", "trigger_id": "t2", "batch_id": "b2", "category": [], "is_test": False, }, ] dumped = json.dumps(events) request = { "headers": { "x-twilio-email-event-webhook-signature": generate_test_sendgrid_signature( str(test_timestamp) + dumped, private_key, ), "x-twilio-email-event-webhook-timestamp": test_timestamp, }, "path": test_subuser, "body": dumped, } result = test_handler.handle(request) assert kafka_producer_mock.produce.call_count == 2 s3_client_mock.put_object.assert_not_called() assert result == {"statusCode": 200}