From f9633bd2966eebacfdbd0030208c7ae2bfff10c7 Mon Sep 17 00:00:00 2001 From: Lukasz Lancucki Date: Wed, 18 Mar 2026 14:44:48 +0000 Subject: [PATCH] feat: add helpdesk chat links services and tests --- .../resources/helpdesk/chat_links.py | 46 ++++++++++++++++++ mpt_api_client/resources/helpdesk/chats.py | 14 ++++++ tests/e2e/helpdesk/chats/links/__init__.py | 1 + tests/e2e/helpdesk/chats/links/conftest.py | 43 +++++++++++++++++ .../helpdesk/chats/links/test_async_links.py | 47 +++++++++++++++++++ .../helpdesk/chats/links/test_sync_links.py | 41 ++++++++++++++++ .../resources/helpdesk/test_chat_links.py | 46 ++++++++++++++++++ tests/unit/resources/helpdesk/test_chats.py | 30 +++++++++--- 8 files changed, 262 insertions(+), 6 deletions(-) create mode 100644 mpt_api_client/resources/helpdesk/chat_links.py create mode 100644 tests/e2e/helpdesk/chats/links/__init__.py create mode 100644 tests/e2e/helpdesk/chats/links/conftest.py create mode 100644 tests/e2e/helpdesk/chats/links/test_async_links.py create mode 100644 tests/e2e/helpdesk/chats/links/test_sync_links.py create mode 100644 tests/unit/resources/helpdesk/test_chat_links.py diff --git a/mpt_api_client/resources/helpdesk/chat_links.py b/mpt_api_client/resources/helpdesk/chat_links.py new file mode 100644 index 0000000..2c005fd --- /dev/null +++ b/mpt_api_client/resources/helpdesk/chat_links.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 ChatLink(Model): + """Helpdesk Chat Link resource.""" + + +class ChatLinksServiceConfig: + """Helpdesk Chat Links service configuration.""" + + _endpoint = "/public/v1/helpdesk/chats/{chat_id}/links" + _model_class = ChatLink + _collection_key = "data" + + +class ChatLinksService( + CreateMixin[ChatLink], + UpdateMixin[ChatLink], + DeleteMixin, + CollectionMixin[ChatLink], + Service[ChatLink], + ChatLinksServiceConfig, +): + """Helpdesk Chat Links service.""" + + +class AsyncChatLinksService( + AsyncCreateMixin[ChatLink], + AsyncUpdateMixin[ChatLink], + AsyncDeleteMixin, + AsyncCollectionMixin[ChatLink], + AsyncService[ChatLink], + ChatLinksServiceConfig, +): + """Async Helpdesk Chat Links service.""" diff --git a/mpt_api_client/resources/helpdesk/chats.py b/mpt_api_client/resources/helpdesk/chats.py index 62dcbeb..0672da1 100644 --- a/mpt_api_client/resources/helpdesk/chats.py +++ b/mpt_api_client/resources/helpdesk/chats.py @@ -10,6 +10,10 @@ UpdateMixin, ) from mpt_api_client.models import Model +from mpt_api_client.resources.helpdesk.chat_links import ( + AsyncChatLinksService, + ChatLinksService, +) from mpt_api_client.resources.helpdesk.chat_messages import ( AsyncChatMessagesService, ChatMessagesService, @@ -44,6 +48,10 @@ def messages(self, chat_id: str) -> ChatMessagesService: http_client=self.http_client, endpoint_params={"chat_id": chat_id} ) + def links(self, chat_id: str) -> ChatLinksService: + """Return chat links service.""" + return ChatLinksService(http_client=self.http_client, endpoint_params={"chat_id": chat_id}) + class AsyncChatsService( AsyncCreateMixin[Chat], @@ -60,3 +68,9 @@ def messages(self, chat_id: str) -> AsyncChatMessagesService: return AsyncChatMessagesService( http_client=self.http_client, endpoint_params={"chat_id": chat_id} ) + + def links(self, chat_id: str) -> AsyncChatLinksService: + """Return async chat links service.""" + return AsyncChatLinksService( + http_client=self.http_client, endpoint_params={"chat_id": chat_id} + ) diff --git a/tests/e2e/helpdesk/chats/links/__init__.py b/tests/e2e/helpdesk/chats/links/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/e2e/helpdesk/chats/links/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/e2e/helpdesk/chats/links/conftest.py b/tests/e2e/helpdesk/chats/links/conftest.py new file mode 100644 index 0000000..2729c93 --- /dev/null +++ b/tests/e2e/helpdesk/chats/links/conftest.py @@ -0,0 +1,43 @@ +import pytest + +from tests.e2e.helper import ( + async_create_fixture_resource_and_delete, + create_fixture_resource_and_delete, +) + + +@pytest.fixture +def chat_links_service(mpt_ops, chat_id): + return mpt_ops.helpdesk.chats.links(chat_id) + + +@pytest.fixture +def async_chat_links_service(async_mpt_ops, chat_id): + return async_mpt_ops.helpdesk.chats.links(chat_id) + + +@pytest.fixture +def chat_link_data(short_uuid): + return { + "uri": f"https://example.com/e2e-link-{short_uuid}", + "name": f"e2e link - {short_uuid}", + } + + +@pytest.fixture +def created_chat_link(chat_links_service, chat_link_data): + with create_fixture_resource_and_delete(chat_links_service, chat_link_data) as chat_link: + yield chat_link + + +@pytest.fixture +async def async_created_chat_link(async_chat_links_service, chat_link_data): + async with async_create_fixture_resource_and_delete( + async_chat_links_service, chat_link_data + ) as chat_link: + yield chat_link + + +@pytest.fixture +def invalid_chat_link_id(): + return "LNK-0000-0000-0000" diff --git a/tests/e2e/helpdesk/chats/links/test_async_links.py b/tests/e2e/helpdesk/chats/links/test_async_links.py new file mode 100644 index 0000000..4c7a62f --- /dev/null +++ b/tests/e2e/helpdesk/chats/links/test_async_links.py @@ -0,0 +1,47 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError + +pytestmark = [pytest.mark.flaky] + + +async def test_list_chat_links(async_chat_links_service): + result = await async_chat_links_service.fetch_page(limit=1) + + assert len(result) > 0 + + +def test_create_chat_link(async_created_chat_link, chat_link_data): # noqa: AAA01 + assert async_created_chat_link.id is not None + assert async_created_chat_link.to_dict().get("uri") == chat_link_data["uri"] + + +async def test_update_chat_link_name(async_chat_links_service, async_created_chat_link, short_uuid): + new_name = f"e2e updated link - {short_uuid}" + + result = await async_chat_links_service.update( + async_created_chat_link.id, + {"name": new_name}, + ) + + assert result.id == async_created_chat_link.id + assert result.to_dict().get("name") == new_name + + +async def test_delete_chat_link(async_chat_links_service, async_created_chat_link): + result = async_created_chat_link + + await async_chat_links_service.delete(result.id) + + +async def test_update_chat_link_not_found(async_chat_links_service, invalid_chat_link_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + await async_chat_links_service.update( + invalid_chat_link_id, + {"name": "updated name"}, + ) + + +async def test_delete_chat_link_not_found(async_chat_links_service, invalid_chat_link_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + await async_chat_links_service.delete(invalid_chat_link_id) diff --git a/tests/e2e/helpdesk/chats/links/test_sync_links.py b/tests/e2e/helpdesk/chats/links/test_sync_links.py new file mode 100644 index 0000000..0f06631 --- /dev/null +++ b/tests/e2e/helpdesk/chats/links/test_sync_links.py @@ -0,0 +1,41 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError + +pytestmark = [pytest.mark.flaky] + + +def test_list_chat_links(chat_links_service): + result = chat_links_service.fetch_page(limit=1) + + assert len(result) > 0 + + +def test_create_chat_link(created_chat_link, chat_link_data): # noqa: AAA01 + assert created_chat_link.id is not None + assert created_chat_link.to_dict().get("uri") == chat_link_data["uri"] + + +def test_update_chat_link_name(chat_links_service, created_chat_link, short_uuid): + new_name = f"e2e updated link - {short_uuid}" + + result = chat_links_service.update(created_chat_link.id, {"name": new_name}) + + assert result.id == created_chat_link.id + assert result.to_dict().get("name") == new_name + + +def test_delete_chat_link(chat_links_service, created_chat_link): + result = created_chat_link + + chat_links_service.delete(result.id) + + +def test_update_chat_link_not_found(chat_links_service, invalid_chat_link_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + chat_links_service.update(invalid_chat_link_id, {"name": "updated name"}) + + +def test_delete_chat_link_not_found(chat_links_service, invalid_chat_link_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + chat_links_service.delete(invalid_chat_link_id) diff --git a/tests/unit/resources/helpdesk/test_chat_links.py b/tests/unit/resources/helpdesk/test_chat_links.py new file mode 100644 index 0000000..0300eee --- /dev/null +++ b/tests/unit/resources/helpdesk/test_chat_links.py @@ -0,0 +1,46 @@ +import pytest + +from mpt_api_client.resources.helpdesk.chat_links import ( + AsyncChatLinksService, + ChatLinksService, +) + + +@pytest.fixture +def chat_links_service(http_client) -> ChatLinksService: + return ChatLinksService( + http_client=http_client, endpoint_params={"chat_id": "CHT-0000-0000-0001"} + ) + + +@pytest.fixture +def async_chat_links_service(async_http_client) -> AsyncChatLinksService: + return AsyncChatLinksService( + http_client=async_http_client, endpoint_params={"chat_id": "CHT-0000-0000-0001"} + ) + + +def test_endpoint(chat_links_service) -> None: + result = chat_links_service.path == "/public/v1/helpdesk/chats/CHT-0000-0000-0001/links" + + assert result is True + + +def test_async_endpoint(async_chat_links_service) -> None: + result = async_chat_links_service.path == "/public/v1/helpdesk/chats/CHT-0000-0000-0001/links" + + assert result is True + + +@pytest.mark.parametrize("method", ["create", "update", "delete", "iterate"]) +def test_methods_present(chat_links_service, method: str) -> None: + result = hasattr(chat_links_service, method) + + assert result is True + + +@pytest.mark.parametrize("method", ["create", "update", "delete", "iterate"]) +def test_async_methods_present(async_chat_links_service, method: str) -> None: + result = hasattr(async_chat_links_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 7ad72a9..9424648 100644 --- a/tests/unit/resources/helpdesk/test_chats.py +++ b/tests/unit/resources/helpdesk/test_chats.py @@ -1,5 +1,9 @@ import pytest +from mpt_api_client.resources.helpdesk.chat_links import ( + AsyncChatLinksService, + ChatLinksService, +) from mpt_api_client.resources.helpdesk.chat_messages import ( AsyncChatMessagesService, ChatMessagesService, @@ -37,15 +41,29 @@ def test_async_mixins_present(async_chats_service, method): assert result is True -def test_messages_service(chats_service): - result = chats_service.messages("CHT-0000-0000-0001") +@pytest.mark.parametrize( + ("service_method", "expected_service_class"), + [ + ("messages", ChatMessagesService), + ("links", ChatLinksService), + ], +) +def test_property_services(chats_service, service_method, expected_service_class): + result = getattr(chats_service, service_method)("CHT-0000-0000-0001") - assert isinstance(result, ChatMessagesService) + assert isinstance(result, expected_service_class) assert result.endpoint_params == {"chat_id": "CHT-0000-0000-0001"} -def test_async_messages_service(async_chats_service): - result = async_chats_service.messages("CHT-0000-0000-0001") +@pytest.mark.parametrize( + ("service_method", "expected_service_class"), + [ + ("messages", AsyncChatMessagesService), + ("links", AsyncChatLinksService), + ], +) +def test_async_property_services(async_chats_service, service_method, expected_service_class): + result = getattr(async_chats_service, service_method)("CHT-0000-0000-0001") - assert isinstance(result, AsyncChatMessagesService) + assert isinstance(result, expected_service_class) assert result.endpoint_params == {"chat_id": "CHT-0000-0000-0001"}