diff --git a/sinch/domains/conversation/models/v1/messages/categories/contactinfo/contact_info_message.py b/sinch/domains/conversation/models/v1/messages/categories/contactinfo/contact_info_message.py index e106a3d4..e2483c65 100644 --- a/sinch/domains/conversation/models/v1/messages/categories/contactinfo/contact_info_message.py +++ b/sinch/domains/conversation/models/v1/messages/categories/contactinfo/contact_info_message.py @@ -1,4 +1,5 @@ from typing import Optional +from datetime import date from pydantic import Field, conlist from sinch.domains.conversation.models.v1.messages.internal.base import ( BaseModelConfigurationResponse, @@ -40,6 +41,6 @@ class ContactInfoMessage(BaseModelConfigurationResponse): urls: Optional[conlist(UrlInfo)] = Field( default=None, description="URLs/websites associated with the contact." ) - birthday: Optional[str] = Field( + birthday: Optional[date] = Field( default=None, description="Date of birth in YYYY-MM-DD format." ) diff --git a/sinch/domains/conversation/models/v1/messages/response/types/app_message.py b/sinch/domains/conversation/models/v1/messages/response/types/app_message.py index 396568d5..60564b66 100644 --- a/sinch/domains/conversation/models/v1/messages/response/types/app_message.py +++ b/sinch/domains/conversation/models/v1/messages/response/types/app_message.py @@ -15,10 +15,10 @@ CardAppMessage, CarouselAppMessage, ChoiceAppMessage, + ContactInfoAppMessage, + ListAppMessage, LocationAppMessage, MediaAppMessage, TemplateAppMessage, TextAppMessage, - ListAppMessage, - ContactInfoAppMessage, ] diff --git a/tests/unit/domains/conversation/v1/endpoints/messages/test_get_message_endpoint.py b/tests/unit/domains/conversation/v1/endpoints/messages/test_get_message_endpoint.py index 59a1f3a6..177dab0a 100644 --- a/tests/unit/domains/conversation/v1/endpoints/messages/test_get_message_endpoint.py +++ b/tests/unit/domains/conversation/v1/endpoints/messages/test_get_message_endpoint.py @@ -1,5 +1,4 @@ import pytest -from datetime import datetime, timezone from sinch.core.models.http_response import HTTPResponse from sinch.domains.conversation.api.v1.internal import GetMessageEndpoint from sinch.domains.conversation.models.v1.messages.internal.request import MessageIdRequest @@ -7,6 +6,10 @@ AppMessageResponse, ContactMessageResponse, ) +from tests.unit.domains.conversation.v1.models.response.test_conversation_message_response_model import ( + contact_message_response_data, + app_message_response_data, +) @pytest.fixture @@ -15,138 +18,21 @@ def request_data(): @pytest.fixture -def mock_contact_message_response(): +def mock_contact_message_response(contact_message_response_data): """Mock response for ContactMessageResponse (Union type test).""" return HTTPResponse( status_code=200, - body={ - "id": "CAPY123456789ABCDEFGHIJKLMNOP", - "conversation_id": "CONV987654321ZYXWVUTSRQPONMLK", - "contact_id": "CONTACT456789ABCDEFGHIJKLMNOPQR", - "direction": "UNDEFINED_DIRECTION", - "channel_identity": { - "app_id": "APP123456789ABCDEFGHIJK", - "channel": "WHATSAPP", - "identity": "+46701234567" - }, - "metadata": "test_metadata", - "accept_time": "2026-01-14T20:32:31.147Z", - "injected": True, - "sender_id": "SENDER123456789ABCDEFGHIJK", - "processing_mode": "CONVERSATION", - "contact_message": { - "channel_specific_message": { - "message_type": "nfm_reply", - "message": { - "type": "nfm_reply", - "nfm_reply": { - "name": "flow", - "response_json": "{\"key\": \"value\"}", - "body": "Message body text" - } - } - }, - "reply_to": { - "message_id": "REPLY_TO_MSG123456789ABCDEF" - } - } - }, + body=contact_message_response_data, headers={"Content-Type": "application/json"}, ) @pytest.fixture -def mock_app_message_response(): +def mock_app_message_response(app_message_response_data): """Mock response for AppMessageResponse (Union type test).""" return HTTPResponse( status_code=200, - body={ - "id": "APP123456789ABCDEFGHIJKLMNOP", - "conversation_id": "CONV987654321ZYXWVUTSRQPONMLK", - "contact_id": "CONTACT456789ABCDEFGHIJKLMNOPQR", - "direction": "UNDEFINED_DIRECTION", - "channel_identity": { - "app_id": "APP123456789ABCDEFGHIJK", - "channel": "WHATSAPP", - "identity": "+46701234567" - }, - "metadata": "test_metadata", - "accept_time": "2026-01-14T20:32:31.147Z", - "injected": True, - "sender_id": "SENDER123456789ABCDEFGHIJK", - "processing_mode": "CONVERSATION", - "app_message": { - "card_message": { - "choices": [ - { - "call_message": { - "phone_number": "+15551231234", - "title": "Message text" - }, - "postback_data": None - } - ], - "description": "Card description text", - "height": "UNSPECIFIED_HEIGHT", - "title": "Card title", - "media_message": { - "thumbnail_url": "https://example.com/thumbnail.jpg", - "url": "https://example.com/media.jpg", - "filename_override": "custom_filename.jpg" - }, - "message_properties": { - "whatsapp_header": "WhatsApp header text" - } - }, - "explicit_channel_message": { - "property1": "string", - "property2": "string" - }, - "explicit_channel_omni_message": { - "property1": { - "text_message": { - "text": "string" - } - }, - "property2": { - "text_message": { - "text": "string" - } - } - }, - "channel_specific_message": { - "property1": { - "message_type": "FLOWS", - "message": { - "header": { - "type": "text", - "text": "string" - }, - "body": { - "text": "string" - }, - "footer": { - "text": "string" - }, - "flow_id": "string", - "flow_token": "string", - "flow_mode": "draft", - "flow_cta": "string", - "flow_action": "navigate", - "flow_action_payload": { - "screen": "string", - "data": {} - } - } - } - }, - "agent": { - "display_name": "Agent Name", - "type": "UNKNOWN_AGENT_TYPE", - "picture_url": "https://example.com/agent.jpg" - } - } - }, + body=app_message_response_data, headers={"Content-Type": "application/json"}, ) @@ -191,32 +77,6 @@ def test_handle_response_expects_contact_message_response(endpoint, mock_contact assert isinstance(parsed_response, ContactMessageResponse) assert not isinstance(parsed_response, AppMessageResponse) - assert parsed_response.id == "CAPY123456789ABCDEFGHIJKLMNOP" - assert parsed_response.conversation_id == "CONV987654321ZYXWVUTSRQPONMLK" - assert parsed_response.contact_id == "CONTACT456789ABCDEFGHIJKLMNOPQR" - assert parsed_response.direction == "UNDEFINED_DIRECTION" - assert parsed_response.metadata == "test_metadata" - assert parsed_response.contact_message is not None - assert parsed_response.contact_message.channel_specific_message is not None - assert parsed_response.contact_message.channel_specific_message.message_type == "nfm_reply" - assert parsed_response.contact_message.channel_specific_message.message.type == "nfm_reply" - assert parsed_response.contact_message.channel_specific_message.message.nfm_reply.name == "flow" - assert parsed_response.contact_message.channel_specific_message.message.nfm_reply.response_json == "{\"key\": \"value\"}" - assert parsed_response.contact_message.channel_specific_message.message.nfm_reply.body == "Message body text" - assert parsed_response.contact_message.reply_to is not None - assert parsed_response.contact_message.reply_to.message_id == "REPLY_TO_MSG123456789ABCDEF" - assert parsed_response.channel_identity is not None - assert parsed_response.channel_identity.app_id == "APP123456789ABCDEFGHIJK" - assert parsed_response.channel_identity.channel == "WHATSAPP" - assert parsed_response.channel_identity.identity == "+46701234567" - assert parsed_response.injected is True - assert parsed_response.sender_id == "SENDER123456789ABCDEFGHIJK" - assert parsed_response.processing_mode == "CONVERSATION" - - assert parsed_response.accept_time == datetime( - 2026, 1, 14, 20, 32, 31, 147000, tzinfo=timezone.utc - ) - def test_handle_response_expects_app_message_response(mock_app_message_response): """ @@ -231,40 +91,3 @@ def test_handle_response_expects_app_message_response(mock_app_message_response) # In this test case, we expect an AppMessageResponse assert isinstance(parsed_response, AppMessageResponse) assert not isinstance(parsed_response, ContactMessageResponse) - - assert parsed_response.id == "APP123456789ABCDEFGHIJKLMNOP" - assert parsed_response.conversation_id == "CONV987654321ZYXWVUTSRQPONMLK" - assert parsed_response.contact_id == "CONTACT456789ABCDEFGHIJKLMNOPQR" - assert parsed_response.direction == "UNDEFINED_DIRECTION" - assert parsed_response.metadata == "test_metadata" - assert parsed_response.app_message is not None - assert parsed_response.app_message.card_message is not None - assert parsed_response.app_message.card_message.title == "Card title" - assert parsed_response.app_message.card_message.description == "Card description text" - assert parsed_response.app_message.card_message.height == "UNSPECIFIED_HEIGHT" - assert parsed_response.app_message.card_message.choices is not None - assert len(parsed_response.app_message.card_message.choices) == 1 - assert parsed_response.app_message.card_message.choices[0].call_message is not None - assert parsed_response.app_message.card_message.choices[0].call_message.phone_number == "+15551231234" - assert parsed_response.app_message.card_message.choices[0].call_message.title == "Message text" - assert parsed_response.app_message.card_message.media_message is not None - assert parsed_response.app_message.card_message.media_message.url == "https://example.com/media.jpg" - assert parsed_response.app_message.card_message.media_message.thumbnail_url == "https://example.com/thumbnail.jpg" - assert parsed_response.app_message.card_message.media_message.filename_override == "custom_filename.jpg" - assert parsed_response.app_message.card_message.message_properties is not None - assert parsed_response.app_message.card_message.message_properties.whatsapp_header == "WhatsApp header text" - assert parsed_response.app_message.agent is not None - assert parsed_response.app_message.agent.display_name == "Agent Name" - assert parsed_response.app_message.agent.type == "UNKNOWN_AGENT_TYPE" - assert parsed_response.app_message.agent.picture_url == "https://example.com/agent.jpg" - assert parsed_response.channel_identity is not None - assert parsed_response.channel_identity.app_id == "APP123456789ABCDEFGHIJK" - assert parsed_response.channel_identity.channel == "WHATSAPP" - assert parsed_response.channel_identity.identity == "+46701234567" - assert parsed_response.injected is True - assert parsed_response.sender_id == "SENDER123456789ABCDEFGHIJK" - assert parsed_response.processing_mode == "CONVERSATION" - - assert parsed_response.accept_time == datetime( - 2026, 1, 14, 20, 32, 31, 147000, tzinfo=timezone.utc - ) diff --git a/tests/unit/domains/conversation/v1/endpoints/messages/test_update_message_endpoint.py b/tests/unit/domains/conversation/v1/endpoints/messages/test_update_message_endpoint.py index 9f64d833..8a3ccd29 100644 --- a/tests/unit/domains/conversation/v1/endpoints/messages/test_update_message_endpoint.py +++ b/tests/unit/domains/conversation/v1/endpoints/messages/test_update_message_endpoint.py @@ -1,6 +1,5 @@ import json import pytest -from datetime import datetime, timezone from sinch.core.models.http_response import HTTPResponse from sinch.domains.conversation.api.v1.internal import UpdateMessageMetadataEndpoint from sinch.domains.conversation.models.v1.messages.internal.request import UpdateMessageMetadataRequest @@ -8,149 +7,36 @@ AppMessageResponse, ContactMessageResponse, ) +from tests.unit.domains.conversation.v1.models.response.test_conversation_message_response_model import ( + contact_message_response_data, + app_message_response_data, +) @pytest.fixture def request_data(): return UpdateMessageMetadataRequest( - message_id="UPDATE123456789ABCDEFGHIJKLMNOP", - metadata="updated_metadata_value", + message_id="CAPY123456789ABCDEFGHIJKLMNOP", + metadata="test_metadata", ) @pytest.fixture -def mock_contact_message_response(): +def mock_contact_message_response(contact_message_response_data): """Mock response for ContactMessageResponse (Union type test).""" return HTTPResponse( status_code=200, - body={ - "id": "UPDATE123456789ABCDEFGHIJKLMNOP", - "conversation_id": "UPDATE_CONV987654321ZYXWVUTSRQP", - "contact_id": "UPDATE_CONTACT456789ABCDEFGHIJK", - "direction": "TO_CONTACT", - "channel_identity": { - "app_id": "APP123456789ABCDEFGHIJK", - "channel": "WHATSAPP", - "identity": "+46701234567" - }, - "metadata": "updated_metadata_value", - "accept_time": "2026-01-15T17:19:12.000Z", - "injected": True, - "sender_id": "SENDER123456789ABCDEFGHIJK", - "processing_mode": "CONVERSATION", - "contact_message": { - "channel_specific_message": { - "message_type": "nfm_reply", - "message": { - "type": "nfm_reply", - "nfm_reply": { - "name": "flow", - "response_json": "{\"key\": \"value\"}", - "body": "Updated message content" - } - } - }, - "reply_to": { - "message_id": "REPLY_TO_MSG123456789ABCDEF" - } - } - }, + body=contact_message_response_data, headers={"Content-Type": "application/json"}, ) @pytest.fixture -def mock_app_message_response(): +def mock_app_message_response(app_message_response_data): """Mock response for AppMessageResponse (Union type test).""" return HTTPResponse( status_code=200, - body={ - "id": "UPDATE_APP123456789ABCDEFGHIJK", - "conversation_id": "UPDATE_CONV987654321ZYXWVUTSRQP", - "contact_id": "UPDATE_CONTACT456789ABCDEFGHIJK", - "direction": "TO_CONTACT", - "channel_identity": { - "app_id": "APP123456789ABCDEFGHIJK", - "channel": "WHATSAPP", - "identity": "+46701234567" - }, - "metadata": "updated_metadata_value", - "accept_time": "2026-01-15T17:19:12.000Z", - "injected": True, - "sender_id": "SENDER123456789ABCDEFGHIJK", - "processing_mode": "CONVERSATION", - "app_message": { - "card_message": { - "choices": [ - { - "call_message": { - "phone_number": "+15551231234", - "title": "Message text" - }, - "postback_data": None - } - ], - "description": "Card description text", - "height": "UNSPECIFIED_HEIGHT", - "title": "Card title", - "media_message": { - "thumbnail_url": "https://update.example.com/thumb.jpg", - "url": "https://update.example.com/image.jpg", - "filename_override": "updated_image.jpg" - }, - "message_properties": { - "whatsapp_header": "WhatsApp header text" - } - }, - "explicit_channel_message": { - "property1": "string", - "property2": "string" - }, - "explicit_channel_omni_message": { - "property1": { - "text_message": { - "text": "string" - } - }, - "property2": { - "text_message": { - "text": "string" - } - } - }, - "channel_specific_message": { - "property1": { - "message_type": "FLOWS", - "message": { - "header": { - "type": "text", - "text": "string" - }, - "body": { - "text": "string" - }, - "footer": { - "text": "string" - }, - "flow_id": "string", - "flow_token": "string", - "flow_mode": "draft", - "flow_cta": "string", - "flow_action": "navigate", - "flow_action_payload": { - "screen": "string", - "data": {} - } - } - } - }, - "agent": { - "display_name": "Updated Agent", - "type": "HUMAN_AGENT", - "picture_url": "https://update.example.com/agent_photo.jpg" - } - } - }, + body=app_message_response_data, headers={"Content-Type": "application/json"}, ) @@ -164,7 +50,7 @@ def test_build_url_expects_correct_url(endpoint, mock_sinch_client_conversation) """Test that the URL is built correctly.""" assert ( endpoint.build_url(mock_sinch_client_conversation) - == "https://us.conversation.api.sinch.com/v1/projects/test_project_id/messages/UPDATE123456789ABCDEFGHIJKLMNOP" + == "https://us.conversation.api.sinch.com/v1/projects/test_project_id/messages/CAPY123456789ABCDEFGHIJKLMNOP" ) @@ -193,7 +79,7 @@ def test_request_body_expects_excludes_message_id_and_query_params(request_data) assert "messages_source" not in body assert "message_id" not in body assert "metadata" in body - assert body["metadata"] == "updated_metadata_value" + assert body["metadata"] == "test_metadata" def test_handle_response_expects_contact_message_mapping(endpoint, mock_contact_message_response): @@ -204,32 +90,9 @@ def test_handle_response_expects_contact_message_mapping(endpoint, mock_contact_ assert isinstance(parsed_response, ContactMessageResponse) assert not isinstance(parsed_response, AppMessageResponse) - - assert parsed_response.id == "UPDATE123456789ABCDEFGHIJKLMNOP" - assert parsed_response.conversation_id == "UPDATE_CONV987654321ZYXWVUTSRQP" - assert parsed_response.contact_id == "UPDATE_CONTACT456789ABCDEFGHIJK" - assert parsed_response.direction == "TO_CONTACT" - assert parsed_response.metadata == "updated_metadata_value" - assert parsed_response.contact_message is not None - assert parsed_response.contact_message.channel_specific_message is not None - assert parsed_response.contact_message.channel_specific_message.message_type == "nfm_reply" - assert parsed_response.contact_message.channel_specific_message.message.type == "nfm_reply" - assert parsed_response.contact_message.channel_specific_message.message.nfm_reply.name == "flow" - assert parsed_response.contact_message.channel_specific_message.message.nfm_reply.response_json == "{\"key\": \"value\"}" - assert parsed_response.contact_message.channel_specific_message.message.nfm_reply.body == "Updated message content" - assert parsed_response.contact_message.reply_to is not None - assert parsed_response.contact_message.reply_to.message_id == "REPLY_TO_MSG123456789ABCDEF" - assert parsed_response.channel_identity is not None - assert parsed_response.channel_identity.app_id == "APP123456789ABCDEFGHIJK" - assert parsed_response.channel_identity.channel == "WHATSAPP" - assert parsed_response.channel_identity.identity == "+46701234567" - assert parsed_response.injected is True - assert parsed_response.sender_id == "SENDER123456789ABCDEFGHIJK" - assert parsed_response.processing_mode == "CONVERSATION" - assert parsed_response.accept_time == datetime( - 2026, 1, 15, 17, 19, 12, 0, tzinfo=timezone.utc - ) + assert parsed_response.id == "CAPY123456789ABCDEFGHIJKLMNOP" + assert parsed_response.metadata == "test_metadata" def test_handle_response_expects_app_message_mapping(mock_app_message_response): @@ -237,8 +100,8 @@ def test_handle_response_expects_app_message_mapping(mock_app_message_response): Test that the response handles AppMessageResponse correctly (Union type test). """ request_data = UpdateMessageMetadataRequest( - message_id="UPDATE_APP123456789ABCDEFGHIJK", - metadata="updated_metadata_value", + message_id="APP123456789ABCDEFGHIJKLMNOP", + metadata="test_metadata", ) endpoint = UpdateMessageMetadataEndpoint("test_project_id", request_data) @@ -246,40 +109,6 @@ def test_handle_response_expects_app_message_mapping(mock_app_message_response): assert isinstance(parsed_response, AppMessageResponse) assert not isinstance(parsed_response, ContactMessageResponse) - - assert parsed_response.id == "UPDATE_APP123456789ABCDEFGHIJK" - assert parsed_response.conversation_id == "UPDATE_CONV987654321ZYXWVUTSRQP" - assert parsed_response.contact_id == "UPDATE_CONTACT456789ABCDEFGHIJK" - assert parsed_response.direction == "TO_CONTACT" - assert parsed_response.metadata == "updated_metadata_value" - assert parsed_response.app_message is not None - assert parsed_response.app_message.card_message is not None - assert parsed_response.app_message.card_message.title == "Card title" - assert parsed_response.app_message.card_message.description == "Card description text" - assert parsed_response.app_message.card_message.height == "UNSPECIFIED_HEIGHT" - assert parsed_response.app_message.card_message.choices is not None - assert len(parsed_response.app_message.card_message.choices) == 1 - assert parsed_response.app_message.card_message.choices[0].call_message is not None - assert parsed_response.app_message.card_message.choices[0].call_message.phone_number == "+15551231234" - assert parsed_response.app_message.card_message.choices[0].call_message.title == "Message text" - assert parsed_response.app_message.card_message.media_message is not None - assert parsed_response.app_message.card_message.media_message.url == "https://update.example.com/image.jpg" - assert parsed_response.app_message.card_message.media_message.thumbnail_url == "https://update.example.com/thumb.jpg" - assert parsed_response.app_message.card_message.media_message.filename_override == "updated_image.jpg" - assert parsed_response.app_message.card_message.message_properties is not None - assert parsed_response.app_message.card_message.message_properties.whatsapp_header == "WhatsApp header text" - assert parsed_response.app_message.agent is not None - assert parsed_response.app_message.agent.display_name == "Updated Agent" - assert parsed_response.app_message.agent.type == "HUMAN_AGENT" - assert parsed_response.app_message.agent.picture_url == "https://update.example.com/agent_photo.jpg" - assert parsed_response.channel_identity is not None - assert parsed_response.channel_identity.app_id == "APP123456789ABCDEFGHIJK" - assert parsed_response.channel_identity.channel == "WHATSAPP" - assert parsed_response.channel_identity.identity == "+46701234567" - assert parsed_response.injected is True - assert parsed_response.sender_id == "SENDER123456789ABCDEFGHIJK" - assert parsed_response.processing_mode == "CONVERSATION" - assert parsed_response.accept_time == datetime( - 2026, 1, 15, 17, 19, 12, 0, tzinfo=timezone.utc - ) + assert parsed_response.id == "APP123456789ABCDEFGHIJKLMNOP" + assert parsed_response.metadata == "test_metadata" diff --git a/tests/unit/domains/conversation/v1/models/response/app_message/test_card_app_message.py b/tests/unit/domains/conversation/v1/models/response/app_message/test_card_app_message.py new file mode 100644 index 00000000..56660608 --- /dev/null +++ b/tests/unit/domains/conversation/v1/models/response/app_message/test_card_app_message.py @@ -0,0 +1,148 @@ +import pytest +from sinch.domains.conversation.models.v1.messages.categories.app.app_message import ( + CardAppMessage, +) + + +@pytest.fixture +def card_app_message_data(): + """Test data for CardAppMessage from Java SDK.""" + return { + "card_message": { + "title": "title value", + "description": "description value", + "media_message": { + "url": "an url value", + "thumbnail_url": "another url", + "filename_override": "filename override value" + }, + "height": "MEDIUM", + "choices": [ + { + "text_message": { + "text": "This is a text message." + }, + "postback_data": "postback_data text" + }, + { + "call_message": { + "title": "title value", + "phone_number": "phone number value" + }, + "postback_data": "postback_data call" + }, + { + "location_message": { + "coordinates": { + "latitude": 47.6279809, + "longitude": -2.8229159 + }, + "title": "title value", + "label": "label value" + }, + "postback_data": "postback_data location" + }, + { + "url_message": { + "title": "title value", + "url": "an url value" + }, + "postback_data": "postback_data url" + }, + { + "calendar_message": { + "title": "Calendar Message Example", + "event_start": "2023-10-01T10:00:00Z", + "event_end": "2023-10-01T11:00:00Z", + "event_title": "Team Meeting", + "event_description": "Monthly team sync-up", + "fallback_url": "https://calendar.example.com/event/12345" + }, + "postback_data": "postback calendar_message data value" + }, + { + "share_location_message": { + "title": "Share Location Example", + "fallback_url": "https://maps.example.com/?q=37.7749,-122.4194" + }, + "postback_data": "postback share_location_message data value" + } + ] + }, + "explicit_channel_message": { + "KAKAOTALK": "foo value" + }, + "explicit_channel_omni_message": { + "KAKAOTALK": { + "choice_message": { + "text_message": { + "text": "This is a text message." + }, + "choices": [ + { + "call_message": { + "title": "title value", + "phone_number": "phone number value" + }, + "postback_data": "postback call_message data value" + } + ] + } + } + }, + "channel_specific_message": { + "MESSENGER": { + "message_type": "FLOWS", + "message": { + "flow_id": "1", + "flow_cta": "Book!", + "header": { + "type": "text", + "text": "text header value" + }, + "body": { + "text": "Flow message body" + }, + "footer": { + "text": "Flow message footer" + }, + "flow_token": "AQAAAAACS5FpgQ_cAAAAAD0QI3s.", + "flow_mode": "draft", + "flow_action": "navigate", + "flow_action_payload": { + "screen": "", + "data": { + "product_name": "name", + "product_description": "description", + "product_price": 100 + } + } + } + } + }, + "agent": { + "display_name": "display_name value", + "type": "BOT", + "picture_url": "picture_url value" + } + } + + +def test_parsing_card_app_message_expects_correct_fields(card_app_message_data): + """Test that CardAppMessage is parsed correctly with all fields.""" + parsed_response = CardAppMessage.model_validate(card_app_message_data) + + assert isinstance(parsed_response, CardAppMessage) + assert parsed_response.card_message is not None + assert parsed_response.card_message.title == "title value" + assert parsed_response.card_message.description == "description value" + assert parsed_response.card_message.height == "MEDIUM" + assert parsed_response.card_message.media_message is not None + assert parsed_response.card_message.media_message.url == "an url value" + assert parsed_response.card_message.media_message.thumbnail_url == "another url" + assert parsed_response.card_message.media_message.filename_override == "filename override value" + assert len(parsed_response.card_message.choices) == 6 + assert parsed_response.channel_specific_message is not None + assert parsed_response.explicit_channel_omni_message is not None + assert parsed_response.explicit_channel_message is not None + assert parsed_response.agent is not None diff --git a/tests/unit/domains/conversation/v1/models/response/app_message/test_carousel_app_message.py b/tests/unit/domains/conversation/v1/models/response/app_message/test_carousel_app_message.py new file mode 100644 index 00000000..9fc37981 --- /dev/null +++ b/tests/unit/domains/conversation/v1/models/response/app_message/test_carousel_app_message.py @@ -0,0 +1,118 @@ +import pytest +from sinch.domains.conversation.models.v1.messages.categories.app.app_message import ( + CarouselAppMessage, +) + + +@pytest.fixture +def carousel_app_message_data(): + """Test data for CarouselAppMessage from Java SDK.""" + return { + "carousel_message": { + "cards": [ + { + "title": "title value", + "description": "description value", + "media_message": { + "url": "an url value", + "thumbnail_url": "another url", + "filename_override": "filename override value" + }, + "height": "MEDIUM", + "choices": [ + { + "text_message": { + "text": "This is a text message." + }, + "postback_data": "postback_data text" + } + ] + } + ], + "choices": [ + { + "call_message": { + "title": "title value", + "phone_number": "phone number value" + }, + "postback_data": "postback call_message data value" + } + ] + }, + "explicit_channel_message": { + "KAKAOTALK": "foo value" + }, + "explicit_channel_omni_message": { + "KAKAOTALK": { + "choice_message": { + "text_message": { + "text": "This is a text message." + }, + "choices": [ + { + "call_message": { + "title": "title value", + "phone_number": "phone number value" + }, + "postback_data": "postback call_message data value" + } + ] + } + } + }, + "channel_specific_message": { + "MESSENGER": { + "message_type": "FLOWS", + "message": { + "flow_id": "1", + "flow_cta": "Book!", + "header": { + "type": "text", + "text": "text header value" + }, + "body": { + "text": "Flow message body" + }, + "footer": { + "text": "Flow message footer" + }, + "flow_token": "AQAAAAACS5FpgQ_cAAAAAD0QI3s.", + "flow_mode": "draft", + "flow_action": "navigate", + "flow_action_payload": { + "screen": "", + "data": { + "product_name": "name", + "product_description": "description", + "product_price": 100 + } + } + } + } + }, + "agent": { + "display_name": "display_name value", + "type": "BOT", + "picture_url": "picture_url value" + } + } + + +def test_parsing_carousel_app_message_expects_correct_fields(carousel_app_message_data): + """Test that CarouselAppMessage is parsed correctly with all fields.""" + parsed_response = CarouselAppMessage.model_validate(carousel_app_message_data) + + assert isinstance(parsed_response, CarouselAppMessage) + assert parsed_response.carousel_message is not None + assert len(parsed_response.carousel_message.cards) == 1 + assert parsed_response.carousel_message.cards[0].title == "title value" + assert parsed_response.carousel_message.cards[0].description == "description value" + assert parsed_response.carousel_message.cards[0].height == "MEDIUM" + assert len(parsed_response.carousel_message.choices) == 1 + assert parsed_response.carousel_message.choices[0].call_message.title == "title value" + assert parsed_response.carousel_message.choices[0].call_message.phone_number == "phone number value" + assert parsed_response.carousel_message.choices[0].postback_data == "postback call_message data value" + assert parsed_response.explicit_channel_message is not None + assert parsed_response.explicit_channel_omni_message is not None + assert parsed_response.channel_specific_message is not None + assert parsed_response.agent is not None diff --git a/tests/unit/domains/conversation/v1/models/response/app_message/test_choice_app_message.py b/tests/unit/domains/conversation/v1/models/response/app_message/test_choice_app_message.py new file mode 100644 index 00000000..4b88081a --- /dev/null +++ b/tests/unit/domains/conversation/v1/models/response/app_message/test_choice_app_message.py @@ -0,0 +1,141 @@ +import pytest +from sinch.domains.conversation.models.v1.messages.categories.app.app_message import ( + ChoiceAppMessage, +) + + +@pytest.fixture +def choice_app_message_data(): + """Test data for ChoiceAppMessage from Java SDK.""" + return { + "choice_message": { + "text_message": { + "text": "This is a text message." + }, + "choices": [ + { + "call_message": { + "title": "title value", + "phone_number": "phone number value" + }, + "postback_data": "postback call_message data value" + }, + { + "location_message": { + "coordinates": { + "latitude": 47.6279809, + "longitude": -2.8229159 + }, + "title": "title value", + "label": "label value" + }, + "postback_data": "postback location_message data value" + }, + { + "text_message": { + "text": "This is a text message." + }, + "postback_data": "postback text_message data value" + }, + { + "url_message": { + "title": "title value", + "url": "an url value" + }, + "postback_data": "postback url_message data value" + }, + { + "calendar_message": { + "title": "Calendar Message Example", + "event_start": "2023-10-01T10:00:00Z", + "event_end": "2023-10-01T11:00:00Z", + "event_title": "Team Meeting", + "event_description": "Monthly team sync-up", + "fallback_url": "https://calendar.example.com/event/12345" + }, + "postback_data": "postback calendar_message data value" + }, + { + "share_location_message": { + "title": "Share Location Example", + "fallback_url": "https://maps.example.com/?q=37.7749,-122.4194" + }, + "postback_data": "postback share_location_message data value" + } + ] + }, + "explicit_channel_message": { + "KAKAOTALK": "foo value" + }, + "explicit_channel_omni_message": { + "KAKAOTALK": { + "choice_message": { + "text_message": { + "text": "This is a text message." + }, + "choices": [ + { + "call_message": { + "title": "title value", + "phone_number": "phone number value" + }, + "postback_data": "postback call_message data value" + } + ] + } + } + }, + "channel_specific_message": { + "MESSENGER": { + "message_type": "FLOWS", + "message": { + "flow_id": "1", + "flow_cta": "Book!", + "header": { + "type": "text", + "text": "text header value" + }, + "body": { + "text": "Flow message body" + }, + "footer": { + "text": "Flow message footer" + }, + "flow_token": "AQAAAAACS5FpgQ_cAAAAAD0QI3s.", + "flow_mode": "draft", + "flow_action": "navigate", + "flow_action_payload": { + "screen": "", + "data": { + "product_name": "name", + "product_description": "description", + "product_price": 100 + } + } + } + } + }, + "agent": { + "display_name": "display_name value", + "type": "BOT", + "picture_url": "picture_url value" + } + } + + +def test_parsing_choice_app_message_expects_correct_fields(choice_app_message_data): + """Test that ChoiceAppMessage is parsed correctly with all fields.""" + parsed_response = ChoiceAppMessage.model_validate(choice_app_message_data) + + assert isinstance(parsed_response, ChoiceAppMessage) + assert parsed_response.choice_message is not None + assert parsed_response.choice_message.text_message is not None + assert parsed_response.choice_message.text_message.text == "This is a text message." + assert len(parsed_response.choice_message.choices) == 6 + assert parsed_response.choice_message.choices[0].call_message.title == "title value" + assert parsed_response.choice_message.choices[0].call_message.phone_number == "phone number value" + assert parsed_response.choice_message.choices[0].postback_data == "postback call_message data value" + assert parsed_response.explicit_channel_message is not None + assert parsed_response.explicit_channel_omni_message is not None + assert parsed_response.channel_specific_message is not None + assert parsed_response.agent is not None diff --git a/tests/unit/domains/conversation/v1/models/response/app_message/test_contact_info_app_message.py b/tests/unit/domains/conversation/v1/models/response/app_message/test_contact_info_app_message.py new file mode 100644 index 00000000..3205060d --- /dev/null +++ b/tests/unit/domains/conversation/v1/models/response/app_message/test_contact_info_app_message.py @@ -0,0 +1,139 @@ +import pytest +from datetime import date +from sinch.domains.conversation.models.v1.messages.categories.app.app_message import ( + ContactInfoAppMessage, +) + + +@pytest.fixture +def contact_info_app_message_data(): + """Test data for ContactInfoAppMessage from Java SDK.""" + return { + "contact_info_message": { + "name": { + "full_name": "full_name value", + "first_name": "first_name value", + "last_name": "last_name value", + "middle_name": "middle_name value", + "prefix": "prefix value", + "suffix": "suffix value" + }, + "phone_numbers": [ + { + "phone_number": "phone_number value", + "type": "type value" + } + ], + "addresses": [ + { + "city": "city value", + "country": "country value", + "state": "state va@lue", + "zip": "zip value", + "country_code": "country_code value" + } + ], + "email_addresses": [ + { + "email_address": "email_address value", + "type": "type value" + } + ], + "organization": { + "company": "company value", + "department": "department value", + "title": "title value" + }, + "urls": [ + { + "url": "url value", + "type": "type value" + } + ], + "birthday": "1968-07-07" + }, + "explicit_channel_message": { + "KAKAOTALK": "foo value" + }, + "explicit_channel_omni_message": { + "KAKAOTALK": { + "choice_message": { + "text_message": { + "text": "This is a text message." + }, + "choices": [ + { + "call_message": { + "title": "title value", + "phone_number": "phone number value" + }, + "postback_data": "postback call_message data value" + } + ] + } + } + }, + "channel_specific_message": { + "MESSENGER": { + "message_type": "FLOWS", + "message": { + "flow_id": "1", + "flow_cta": "Book!", + "header": { + "type": "text", + "text": "text header value" + }, + "body": { + "text": "Flow message body" + }, + "footer": { + "text": "Flow message footer" + }, + "flow_token": "AQAAAAACS5FpgQ_cAAAAAD0QI3s.", + "flow_mode": "draft", + "flow_action": "navigate", + "flow_action_payload": { + "screen": "", + "data": { + "product_name": "name", + "product_description": "description", + "product_price": 100 + } + } + } + } + }, + "agent": { + "display_name": "display_name value", + "type": "BOT", + "picture_url": "picture_url value" + } + } + + +def test_parsing_contact_info_app_message_expects_correct_fields(contact_info_app_message_data): + """Test that ContactInfoAppMessage is parsed correctly with all fields.""" + parsed_response = ContactInfoAppMessage.model_validate(contact_info_app_message_data) + + assert isinstance(parsed_response, ContactInfoAppMessage) + assert parsed_response.contact_info_message is not None + assert parsed_response.contact_info_message.name is not None + assert parsed_response.contact_info_message.name.full_name == "full_name value" + assert parsed_response.contact_info_message.name.first_name == "first_name value" + assert parsed_response.contact_info_message.name.last_name == "last_name value" + assert parsed_response.contact_info_message.name.middle_name == "middle_name value" + assert parsed_response.contact_info_message.name.prefix == "prefix value" + assert parsed_response.contact_info_message.name.suffix == "suffix value" + assert len(parsed_response.contact_info_message.phone_numbers) == 1 + assert parsed_response.contact_info_message.phone_numbers[0].phone_number == "phone_number value" + assert parsed_response.contact_info_message.phone_numbers[0].type == "type value" + assert len(parsed_response.contact_info_message.addresses) == 1 + assert len(parsed_response.contact_info_message.email_addresses) == 1 + assert parsed_response.contact_info_message.organization is not None + assert len(parsed_response.contact_info_message.urls) == 1 + assert isinstance(parsed_response.contact_info_message.birthday, date) + assert parsed_response.contact_info_message.birthday == date(1968, 7, 7) + assert parsed_response.channel_specific_message is not None + assert parsed_response.explicit_channel_omni_message is not None + assert parsed_response.explicit_channel_message is not None + assert parsed_response.agent is not None diff --git a/tests/unit/domains/conversation/v1/models/response/app_message/test_list_app_message.py b/tests/unit/domains/conversation/v1/models/response/app_message/test_list_app_message.py new file mode 100644 index 00000000..9cf9bad6 --- /dev/null +++ b/tests/unit/domains/conversation/v1/models/response/app_message/test_list_app_message.py @@ -0,0 +1,123 @@ +import pytest +from sinch.domains.conversation.models.v1.messages.categories.app.app_message import ( + ListAppMessage, +) + + +@pytest.fixture +def list_app_message_data(): + """Test data for ListAppMessage from Java SDK.""" + return { + "list_message": { + "title": "a list message title value", + "sections": [ + { + "title": "a list section title value", + "items": [ + { + "choice": { + "title": "choice title", + "description": "description value", + "media": { + "url": "an url value", + "thumbnail_url": "another url", + "filename_override": "filename override value" + }, + "postback_data": "postback value" + } + } + ] + } + ], + "description": "description value", + "message_properties": { + "catalog_id": "catalog ID value", + "menu": "menu value" + }, + "media": { + "url": "an url value", + "thumbnail_url": "another url", + "filename_override": "filename override value" + } + }, + "explicit_channel_message": { + "KAKAOTALK": "foo value" + }, + "explicit_channel_omni_message": { + "KAKAOTALK": { + "choice_message": { + "text_message": { + "text": "This is a text message." + }, + "choices": [ + { + "call_message": { + "title": "title value", + "phone_number": "phone number value" + }, + "postback_data": "postback call_message data value" + } + ] + } + } + }, + "channel_specific_message": { + "MESSENGER": { + "message_type": "FLOWS", + "message": { + "flow_id": "1", + "flow_cta": "Book!", + "header": { + "type": "text", + "text": "text header value" + }, + "body": { + "text": "Flow message body" + }, + "footer": { + "text": "Flow message footer" + }, + "flow_token": "AQAAAAACS5FpgQ_cAAAAAD0QI3s.", + "flow_mode": "draft", + "flow_action": "navigate", + "flow_action_payload": { + "screen": "", + "data": { + "product_name": "name", + "product_description": "description", + "product_price": 100 + } + } + } + } + }, + "agent": { + "display_name": "display_name value", + "type": "BOT", + "picture_url": "picture_url value" + } + } + + +def test_parsing_list_app_message_expects_correct_fields(list_app_message_data): + """Test that ListAppMessage is parsed correctly with all fields.""" + parsed_response = ListAppMessage.model_validate(list_app_message_data) + + assert isinstance(parsed_response, ListAppMessage) + assert parsed_response.list_message is not None + assert parsed_response.list_message.title == "a list message title value" + assert parsed_response.list_message.description == "description value" + assert len(parsed_response.list_message.sections) == 1 + assert parsed_response.list_message.sections[0].title == "a list section title value" + assert len(parsed_response.list_message.sections[0].items) == 1 + assert parsed_response.list_message.sections[0].items[0].choice.title == "choice title" + assert parsed_response.list_message.sections[0].items[0].choice.description == "description value" + assert parsed_response.list_message.message_properties is not None + assert parsed_response.list_message.message_properties.catalog_id == "catalog ID value" + assert parsed_response.list_message.message_properties.menu == "menu value" + assert parsed_response.list_message.media is not None + assert parsed_response.list_message.media.url == "an url value" + assert parsed_response.explicit_channel_message is not None + assert parsed_response.explicit_channel_omni_message is not None + assert parsed_response.channel_specific_message is not None + assert parsed_response.agent is not None diff --git a/tests/unit/domains/conversation/v1/models/response/app_message/test_location_app_message.py b/tests/unit/domains/conversation/v1/models/response/app_message/test_location_app_message.py new file mode 100644 index 00000000..27340e2c --- /dev/null +++ b/tests/unit/domains/conversation/v1/models/response/app_message/test_location_app_message.py @@ -0,0 +1,91 @@ +import pytest +from sinch.domains.conversation.models.v1.messages.categories.app.app_message import ( + LocationAppMessage, +) + + +@pytest.fixture +def location_app_message_data(): + """Test data for LocationAppMessage from Java SDK.""" + return { + "location_message": { + "coordinates": { + "latitude": 47.6279809, + "longitude": -2.8229159 + }, + "label": "label value", + "title": "title value" + }, + "explicit_channel_message": { + "KAKAOTALK": "foo value" + }, + "explicit_channel_omni_message": { + "KAKAOTALK": { + "choice_message": { + "choices": [ + { + "call_message": { + "phone_number": "phone number value", + "title": "title value" + }, + "postback_data": "postback call_message data value" + } + ], + "text_message": { + "text": "This is a text message." + } + } + } + }, + "channel_specific_message": { + "MESSENGER": { + "message_type": "FLOWS", + "message": { + "header": { + "type": "text", + "text": "text header value" + }, + "body": { + "text": "Flow message body" + }, + "footer": { + "text": "Flow message footer" + }, + "flow_id": "1", + "flow_token": "AQAAAAACS5FpgQ_cAAAAAD0QI3s.", + "flow_mode": "draft", + "flow_cta": "Book!", + "flow_action": "navigate", + "flow_action_payload": { + "screen": "", + "data": { + "product_price": 100, + "product_description": "description", + "product_name": "name" + } + } + } + } + }, + "agent": { + "display_name": "display_name value", + "type": "BOT", + "picture_url": "picture_url value" + } + } + + +def test_parsing_location_app_message_expects_correct_fields(location_app_message_data): + """Test that LocationAppMessage is parsed correctly with all fields.""" + parsed_response = LocationAppMessage.model_validate(location_app_message_data) + + assert isinstance(parsed_response, LocationAppMessage) + assert parsed_response.location_message is not None + assert parsed_response.location_message.title == "title value" + assert parsed_response.location_message.label == "label value" + assert parsed_response.location_message.coordinates.latitude == 47.6279809 + assert parsed_response.location_message.coordinates.longitude == -2.8229159 + assert parsed_response.explicit_channel_message is not None + assert parsed_response.explicit_channel_omni_message is not None + assert parsed_response.channel_specific_message is not None + assert parsed_response.agent is not None diff --git a/tests/unit/domains/conversation/v1/models/response/app_message/test_media_app_message.py b/tests/unit/domains/conversation/v1/models/response/app_message/test_media_app_message.py new file mode 100644 index 00000000..51b18435 --- /dev/null +++ b/tests/unit/domains/conversation/v1/models/response/app_message/test_media_app_message.py @@ -0,0 +1,87 @@ +import pytest +from sinch.domains.conversation.models.v1.messages.categories.app.app_message import ( + MediaAppMessage, +) + + +@pytest.fixture +def media_app_message_data(): + """Test data for MediaAppMessage from Java SDK.""" + return { + "media_message": { + "url": "an url value", + "thumbnail_url": "another url", + "filename_override": "filename override value" + }, + "explicit_channel_message": { + "KAKAOTALK": "foo value" + }, + "explicit_channel_omni_message": { + "KAKAOTALK": { + "choice_message": { + "text_message": { + "text": "This is a text message." + }, + "choices": [ + { + "call_message": { + "title": "title value", + "phone_number": "phone number value" + }, + "postback_data": "postback call_message data value" + } + ] + } + } + }, + "channel_specific_message": { + "MESSENGER": { + "message_type": "FLOWS", + "message": { + "flow_id": "1", + "flow_cta": "Book!", + "header": { + "type": "text", + "text": "text header value" + }, + "body": { + "text": "Flow message body" + }, + "footer": { + "text": "Flow message footer" + }, + "flow_token": "AQAAAAACS5FpgQ_cAAAAAD0QI3s.", + "flow_mode": "draft", + "flow_action": "navigate", + "flow_action_payload": { + "screen": "", + "data": { + "product_name": "name", + "product_description": "description", + "product_price": 100 + } + } + } + } + }, + "agent": { + "display_name": "display_name value", + "type": "BOT", + "picture_url": "picture_url value" + } + } + + +def test_parsing_media_app_message_expects_correct_fields(media_app_message_data): + """Test that MediaAppMessage is parsed correctly with all fields.""" + parsed_response = MediaAppMessage.model_validate(media_app_message_data) + + assert isinstance(parsed_response, MediaAppMessage) + assert parsed_response.media_message is not None + assert parsed_response.media_message.url == "an url value" + assert parsed_response.media_message.thumbnail_url == "another url" + assert parsed_response.media_message.filename_override == "filename override value" + assert parsed_response.explicit_channel_message is not None + assert parsed_response.explicit_channel_omni_message is not None + assert parsed_response.channel_specific_message is not None + assert parsed_response.agent is not None diff --git a/tests/unit/domains/conversation/v1/models/response/app_message/test_template_app_message.py b/tests/unit/domains/conversation/v1/models/response/app_message/test_template_app_message.py new file mode 100644 index 00000000..47c6677f --- /dev/null +++ b/tests/unit/domains/conversation/v1/models/response/app_message/test_template_app_message.py @@ -0,0 +1,104 @@ +import pytest +from sinch.domains.conversation.models.v1.messages.categories.app.app_message import ( + TemplateAppMessage, +) + + +@pytest.fixture +def template_app_message_data(): + """Test data for TemplateAppMessage from Java SDK.""" + return { + "template_message": { + "channel_template": { + "KAKAOTALK": { + "template_id": "my template ID value", + "language_code": "en-US" + } + }, + "omni_template": { + "template_id": "another template ID", + "version": "another version", + "language_code": "another language", + "parameters": { + "name": "Value for the name parameter used in the version 1 and language \"en-US\" of the template" + } + } + }, + "explicit_channel_message": { + "KAKAOTALK": "foo value" + }, + "explicit_channel_omni_message": { + "KAKAOTALK": { + "choice_message": { + "text_message": { + "text": "This is a text message." + }, + "choices": [ + { + "call_message": { + "title": "title value", + "phone_number": "phone number value" + }, + "postback_data": "postback call_message data value" + } + ] + } + } + }, + "channel_specific_message": { + "MESSENGER": { + "message_type": "FLOWS", + "message": { + "flow_id": "1", + "flow_cta": "Book!", + "header": { + "type": "text", + "text": "text header value" + }, + "body": { + "text": "Flow message body" + }, + "footer": { + "text": "Flow message footer" + }, + "flow_token": "AQAAAAACS5FpgQ_cAAAAAD0QI3s.", + "flow_mode": "draft", + "flow_action": "navigate", + "flow_action_payload": { + "screen": "", + "data": { + "product_name": "name", + "product_description": "description", + "product_price": 100 + } + } + } + } + }, + "agent": { + "display_name": "display_name value", + "type": "BOT", + "picture_url": "picture_url value" + } + } + + +def test_parsing_template_app_message_expects_correct_fields(template_app_message_data): + """Test that TemplateAppMessage is parsed correctly with all fields.""" + parsed_response = TemplateAppMessage.model_validate(template_app_message_data) + + assert isinstance(parsed_response, TemplateAppMessage) + assert parsed_response.template_message is not None + assert parsed_response.template_message.channel_template is not None + assert "KAKAOTALK" in parsed_response.template_message.channel_template + assert parsed_response.template_message.channel_template["KAKAOTALK"].template_id == "my template ID value" + assert parsed_response.template_message.channel_template["KAKAOTALK"].language_code == "en-US" + assert parsed_response.template_message.omni_template is not None + assert parsed_response.template_message.omni_template.template_id == "another template ID" + assert parsed_response.template_message.omni_template.version == "another version" + assert parsed_response.template_message.omni_template.language_code == "another language" + assert parsed_response.template_message.omni_template.parameters is not None + assert parsed_response.explicit_channel_message is not None + assert parsed_response.explicit_channel_omni_message is not None + assert parsed_response.channel_specific_message is not None + assert parsed_response.agent is not None diff --git a/tests/unit/domains/conversation/v1/models/response/app_message/test_text_app_message.py b/tests/unit/domains/conversation/v1/models/response/app_message/test_text_app_message.py new file mode 100644 index 00000000..88972885 --- /dev/null +++ b/tests/unit/domains/conversation/v1/models/response/app_message/test_text_app_message.py @@ -0,0 +1,83 @@ +import pytest +from sinch.domains.conversation.models.v1.messages.categories.app.app_message import ( + TextAppMessage, +) + + +@pytest.fixture +def text_app_message_data(): + """Test data for TextAppMessage from Java SDK.""" + return { + "text_message": { + "text": "This is a text message." + }, + "explicit_channel_message": { + "KAKAOTALK": "foo value" + }, + "explicit_channel_omni_message": { + "KAKAOTALK": { + "choice_message": { + "text_message": { + "text": "This is a text message." + }, + "choices": [ + { + "call_message": { + "title": "title value", + "phone_number": "phone number value" + }, + "postback_data": "postback call_message data value" + } + ] + } + } + }, + "channel_specific_message": { + "MESSENGER": { + "message_type": "FLOWS", + "message": { + "flow_id": "1", + "flow_cta": "Book!", + "header": { + "type": "text", + "text": "text header value" + }, + "body": { + "text": "Flow message body" + }, + "footer": { + "text": "Flow message footer" + }, + "flow_token": "AQAAAAACS5FpgQ_cAAAAAD0QI3s.", + "flow_mode": "draft", + "flow_action": "navigate", + "flow_action_payload": { + "screen": "", + "data": { + "product_name": "name", + "product_description": "description", + "product_price": 100 + } + } + } + } + }, + "agent": { + "display_name": "display_name value", + "type": "BOT", + "picture_url": "picture_url value" + } + } + + +def test_parsing_text_app_message_expects_correct_fields(text_app_message_data): + """Test that TextAppMessage is parsed correctly with all fields.""" + parsed_response = TextAppMessage.model_validate(text_app_message_data) + + assert isinstance(parsed_response, TextAppMessage) + assert parsed_response.text_message is not None + assert parsed_response.text_message.text == "This is a text message." + assert parsed_response.explicit_channel_message is not None + assert parsed_response.channel_specific_message is not None + assert parsed_response.explicit_channel_omni_message is not None + assert parsed_response.agent is not None diff --git a/tests/unit/domains/conversation/v1/models/response/test_conversation_message_response_model.py b/tests/unit/domains/conversation/v1/models/response/test_conversation_message_response_model.py new file mode 100644 index 00000000..ebbdecff --- /dev/null +++ b/tests/unit/domains/conversation/v1/models/response/test_conversation_message_response_model.py @@ -0,0 +1,176 @@ +import pytest +from datetime import datetime, timezone +from sinch.domains.conversation.models.v1.messages.response.message_response import ( + AppMessageResponse, + ContactMessageResponse, +) + + +@pytest.fixture +def contact_message_response_data(): + """Test data for ContactMessageResponse.""" + return { + "id": "CAPY123456789ABCDEFGHIJKLMNOP", + "conversation_id": "CONV987654321ZYXWVUTSRQPONMLK", + "contact_id": "CONTACT456789ABCDEFGHIJKLMNOPQR", + "direction": "UNDEFINED_DIRECTION", + "channel_identity": { + "app_id": "APP123456789ABCDEFGHIJK", + "channel": "WHATSAPP", + "identity": "+46701234567" + }, + "metadata": "test_metadata", + "accept_time": "2026-01-14T20:32:31.147Z", + "injected": True, + "sender_id": "SENDER123456789ABCDEFGHIJK", + "processing_mode": "CONVERSATION", + "contact_message": { + "channel_specific_message": { + "message_type": "nfm_reply", + "message": { + "type": "nfm_reply", + "nfm_reply": { + "name": "flow", + "response_json": "{\"key\": \"value\"}", + "body": "Message body text" + } + } + }, + "reply_to": { + "message_id": "REPLY_TO_MSG123456789ABCDEF" + } + } + } + + +@pytest.fixture +def app_message_response_data(): + """Test data for AppMessageResponse.""" + return { + "id": "APP123456789ABCDEFGHIJKLMNOP", + "conversation_id": "CONV987654321ZYXWVUTSRQPONMLK", + "contact_id": "CONTACT456789ABCDEFGHIJKLMNOPQR", + "direction": "UNDEFINED_DIRECTION", + "channel_identity": { + "app_id": "APP123456789ABCDEFGHIJK", + "channel": "WHATSAPP", + "identity": "+46701234567" + }, + "metadata": "test_metadata", + "accept_time": "2026-01-14T20:32:31.147Z", + "injected": True, + "sender_id": "SENDER123456789ABCDEFGHIJK", + "processing_mode": "CONVERSATION", + "app_message": { + "card_message": { + "choices": [ + { + "call_message": { + "phone_number": "+15551231234", + "title": "Message text" + }, + "postback_data": None + } + ], + "description": "Card description text", + "height": "UNSPECIFIED_HEIGHT", + "title": "Card title", + "media_message": { + "thumbnail_url": "https://example.com/thumbnail.jpg", + "url": "https://example.com/media.jpg", + "filename_override": "custom_filename.jpg" + }, + "message_properties": { + "whatsapp_header": "WhatsApp header text" + } + }, + "agent": { + "display_name": "Agent Name", + "type": "UNKNOWN_AGENT_TYPE", + "picture_url": "https://example.com/agent.jpg" + } + } + } + + +def test_parsing_contact_message_response_expects_correct_fields(contact_message_response_data): + """Test that ContactMessageResponse is parsed correctly with all fields.""" + parsed_response = ContactMessageResponse.model_validate(contact_message_response_data) + + # ConversationMessageResponse is a Union of AppMessageResponse and ContactMessageResponse + # In this test case, we expect a ContactMessageResponse + assert isinstance(parsed_response, ContactMessageResponse) + assert not isinstance(parsed_response, AppMessageResponse) + + assert parsed_response.id == "CAPY123456789ABCDEFGHIJKLMNOP" + assert parsed_response.conversation_id == "CONV987654321ZYXWVUTSRQPONMLK" + assert parsed_response.contact_id == "CONTACT456789ABCDEFGHIJKLMNOPQR" + assert parsed_response.direction == "UNDEFINED_DIRECTION" + assert parsed_response.metadata == "test_metadata" + assert parsed_response.contact_message is not None + assert parsed_response.contact_message.channel_specific_message is not None + assert parsed_response.contact_message.channel_specific_message.message_type == "nfm_reply" + assert parsed_response.contact_message.channel_specific_message.message.type == "nfm_reply" + assert parsed_response.contact_message.channel_specific_message.message.nfm_reply.name == "flow" + assert parsed_response.contact_message.channel_specific_message.message.nfm_reply.response_json == "{\"key\": \"value\"}" + assert parsed_response.contact_message.channel_specific_message.message.nfm_reply.body == "Message body text" + assert parsed_response.contact_message.reply_to is not None + assert parsed_response.contact_message.reply_to.message_id == "REPLY_TO_MSG123456789ABCDEF" + assert parsed_response.channel_identity is not None + assert parsed_response.channel_identity.app_id == "APP123456789ABCDEFGHIJK" + assert parsed_response.channel_identity.channel == "WHATSAPP" + assert parsed_response.channel_identity.identity == "+46701234567" + assert parsed_response.injected is True + assert parsed_response.sender_id == "SENDER123456789ABCDEFGHIJK" + assert parsed_response.processing_mode == "CONVERSATION" + + assert parsed_response.accept_time == datetime( + 2026, 1, 14, 20, 32, 31, 147000, tzinfo=timezone.utc + ) + + +def test_parsing_app_message_response_expects_correct_fields(app_message_response_data): + """Test that AppMessageResponse is parsed correctly with all fields.""" + parsed_response = AppMessageResponse.model_validate(app_message_response_data) + + # ConversationMessageResponse is a Union of AppMessageResponse and ContactMessageResponse + # In this test case, we expect an AppMessageResponse + assert isinstance(parsed_response, AppMessageResponse) + assert not isinstance(parsed_response, ContactMessageResponse) + + assert parsed_response.id == "APP123456789ABCDEFGHIJKLMNOP" + assert parsed_response.conversation_id == "CONV987654321ZYXWVUTSRQPONMLK" + assert parsed_response.contact_id == "CONTACT456789ABCDEFGHIJKLMNOPQR" + assert parsed_response.direction == "UNDEFINED_DIRECTION" + assert parsed_response.metadata == "test_metadata" + assert parsed_response.app_message is not None + assert parsed_response.app_message.card_message is not None + assert parsed_response.app_message.card_message.title == "Card title" + assert parsed_response.app_message.card_message.description == "Card description text" + assert parsed_response.app_message.card_message.height == "UNSPECIFIED_HEIGHT" + assert parsed_response.app_message.card_message.choices is not None + assert len(parsed_response.app_message.card_message.choices) == 1 + assert parsed_response.app_message.card_message.choices[0].call_message is not None + assert parsed_response.app_message.card_message.choices[0].call_message.phone_number == "+15551231234" + assert parsed_response.app_message.card_message.choices[0].call_message.title == "Message text" + assert parsed_response.app_message.card_message.media_message is not None + assert parsed_response.app_message.card_message.media_message.url == "https://example.com/media.jpg" + assert parsed_response.app_message.card_message.media_message.thumbnail_url == "https://example.com/thumbnail.jpg" + assert parsed_response.app_message.card_message.media_message.filename_override == "custom_filename.jpg" + assert parsed_response.app_message.card_message.message_properties is not None + assert parsed_response.app_message.card_message.message_properties.whatsapp_header == "WhatsApp header text" + assert parsed_response.app_message.agent is not None + assert parsed_response.app_message.agent.display_name == "Agent Name" + assert parsed_response.app_message.agent.type == "UNKNOWN_AGENT_TYPE" + assert parsed_response.app_message.agent.picture_url == "https://example.com/agent.jpg" + assert parsed_response.channel_identity is not None + assert parsed_response.channel_identity.app_id == "APP123456789ABCDEFGHIJK" + assert parsed_response.channel_identity.channel == "WHATSAPP" + assert parsed_response.channel_identity.identity == "+46701234567" + assert parsed_response.injected is True + assert parsed_response.sender_id == "SENDER123456789ABCDEFGHIJK" + assert parsed_response.processing_mode == "CONVERSATION" + + assert parsed_response.accept_time == datetime( + 2026, 1, 14, 20, 32, 31, 147000, tzinfo=timezone.utc + )