From 1a287eb3d44039785f396c52b6fd810b8cdcc989 Mon Sep 17 00:00:00 2001 From: Albert Sola Date: Fri, 19 Dec 2025 12:27:22 +0000 Subject: [PATCH] MPT-16512 E2E for Notification batch attachments --- e2e_config.test.json | 1 + mpt_api_client/http/async_client.py | 1 - .../resources/notifications/batches.py | 43 ++++++- tests/e2e/notifications/batch/conftest.py | 5 + .../notifications/batch/test_async_batches.py | 16 ++- tests/e2e/notifications/batch/test_batches.py | 16 ++- tests/unit/http/test_async_client.py | 2 - .../resources/notifications/test_batches.py | 111 +++++++++++++++++- 8 files changed, 174 insertions(+), 21 deletions(-) diff --git a/e2e_config.test.json b/e2e_config.test.json index deb30fa..c3b6fe1 100644 --- a/e2e_config.test.json +++ b/e2e_config.test.json @@ -47,6 +47,7 @@ "commerce.subscription.agreement.id": "AGR-2473-3299-1721", "commerce.subscription.id": "SUB-3678-1831-2188", "commerce.subscription.product.item.id": "ITM-1767-7355-0001", + "notifications.batch.attachment.id": "ATT-7183-1965-7758", "notifications.batch.id": "MST-3638-2460-4825", "notifications.category.id": "NTC-6157-0397", "notifications.subscriber.id": "NTS-0829-7123-7123", diff --git a/mpt_api_client/http/async_client.py b/mpt_api_client/http/async_client.py index 7881af8..71be9dc 100644 --- a/mpt_api_client/http/async_client.py +++ b/mpt_api_client/http/async_client.py @@ -48,7 +48,6 @@ def __init__( base_headers = { "User-Agent": "swo-marketplace-client/1.0", "Authorization": f"Bearer {api_token}", - "Accept": "application/json", } self.httpx_client = AsyncClient( base_url=base_url, diff --git a/mpt_api_client/resources/notifications/batches.py b/mpt_api_client/resources/notifications/batches.py index 80bdd4e..654024a 100644 --- a/mpt_api_client/resources/notifications/batches.py +++ b/mpt_api_client/resources/notifications/batches.py @@ -14,6 +14,10 @@ class Batch(Model): """Notifications Batch resource.""" +class BatchAttachment(Model): + """Notifications Batch Attachment resource.""" + + class BatchesServiceConfig: """Notifications Batches service configuration.""" @@ -33,9 +37,27 @@ class BatchesService( ): """Notifications Batches service.""" - def get_batch_attachment(self, batch_id: str, attachment_id: str) -> FileModel: + def get_attachment(self, batch_id: str, attachment_id: str) -> BatchAttachment: """Get batch attachment. + Args: + batch_id: Batch ID. + attachment_id: Attachment ID. + + Returns: + BatchAttachment containing the attachment data. + """ + response = self.http_client.request( + "get", + f"{self.path}/{batch_id}/attachments/{attachment_id}", + headers={"Accept": "application/json"}, + ) + + return BatchAttachment.from_response(response) + + def download_attachment(self, batch_id: str, attachment_id: str) -> FileModel: + """Download batch attachment. + Args: batch_id: Batch ID. attachment_id: Attachment ID. @@ -59,9 +81,26 @@ class AsyncBatchesService( ): """Async Notifications Batches service.""" - async def get_batch_attachment(self, batch_id: str, attachment_id: str) -> FileModel: + async def get_attachment(self, batch_id: str, attachment_id: str) -> BatchAttachment: """Get batch attachment. + Args: + batch_id: Batch ID. + attachment_id: Attachment ID. + + Returns: + BatchAttachment containing the attachment data. + """ + response = await self.http_client.request( + "get", + f"{self.path}/{batch_id}/attachments/{attachment_id}", + headers={"Accept": "application/json"}, + ) + return BatchAttachment.from_response(response) + + async def download_attachment(self, batch_id: str, attachment_id: str) -> FileModel: + """Download batch attachment. + Args: batch_id: Batch ID. attachment_id: Attachment ID. diff --git a/tests/e2e/notifications/batch/conftest.py b/tests/e2e/notifications/batch/conftest.py index 4727827..d25e437 100644 --- a/tests/e2e/notifications/batch/conftest.py +++ b/tests/e2e/notifications/batch/conftest.py @@ -24,3 +24,8 @@ def batch_data(category_id, short_uuid): "body": "Hello world", "contacts": [{"email": f"{short_uuid}@example.com"}], } + + +@pytest.fixture +def batch_attachment_id(e2e_config): + return e2e_config["notifications.batch.attachment.id"] diff --git a/tests/e2e/notifications/batch/test_async_batches.py b/tests/e2e/notifications/batch/test_async_batches.py index a5d4d4e..31950a2 100644 --- a/tests/e2e/notifications/batch/test_async_batches.py +++ b/tests/e2e/notifications/batch/test_async_batches.py @@ -11,7 +11,7 @@ async def test_create_batch(async_batch_service, batch_data): async def test_get_batch(async_batch_service, batch_id): - result = await async_batch_service.get(batch_id) + result = await async_batch_service.get(batch_id, select=["attachments"]) assert result.id == batch_id @@ -30,7 +30,13 @@ async def test_create_batch_with_file(async_batch_service, batch_data, logo_fd): assert result is not None -@pytest.mark.skip(reason="Batches attachments not implemented") -async def test_download_attachment(): - # TODO - Implement get and download E2E tests for attachments - raise NotImplementedError +async def test_download_attachment(async_batch_service, batch_id, batch_attachment_id): + result = await async_batch_service.download_attachment(batch_id, batch_attachment_id) + + assert result.filename == "logo.png" + + +async def test_get_attachment(async_batch_service, batch_id, batch_attachment_id): + result = await async_batch_service.get_attachment(batch_id, batch_attachment_id) + + assert result.id == batch_attachment_id diff --git a/tests/e2e/notifications/batch/test_batches.py b/tests/e2e/notifications/batch/test_batches.py index ced54c4..f3afc0a 100644 --- a/tests/e2e/notifications/batch/test_batches.py +++ b/tests/e2e/notifications/batch/test_batches.py @@ -11,7 +11,7 @@ def test_create_batch(batch_service, batch_data): def test_get_batch(batch_service, batch_id): - result = batch_service.get(batch_id) + result = batch_service.get(batch_id, select=["attachments"]) assert result.id == batch_id @@ -30,7 +30,13 @@ def test_create_batch_with_file(batch_service, batch_data, logo_fd): assert result is not None -@pytest.mark.skip(reason="Batches attachments not implemented") # noqa: AAA01 -def test_download_attachment(): - # TODO - Implement get and download E2E tests for attachment - raise NotImplementedError +def test_download_attachment(batch_service, batch_id, batch_attachment_id): + result = batch_service.download_attachment(batch_id, batch_attachment_id) + + assert result.filename == "logo.png" + + +def test_get_attachment(batch_service, batch_id, batch_attachment_id): + result = batch_service.get_attachment(batch_id, batch_attachment_id) + + assert result.id == batch_attachment_id diff --git a/tests/unit/http/test_async_client.py b/tests/unit/http/test_async_client.py index 7e823a8..b578c89 100644 --- a/tests/unit/http/test_async_client.py +++ b/tests/unit/http/test_async_client.py @@ -31,7 +31,6 @@ def test_async_http_initialization(mocker): headers={ "User-Agent": "swo-marketplace-client/1.0", "Authorization": "Bearer test-token", - "Accept": "application/json", }, timeout=10.0, transport=mocker.ANY, @@ -51,7 +50,6 @@ def test_async_env_initialization(monkeypatch, mocker): headers={ "User-Agent": "swo-marketplace-client/1.0", "Authorization": f"Bearer {API_TOKEN}", - "Accept": "application/json", }, timeout=10.0, transport=mocker.ANY, diff --git a/tests/unit/resources/notifications/test_batches.py b/tests/unit/resources/notifications/test_batches.py index ae97844..b315247 100644 --- a/tests/unit/resources/notifications/test_batches.py +++ b/tests/unit/resources/notifications/test_batches.py @@ -1,30 +1,129 @@ import pytest +from mpt_api_client.http import AsyncHTTPClient, HTTPClient +from mpt_api_client.http.types import Response +from mpt_api_client.models import FileModel from mpt_api_client.resources.notifications.batches import ( AsyncBatchesService, + BatchAttachment, BatchesService, ) @pytest.fixture -def batches_service(http_client): +def batches_service(http_client: HTTPClient) -> BatchesService: return BatchesService(http_client=http_client) @pytest.fixture -def async_batches_service(async_http_client): +def async_batches_service(async_http_client: AsyncHTTPClient) -> AsyncBatchesService: return AsyncBatchesService(http_client=async_http_client) -@pytest.mark.parametrize("method", ["get", "create", "iterate", "get_batch_attachment"]) -def test_sync_batches_service_methods(batches_service, method): +@pytest.fixture +def attachment_response() -> Response: + return Response( + headers={"content-disposition": 'attachment; filename="test.csv"'}, + status_code=200, + content=b"content", + ) + + +@pytest.fixture +def batch_attachment_response() -> Response: + return Response( + headers={"Content-Type": "application/json"}, + status_code=200, + content=b'{"id": "A-123", "name": "test.csv"}', + ) + + +@pytest.mark.parametrize( + "method", ["get", "create", "iterate", "download_attachment", "get_attachment"] +) +def test_sync_batches_service_methods(batches_service: BatchesService, method: str) -> None: result = hasattr(batches_service, method) assert result is True -@pytest.mark.parametrize("method", ["get", "create", "iterate", "get_batch_attachment"]) -def test_async_batches_service_methods(async_batches_service, method): +@pytest.mark.parametrize( + "method", ["get", "create", "iterate", "download_attachment", "get_attachment"] +) +def test_async_batches_service_methods( + async_batches_service: AsyncBatchesService, method: str +) -> None: result = hasattr(async_batches_service, method) assert result is True + + +def test_get_attachment(mocker, batches_service, batch_attachment_response) -> None: + batch_id = "B-123" + attachment_id = "A-123" + mock_request = mocker.patch.object( + batches_service.http_client, "request", return_value=batch_attachment_response + ) + + result = batches_service.get_attachment(batch_id, attachment_id) + + assert isinstance(result, BatchAttachment) + assert result.id == "A-123" + mock_request.assert_called_once_with( + "get", + f"/public/v1/notifications/batches/{batch_id}/attachments/{attachment_id}", + headers={"Accept": "application/json"}, + ) + + +async def test_async_get_attachment( + mocker, async_batches_service, batch_attachment_response +) -> None: + batch_id = "B-123" + attachment_id = "A-123" + mock_request = mocker.patch.object( + async_batches_service.http_client, "request", return_value=batch_attachment_response + ) + + result = await async_batches_service.get_attachment(batch_id, attachment_id) + + assert isinstance(result, BatchAttachment) + assert result.id == "A-123" + mock_request.assert_called_once_with( + "get", + f"/public/v1/notifications/batches/{batch_id}/attachments/{attachment_id}", + headers={"Accept": "application/json"}, + ) + + +def test_download_attachment(mocker, batches_service, attachment_response) -> None: + batch_id = "B-123" + attachment_id = "A-123" + mock_request = mocker.patch.object( + batches_service.http_client, "request", return_value=attachment_response + ) + + result = batches_service.download_attachment(batch_id, attachment_id) + + assert isinstance(result, FileModel) + mock_request.assert_called_once_with( + "get", f"/public/v1/notifications/batches/{batch_id}/attachments/{attachment_id}" + ) + + +async def test_async_download_attachment( # noqa: WPS210 + mocker, async_batches_service, attachment_response +) -> None: + batch_id = "B-123" + attachment_id = "A-123" + mock_request = mocker.patch.object( + async_batches_service.http_client, "request", return_value=attachment_response + ) + + result = await async_batches_service.download_attachment(batch_id, attachment_id) + + assert isinstance(result, FileModel) + + mock_request.assert_called_once_with( + "get", f"/public/v1/notifications/batches/{batch_id}/attachments/{attachment_id}" + )