From dfbb9b54c13e25825a6a1ccf1cd568bb4023035b Mon Sep 17 00:00:00 2001 From: Lukasz Lancucki Date: Thu, 19 Mar 2026 10:55:54 +0000 Subject: [PATCH] feat: add helpdesk chat participants api with unit and e2e tests --- .../resources/helpdesk/chat_participants.py | 46 +++++++++++++++ mpt_api_client/resources/helpdesk/chats.py | 16 ++++++ pyproject.toml | 1 + .../helpdesk/chats/participants/__init__.py | 1 + .../helpdesk/chats/participants/conftest.py | 45 +++++++++++++++ .../participants/test_async_participants.py | 57 +++++++++++++++++++ .../participants/test_sync_participants.py | 43 ++++++++++++++ .../helpdesk/test_chat_participants.py | 52 +++++++++++++++++ tests/unit/resources/helpdesk/test_chats.py | 6 ++ 9 files changed, 267 insertions(+) create mode 100644 mpt_api_client/resources/helpdesk/chat_participants.py create mode 100644 tests/e2e/helpdesk/chats/participants/__init__.py create mode 100644 tests/e2e/helpdesk/chats/participants/conftest.py create mode 100644 tests/e2e/helpdesk/chats/participants/test_async_participants.py create mode 100644 tests/e2e/helpdesk/chats/participants/test_sync_participants.py create mode 100644 tests/unit/resources/helpdesk/test_chat_participants.py diff --git a/mpt_api_client/resources/helpdesk/chat_participants.py b/mpt_api_client/resources/helpdesk/chat_participants.py new file mode 100644 index 00000000..10b3d208 --- /dev/null +++ b/mpt_api_client/resources/helpdesk/chat_participants.py @@ -0,0 +1,46 @@ +from mpt_api_client.http import AsyncService, Service +from mpt_api_client.http.mixins import ( + AsyncCollectionMixin, + AsyncCreateMixin, + AsyncDeleteMixin, + AsyncUpdateMixin, + CollectionMixin, + CreateMixin, + DeleteMixin, + UpdateMixin, +) +from mpt_api_client.models import Model + + +class ChatParticipant(Model): + """Helpdesk Chat Participant resource.""" + + +class ChatParticipantsServiceConfig: + """Helpdesk Chat Participants service configuration.""" + + _endpoint = "/public/v1/helpdesk/chats/{chat_id}/participants" + _model_class = ChatParticipant + _collection_key = "data" + + +class ChatParticipantsService( + CreateMixin[ChatParticipant], + UpdateMixin[ChatParticipant], + DeleteMixin, + CollectionMixin[ChatParticipant], + Service[ChatParticipant], + ChatParticipantsServiceConfig, +): + """Helpdesk Chat Participants service.""" + + +class AsyncChatParticipantsService( + AsyncCreateMixin[ChatParticipant], + AsyncUpdateMixin[ChatParticipant], + AsyncDeleteMixin, + AsyncCollectionMixin[ChatParticipant], + AsyncService[ChatParticipant], + ChatParticipantsServiceConfig, +): + """Async Helpdesk Chat Participants service.""" diff --git a/mpt_api_client/resources/helpdesk/chats.py b/mpt_api_client/resources/helpdesk/chats.py index 26d416a7..297794ef 100644 --- a/mpt_api_client/resources/helpdesk/chats.py +++ b/mpt_api_client/resources/helpdesk/chats.py @@ -22,6 +22,10 @@ AsyncChatMessagesService, ChatMessagesService, ) +from mpt_api_client.resources.helpdesk.chat_participants import ( + AsyncChatParticipantsService, + ChatParticipantsService, +) class Chat(Model): @@ -62,6 +66,12 @@ def links(self, chat_id: str) -> ChatLinksService: """Return chat links service.""" return ChatLinksService(http_client=self.http_client, endpoint_params={"chat_id": chat_id}) + def participants(self, chat_id: str) -> ChatParticipantsService: + """Return chat participants service.""" + return ChatParticipantsService( + http_client=self.http_client, endpoint_params={"chat_id": chat_id} + ) + class AsyncChatsService( AsyncCreateMixin[Chat], @@ -90,3 +100,9 @@ def links(self, chat_id: str) -> AsyncChatLinksService: return AsyncChatLinksService( http_client=self.http_client, endpoint_params={"chat_id": chat_id} ) + + def participants(self, chat_id: str) -> AsyncChatParticipantsService: + """Return async chat participants service.""" + return AsyncChatParticipantsService( + http_client=self.http_client, endpoint_params={"chat_id": chat_id} + ) diff --git a/pyproject.toml b/pyproject.toml index bb278f44..be7b59d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -122,6 +122,7 @@ per-file-ignores = [ "mpt_api_client/resources/catalog/*.py: WPS110 WPS214 WPS215 WPS235", "mpt_api_client/resources/catalog/products.py: WPS204 WPS214 WPS215 WPS235", "mpt_api_client/resources/commerce/*.py: WPS235 WPS215", + "mpt_api_client/resources/helpdesk/*.py: WPS204 WPS215", "mpt_api_client/rql/query_builder.py: WPS110 WPS115 WPS210 WPS214", "tests/e2e/accounts/*.py: WPS430 WPS202", "tests/e2e/billing/*.py: WPS202 WPS421 WPS118", diff --git a/tests/e2e/helpdesk/chats/participants/__init__.py b/tests/e2e/helpdesk/chats/participants/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/e2e/helpdesk/chats/participants/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/e2e/helpdesk/chats/participants/conftest.py b/tests/e2e/helpdesk/chats/participants/conftest.py new file mode 100644 index 00000000..a3c191c8 --- /dev/null +++ b/tests/e2e/helpdesk/chats/participants/conftest.py @@ -0,0 +1,45 @@ +import pytest + +from tests.e2e.helper import ( + async_create_fixture_resource_and_delete, + create_fixture_resource_and_delete, +) + + +@pytest.fixture +def chat_participants_service(mpt_ops, chat_id): + return mpt_ops.helpdesk.chats.participants(chat_id) + + +@pytest.fixture +def async_chat_participants_service(async_mpt_ops, chat_id): + return async_mpt_ops.helpdesk.chats.participants(chat_id) + + +@pytest.fixture +def chat_participant_data(account_id, user_id): + return { + "identity": {"id": user_id}, + "account": {"id": account_id}, + } + + +@pytest.fixture +def created_chat_participant(chat_participants_service, chat_participant_data): + with create_fixture_resource_and_delete( + chat_participants_service, chat_participant_data + ) as chat_participant: + yield chat_participant + + +@pytest.fixture +async def async_created_chat_participant(async_chat_participants_service, chat_participant_data): + async with async_create_fixture_resource_and_delete( + async_chat_participants_service, chat_participant_data + ) as chat_participant: + yield chat_participant + + +@pytest.fixture +def invalid_chat_participant_id(): + return "CHP-0000-0000-0000" diff --git a/tests/e2e/helpdesk/chats/participants/test_async_participants.py b/tests/e2e/helpdesk/chats/participants/test_async_participants.py new file mode 100644 index 00000000..7bdd9160 --- /dev/null +++ b/tests/e2e/helpdesk/chats/participants/test_async_participants.py @@ -0,0 +1,57 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError + +pytestmark = [pytest.mark.flaky] + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_list_chat_participants(async_chat_participants_service): + result = await async_chat_participants_service.fetch_page(limit=1) + + assert len(result) > 0 + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") # noqa: AAA01 +def test_create_chat_participant(async_created_chat_participant): + assert async_created_chat_participant.id is not None + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_update_chat_participant( + async_chat_participants_service, async_created_chat_participant +): + result = await async_chat_participants_service.update( + async_created_chat_participant.id, + {"status": "Active"}, + ) + + assert result.id == async_created_chat_participant.id + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_delete_chat_participant( + async_chat_participants_service, async_created_chat_participant +): + result = async_created_chat_participant + + await async_chat_participants_service.delete(result.id) + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_update_chat_participant_not_found( + async_chat_participants_service, invalid_chat_participant_id +): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + await async_chat_participants_service.update( + invalid_chat_participant_id, + {"status": "Active"}, + ) + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_delete_chat_participant_not_found( + async_chat_participants_service, invalid_chat_participant_id +): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + await async_chat_participants_service.delete(invalid_chat_participant_id) diff --git a/tests/e2e/helpdesk/chats/participants/test_sync_participants.py b/tests/e2e/helpdesk/chats/participants/test_sync_participants.py new file mode 100644 index 00000000..27f806da --- /dev/null +++ b/tests/e2e/helpdesk/chats/participants/test_sync_participants.py @@ -0,0 +1,43 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError + +pytestmark = [pytest.mark.flaky] + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_list_chat_participants(chat_participants_service): + result = chat_participants_service.fetch_page(limit=1) + + assert len(result) > 0 + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") # noqa: AAA01 +def test_create_chat_participant(created_chat_participant): + assert created_chat_participant.id is not None + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_update_chat_participant(chat_participants_service, created_chat_participant): + result = chat_participants_service.update(created_chat_participant.id, {"status": "Active"}) + + assert result.id == created_chat_participant.id + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_delete_chat_participant(chat_participants_service, created_chat_participant): + result = created_chat_participant + + chat_participants_service.delete(result.id) + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_update_chat_participant_not_found(chat_participants_service, invalid_chat_participant_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + chat_participants_service.update(invalid_chat_participant_id, {"status": "Active"}) + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_delete_chat_participant_not_found(chat_participants_service, invalid_chat_participant_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + chat_participants_service.delete(invalid_chat_participant_id) diff --git a/tests/unit/resources/helpdesk/test_chat_participants.py b/tests/unit/resources/helpdesk/test_chat_participants.py new file mode 100644 index 00000000..021dae98 --- /dev/null +++ b/tests/unit/resources/helpdesk/test_chat_participants.py @@ -0,0 +1,52 @@ +import pytest + +from mpt_api_client.resources.helpdesk.chat_participants import ( + AsyncChatParticipantsService, + ChatParticipantsService, +) + + +@pytest.fixture +def chat_participants_service(http_client) -> ChatParticipantsService: + return ChatParticipantsService( + http_client=http_client, endpoint_params={"chat_id": "CHT-0000-0000-0001"} + ) + + +@pytest.fixture +def async_chat_participants_service(async_http_client) -> AsyncChatParticipantsService: + return AsyncChatParticipantsService( + http_client=async_http_client, endpoint_params={"chat_id": "CHT-0000-0000-0001"} + ) + + +def test_endpoint(chat_participants_service) -> None: + result = ( + chat_participants_service.path + == "/public/v1/helpdesk/chats/CHT-0000-0000-0001/participants" + ) + + assert result is True + + +def test_async_endpoint(async_chat_participants_service) -> None: + result = ( + async_chat_participants_service.path + == "/public/v1/helpdesk/chats/CHT-0000-0000-0001/participants" + ) + + assert result is True + + +@pytest.mark.parametrize("method", ["create", "update", "delete", "iterate"]) +def test_methods_present(chat_participants_service, method: str) -> None: + result = hasattr(chat_participants_service, method) + + assert result is True + + +@pytest.mark.parametrize("method", ["create", "update", "delete", "iterate"]) +def test_async_methods_present(async_chat_participants_service, method: str) -> None: + result = hasattr(async_chat_participants_service, method) + + assert result is True diff --git a/tests/unit/resources/helpdesk/test_chats.py b/tests/unit/resources/helpdesk/test_chats.py index d9670202..82dfd8e8 100644 --- a/tests/unit/resources/helpdesk/test_chats.py +++ b/tests/unit/resources/helpdesk/test_chats.py @@ -12,6 +12,10 @@ AsyncChatMessagesService, ChatMessagesService, ) +from mpt_api_client.resources.helpdesk.chat_participants import ( + AsyncChatParticipantsService, + ChatParticipantsService, +) from mpt_api_client.resources.helpdesk.chats import AsyncChatsService, ChatsService @@ -50,6 +54,7 @@ def test_async_mixins_present(async_chats_service, method): [ ("messages", ChatMessagesService), ("links", ChatLinksService), + ("participants", ChatParticipantsService), ], ) def test_property_services(chats_service, service_method, expected_service_class): @@ -71,6 +76,7 @@ def test_attachments_service(chats_service): [ ("messages", AsyncChatMessagesService), ("links", AsyncChatLinksService), + ("participants", AsyncChatParticipantsService), ], ) def test_async_property_services(async_chats_service, service_method, expected_service_class):