From 38a2e5681d768bd1e1ecd27c15f92266c8c62bf7 Mon Sep 17 00:00:00 2001 From: Robert Segal Date: Mon, 29 Dec 2025 16:27:04 -0700 Subject: [PATCH] Added e2e tests and updated endpoints for billing journal attachments --- e2e_config.test.json | 1 + .../resources/billing/journal_attachments.py | 30 ++++-- pyproject.toml | 2 +- .../billing/journal/attachment/conftest.py | 24 +++++ .../test_async_journal_attachment.py | 99 +++++++++++++++++++ .../test_sync_journal_attachment.py | 91 +++++++++++++++++ .../billing/test_journal_attachments.py | 4 +- 7 files changed, 240 insertions(+), 11 deletions(-) create mode 100644 tests/e2e/billing/journal/attachment/conftest.py create mode 100644 tests/e2e/billing/journal/attachment/test_async_journal_attachment.py create mode 100644 tests/e2e/billing/journal/attachment/test_sync_journal_attachment.py diff --git a/e2e_config.test.json b/e2e_config.test.json index 49b29034..a61e5d35 100644 --- a/e2e_config.test.json +++ b/e2e_config.test.json @@ -12,6 +12,7 @@ "accounts.user.id": "USR-9673-3314", "accounts.user_group.id": "UGR-6822-0561", "audit.record.id": "AUD-3748-4760-1006-3938", + "billing.journal.attachment.id": "JOA-6425-9776", "billing.journal.id": "BJO-6562-0928", "catalog.authorization.id": "AUT-9288-6146", "catalog.listing.id": "LST-5489-0806", diff --git a/mpt_api_client/resources/billing/journal_attachments.py b/mpt_api_client/resources/billing/journal_attachments.py index fcf81a1d..f9ee3599 100644 --- a/mpt_api_client/resources/billing/journal_attachments.py +++ b/mpt_api_client/resources/billing/journal_attachments.py @@ -1,11 +1,17 @@ from mpt_api_client.http import AsyncService, Service from mpt_api_client.http.mixins import ( AsyncCollectionMixin, - AsyncFilesOperationsMixin, - AsyncModifiableResourceMixin, + AsyncCreateFileMixin, + AsyncDeleteMixin, + AsyncDownloadFileMixin, + AsyncGetMixin, + AsyncUpdateMixin, CollectionMixin, - FilesOperationsMixin, - ModifiableResourceMixin, + CreateFileMixin, + DeleteMixin, + DownloadFileMixin, + GetMixin, + UpdateMixin, ) from mpt_api_client.models import Model @@ -20,11 +26,16 @@ class JournalAttachmentsServiceConfig: _endpoint = "/public/v1/billing/journals/{journal_id}/attachments" _model_class = JournalAttachment _collection_key = "data" + _upload_file_key = "file" + _upload_data_key = "attachment" class JournalAttachmentsService( - FilesOperationsMixin[JournalAttachment], - ModifiableResourceMixin[JournalAttachment], + CreateFileMixin[JournalAttachment], + UpdateMixin[JournalAttachment], + DownloadFileMixin[JournalAttachment], + DeleteMixin, + GetMixin[JournalAttachment], CollectionMixin[JournalAttachment], Service[JournalAttachment], JournalAttachmentsServiceConfig, @@ -33,8 +44,11 @@ class JournalAttachmentsService( class AsyncJournalAttachmentsService( - AsyncFilesOperationsMixin[JournalAttachment], - AsyncModifiableResourceMixin[JournalAttachment], + AsyncCreateFileMixin[JournalAttachment], + AsyncUpdateMixin[JournalAttachment], + AsyncDownloadFileMixin[JournalAttachment], + AsyncDeleteMixin, + AsyncGetMixin[JournalAttachment], AsyncCollectionMixin[JournalAttachment], AsyncService[JournalAttachment], JournalAttachmentsServiceConfig, diff --git a/pyproject.toml b/pyproject.toml index ca25e7d8..e8d1c03d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -119,7 +119,7 @@ per-file-ignores = [ "mpt_api_client/mpt_client.py: WPS214 WPS235", "mpt_api_client/resources/*: WPS215", "mpt_api_client/resources/accounts/*.py: WPS202 WPS215 WPS214 WPS235 WPS453", - "mpt_api_client/resources/billing/*.py: WPS202 WPS204 WPS214 WPS215", + "mpt_api_client/resources/billing/*.py: WPS202 WPS204 WPS214 WPS215 WPS235", "mpt_api_client/resources/catalog/*.py: WPS110 WPS214 WPS215 WPS235", "mpt_api_client/resources/catalog/mixins.py: WPS110 WPS202 WPS214 WPS215 WPS235", "mpt_api_client/resources/catalog/products.py: WPS204 WPS214 WPS215 WPS235", diff --git a/tests/e2e/billing/journal/attachment/conftest.py b/tests/e2e/billing/journal/attachment/conftest.py new file mode 100644 index 00000000..ea7b49e3 --- /dev/null +++ b/tests/e2e/billing/journal/attachment/conftest.py @@ -0,0 +1,24 @@ +import pytest + + +@pytest.fixture +def invalid_journal_attachment_id(): + return "JOA-0000-0000" + + +@pytest.fixture +def journal_attachment_id(e2e_config): + return e2e_config["billing.journal.attachment.id"] + + +@pytest.fixture +def journal_attachment_factory(): + def factory( + name: str = "E2E Created Journal Attachment", + ): + return { + "name": name, + "description": name, + } + + return factory diff --git a/tests/e2e/billing/journal/attachment/test_async_journal_attachment.py b/tests/e2e/billing/journal/attachment/test_async_journal_attachment.py new file mode 100644 index 00000000..cc4d7b21 --- /dev/null +++ b/tests/e2e/billing/journal/attachment/test_async_journal_attachment.py @@ -0,0 +1,99 @@ +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_journal_attachment( + async_mpt_ops, journal_attachment_factory, billing_journal_id, pdf_fd +): + new_journal_attachment_request_data = journal_attachment_factory( + name="E2E Created Journal Attachment", + ) + journal_attachments = async_mpt_ops.billing.journals.attachments(billing_journal_id) + + created_journal = await journal_attachments.create( + new_journal_attachment_request_data, file=pdf_fd + ) + + yield created_journal + + try: + await journal_attachments.delete(created_journal.id) + except MPTAPIError as error: + print("TEARDOWN - Unable to delete journal attachment: %s", error.title) # noqa: WPS421 + + +@pytest.fixture +def journal_attachments(async_mpt_ops, billing_journal_id): + return async_mpt_ops.billing.journals.attachments(billing_journal_id) + + +async def test_get_journal_attachment_by_id(journal_attachments, journal_attachment_id): + result = await journal_attachments.get(journal_attachment_id) + + assert result is not None + + +async def test_get_journal_attachment_by_id_not_found( + journal_attachments, invalid_journal_attachment_id +): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + await journal_attachments.get(invalid_journal_attachment_id) + + +async def test_list_journal_attachments(journal_attachments): + limit = 10 + + result = await journal_attachments.fetch_page(limit=limit) + + assert len(result) > 0 + + +async def test_filter_journal_attachments(journal_attachments, journal_attachment_id): + select_fields = ["-description"] + filtered_attachments = ( + journal_attachments.filter(RQLQuery(id=journal_attachment_id)) + .filter(RQLQuery(name="E2E Seeded Billing Journal Attachment")) + .select(*select_fields) + ) + + result = [filtered_attachment async for filtered_attachment in filtered_attachments.iterate()] + + assert len(result) == 1 + + +def test_create_billing_journal_attachment(created_journal_attachment): + result = created_journal_attachment + + assert result is not None + + +async def test_update_journal_attachment(journal_attachments, created_journal_attachment): + updated_name = "E2E Updated Journal Attachment Name" + updated_attachment_data = { + "name": updated_name, + "description": updated_name, + } + + result = await journal_attachments.update( + created_journal_attachment.id, updated_attachment_data + ) + + assert result is not None + + +async def test_delete_journal_attachment(journal_attachments, created_journal_attachment): + result = created_journal_attachment + + await journal_attachments.delete(result.id) + + +async def test_download_journal_attachment(journal_attachments, journal_attachment_id): + result = await journal_attachments.download(journal_attachment_id) + + assert result.file_contents is not None + assert result.filename is not None diff --git a/tests/e2e/billing/journal/attachment/test_sync_journal_attachment.py b/tests/e2e/billing/journal/attachment/test_sync_journal_attachment.py new file mode 100644 index 00000000..b3115730 --- /dev/null +++ b/tests/e2e/billing/journal/attachment/test_sync_journal_attachment.py @@ -0,0 +1,91 @@ +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_journal_attachment(mpt_ops, journal_attachment_factory, billing_journal_id, pdf_fd): + new_journal_attachment_request_data = journal_attachment_factory( + name="E2E Created Journal Attachment", + ) + journal_attachments = mpt_ops.billing.journals.attachments(billing_journal_id) + + created_journal = journal_attachments.create(new_journal_attachment_request_data, file=pdf_fd) + + yield created_journal + + try: + journal_attachments.delete(created_journal.id) + except MPTAPIError as error: + print("TEARDOWN - Unable to delete journal attachment: %s", error.title) # noqa: WPS421 + + +@pytest.fixture +def journal_attachments(mpt_ops, billing_journal_id): + return mpt_ops.billing.journals.attachments(billing_journal_id) + + +def test_get_journal_attachment_by_id(journal_attachments, journal_attachment_id): + result = journal_attachments.get(journal_attachment_id) + + assert result is not None + + +def test_get_journal_attachment_by_id_not_found(journal_attachments, invalid_journal_attachment_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + journal_attachments.get(invalid_journal_attachment_id) + + +def test_list_journal_attachments(journal_attachments): + limit = 10 + + result = journal_attachments.fetch_page(limit=limit) + + assert len(result) > 0 + + +def test_filter_journal_attachments(journal_attachments, journal_attachment_id): + select_fields = ["-description"] + filtered_attachments = ( + journal_attachments.filter(RQLQuery(id=journal_attachment_id)) + .filter(RQLQuery(name="E2E Seeded Billing Journal Attachment")) + .select(*select_fields) + ) + + result = list(filtered_attachments.iterate()) + + assert len(result) == 1 + + +def test_create_billing_journal_attachment(created_journal_attachment): + result = created_journal_attachment + + assert result is not None + + +def test_update_journal_attachment(journal_attachments, created_journal_attachment): + updated_name = "E2E Updated Journal Attachment Name" + updated_attachment_data = { + "name": updated_name, + "description": updated_name, + } + + result = journal_attachments.update(created_journal_attachment.id, updated_attachment_data) + + assert result is not None + + +def test_delete_journal_attachment(journal_attachments, created_journal_attachment): + result = created_journal_attachment + + journal_attachments.delete(result.id) + + +def test_download_journal_attachment(journal_attachments, journal_attachment_id): + result = journal_attachments.download(journal_attachment_id) + + assert result.file_contents is not None + assert result.filename is not None diff --git a/tests/unit/resources/billing/test_journal_attachments.py b/tests/unit/resources/billing/test_journal_attachments.py index 644afc27..86cdc1c7 100644 --- a/tests/unit/resources/billing/test_journal_attachments.py +++ b/tests/unit/resources/billing/test_journal_attachments.py @@ -37,14 +37,14 @@ def test_async_endpoint(async_journal_attachments_service) -> None: assert result is True -@pytest.mark.parametrize("method", ["get", "create", "update", "delete"]) +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) def test_methods_present(journal_attachments_service, method: str) -> None: result = hasattr(journal_attachments_service, method) assert result is True -@pytest.mark.parametrize("method", ["get", "create", "update", "delete"]) +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) def test_async_methods_present(async_journal_attachments_service, method: str) -> None: result = hasattr(async_journal_attachments_service, method)