From 5e40b58060bf9ce1527cc5c2cf842045aa3d982c Mon Sep 17 00:00:00 2001 From: Robert Segal Date: Thu, 4 Dec 2025 12:51:36 -0700 Subject: [PATCH] Fixed agreement attachments endpoints, created e2e tests --- e2e_config.test.json | 4 +- .../commerce/agreements_attachments.py | 20 +++-- .../commerce/agreement/attachment/conftest.py | 28 ++++++ .../test_async_agreement_attachment.py | 85 +++++++++++++++++++ .../test_sync_agreement_attachment.py | 85 +++++++++++++++++++ .../commerce/test_agreements_attachments.py | 4 +- 6 files changed, 218 insertions(+), 8 deletions(-) create mode 100644 tests/e2e/commerce/agreement/attachment/conftest.py create mode 100644 tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py create mode 100644 tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py diff --git a/e2e_config.test.json b/e2e_config.test.json index df6a5215..b0b7aa4c 100644 --- a/e2e_config.test.json +++ b/e2e_config.test.json @@ -13,8 +13,8 @@ "accounts.user_group.id": "UGR-6822-0561", "catalog.authorization.id": "AUT-9288-6146", "catalog.listing.id": "LST-5489-0806", - "catalog.product.document.id": "PDC-7255-3950-0001", "catalog.price_list.id": "PRC-7255-3950-0245", + "catalog.product.document.id": "PDC-7255-3950-0001", "catalog.product.id": "PRD-7255-3950", "catalog.product.item.id": "ITM-7255-3950-0751", "catalog.product.item_group.id": "IGR-7255-3950-0001", @@ -24,9 +24,11 @@ "catalog.product.terms.id": "TCS-7255-3950-0001", "catalog.product.terms.variant.id": "TCV-7255-3950-0001-0001", "catalog.unit.id": "UNT-1229", + "commerce.agreement.attachment.id": "ATT-9850-2169-6098-0001", "commerce.agreement.id": "AGR-9850-2169-6098", "commerce.authorization.id": "AUT-0031-2873", "commerce.client.id": "ACC-1086-6867", + "commerce.order.id": "ORD-6969-3541-5426", "commerce.product.id": "PRD-1767-7355", "commerce.product.item.id": "ITM-1767-7355-0001", "commerce.product.listing.id": "LST-5489-0806", diff --git a/mpt_api_client/resources/commerce/agreements_attachments.py b/mpt_api_client/resources/commerce/agreements_attachments.py index 55d3e675..c1cfe30d 100644 --- a/mpt_api_client/resources/commerce/agreements_attachments.py +++ b/mpt_api_client/resources/commerce/agreements_attachments.py @@ -1,13 +1,17 @@ from mpt_api_client.http import AsyncService, Service -from mpt_api_client.http.mixins import ( +from mpt_api_client.http.mixins import ( # noqa: WPS235 AsyncCollectionMixin, + AsyncCreateFileMixin, AsyncDeleteMixin, - AsyncFilesOperationsMixin, + AsyncDownloadFileMixin, AsyncGetMixin, + AsyncUpdateMixin, CollectionMixin, + CreateFileMixin, DeleteMixin, - FilesOperationsMixin, + DownloadFileMixin, GetMixin, + UpdateMixin, ) from mpt_api_client.models import Model @@ -22,10 +26,14 @@ class AgreementsAttachmentServiceConfig: _endpoint = "/public/v1/commerce/agreements/{agreement_id}/attachments" _model_class = AgreementAttachment _collection_key = "data" + _upload_file_key = "file" + _upload_data_key = "attachment" class AgreementsAttachmentService( - FilesOperationsMixin[AgreementAttachment], + CreateFileMixin[AgreementAttachment], + UpdateMixin[AgreementAttachment], + DownloadFileMixin[AgreementAttachment], DeleteMixin, GetMixin[AgreementAttachment], CollectionMixin[AgreementAttachment], @@ -36,7 +44,9 @@ class AgreementsAttachmentService( class AsyncAgreementsAttachmentService( - AsyncFilesOperationsMixin[AgreementAttachment], + AsyncCreateFileMixin[AgreementAttachment], + AsyncUpdateMixin[AgreementAttachment], + AsyncDownloadFileMixin[AgreementAttachment], AsyncDeleteMixin, AsyncGetMixin[AgreementAttachment], AsyncCollectionMixin[AgreementAttachment], diff --git a/tests/e2e/commerce/agreement/attachment/conftest.py b/tests/e2e/commerce/agreement/attachment/conftest.py new file mode 100644 index 00000000..31dcc2d7 --- /dev/null +++ b/tests/e2e/commerce/agreement/attachment/conftest.py @@ -0,0 +1,28 @@ +import pytest + + +@pytest.fixture +def invalid_agreement_attachment_id(): + return "ATT-0000-0000-0000-0000" + + +@pytest.fixture +def agreement_attachment_id(e2e_config): + return e2e_config["commerce.agreement.attachment.id"] + + +@pytest.fixture +def agreement_attachment_factory(): + def factory( + name: str = "E2E Created Agreement Attachment", + attachment_type: str = "File", + license_key: str = "", + ): + return { + "name": name, + "description": name, + "type": attachment_type, + "licenseKey": license_key, + } + + return factory diff --git a/tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py b/tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py new file mode 100644 index 00000000..2071a4ba --- /dev/null +++ b/tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py @@ -0,0 +1,85 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError +from mpt_api_client.rql.query_builder import RQLQuery + +pytestmark = [pytest.mark.flaky] + + +@pytest.fixture +async def created_agreement_attachment(agreement_attachments, agreement_attachment_factory, pdf_fd): + new_agreement_attachment_request_data = agreement_attachment_factory( + name="E2E Created Agreement Attachment", + ) + return await agreement_attachments.create(new_agreement_attachment_request_data, file=pdf_fd) + + +@pytest.fixture +def agreement_attachments(async_mpt_ops, agreement_id): + return async_mpt_ops.commerce.agreements.attachments(agreement_id) + + +async def test_get_agreement_attachment_by_id(agreement_attachments, agreement_attachment_id): + result = await agreement_attachments.get(agreement_attachment_id) + assert result is not None + + +async def test_get_agreement_attachment_by_id_not_found( + agreement_attachments, invalid_agreement_attachment_id +): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + await agreement_attachments.get(invalid_agreement_attachment_id) + + +async def test_list_agreement_attachments(agreement_attachments): + limit = 10 + + result = await agreement_attachments.fetch_page(limit=limit) + + assert len(result) > 0 + + +async def test_filter_agreement_attachments(agreement_attachments, agreement_attachment_id): + select_fields = ["-description"] + filtered_attachments = ( + agreement_attachments.filter(RQLQuery(id=agreement_attachment_id)) + .filter(RQLQuery(name="E2E Seeded Agreement Attachment")) + .select(*select_fields) + ) + + result = [att async for att in filtered_attachments.iterate()] + + assert len(result) == 1 + + +def test_create_agreement_attachment(created_agreement_attachment): + result = created_agreement_attachment + + assert result is not None + + +async def test_update_agreement_attachment(agreement_attachments, created_agreement_attachment): + updated_name = "E2E Updated Agreement Attachment Name" + updated_attachment_data = { + "name": updated_name, + "description": updated_name, + } + attachment = created_agreement_attachment + + result = await agreement_attachments.update(attachment.id, updated_attachment_data) + + assert result is not None + + +async def test_delete_agreement_attachment(agreement_attachments, created_agreement_attachment): + result = created_agreement_attachment + await agreement_attachments.delete(result.id) + + +async def test_download_agreement_attachment(agreement_attachments, created_agreement_attachment): + attachment = created_agreement_attachment + + result = await agreement_attachments.download(attachment.id) + + assert result.file_contents is not None + assert result.filename == "empty.pdf" diff --git a/tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py b/tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py new file mode 100644 index 00000000..8343260b --- /dev/null +++ b/tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py @@ -0,0 +1,85 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError +from mpt_api_client.rql.query_builder import RQLQuery + +pytestmark = [pytest.mark.flaky] + + +@pytest.fixture +def created_agreement_attachment(mpt_ops, agreement_attachment_factory, agreement_id, pdf_fd): + new_agreement_attachment_request_data = agreement_attachment_factory( + name="E2E Created Agreement Attachment", + ) + agreement_attachments = mpt_ops.commerce.agreements.attachments(agreement_id) + return agreement_attachments.create(new_agreement_attachment_request_data, file=pdf_fd) + + +@pytest.fixture +def agreement_attachments(mpt_ops, agreement_id): + return mpt_ops.commerce.agreements.attachments(agreement_id) + + +def test_get_agreement_attachment_by_id(agreement_attachments, agreement_attachment_id): + result = agreement_attachments.get(agreement_attachment_id) + + assert result is not None + + +def test_get_agreement_attachment_by_id_not_found( + agreement_attachments, invalid_agreement_attachment_id +): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + agreement_attachments.get(invalid_agreement_attachment_id) + + +def test_list_agreement_attachments(agreement_attachments): + limit = 10 + + result = agreement_attachments.fetch_page(limit=limit) + + assert len(result) > 0 + + +def test_filter_agreement_attachments(agreement_attachments, agreement_attachment_id): + select_fields = ["-description"] + filtered_attachments = ( + agreement_attachments.filter(RQLQuery(id=agreement_attachment_id)) + .filter(RQLQuery(name="E2E Seeded Agreement Attachment")) + .select(*select_fields) + ) + + result = list(filtered_attachments.iterate()) + + assert len(result) == 1 + + +def test_create_agreement_attachment(created_agreement_attachment): + result = created_agreement_attachment + + assert result is not None + + +def test_update_agreement_attachment(agreement_attachments, created_agreement_attachment): + updated_name = "E2E Updated Agreement Attachment Name" + updated_attachment_data = { + "name": updated_name, + "description": updated_name, + } + + result = agreement_attachments.update(created_agreement_attachment.id, updated_attachment_data) + + assert result is not None + + +def test_delete_agreement_attachment(agreement_attachments, created_agreement_attachment): + result = created_agreement_attachment + + agreement_attachments.delete(result.id) + + +def test_download_agreement_attachment(agreement_attachments, created_agreement_attachment): + result = agreement_attachments.download(created_agreement_attachment.id) + + assert result.file_contents is not None + assert result.filename == "empty.pdf" diff --git a/tests/unit/resources/commerce/test_agreements_attachments.py b/tests/unit/resources/commerce/test_agreements_attachments.py index 20975975..96eb0044 100644 --- a/tests/unit/resources/commerce/test_agreements_attachments.py +++ b/tests/unit/resources/commerce/test_agreements_attachments.py @@ -32,14 +32,14 @@ def test_async_endpoint(async_attachment_service) -> None: assert result is True -@pytest.mark.parametrize("method", ["get", "create", "delete", "download"]) +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) def test_methods_present(attachment_service, method: str) -> None: result = hasattr(attachment_service, method) assert result is True -@pytest.mark.parametrize("method", ["get", "create", "delete", "download"]) +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) def test_async_methods_present(async_attachment_service, method: str) -> None: result = hasattr(async_attachment_service, method)