Skip to content

Commit 91901a2

Browse files
authored
[MPT-14914] Added e2e tests and updated endpoints for billing journal attachments (#174)
Added e2e tests and updated endpoints for billing journal attachments https://softwareone.atlassian.net/browse/MPT-14914 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Added comprehensive end-to-end tests (sync + async) for journal attachments: retrieval (including 404), listing, pagination, filtering, create/update/delete, download; new fixtures and an invalid-ID fixture; unit tests extended to cover iterate/download. * **Refactor** * Simplified journal attachments service into focused upload/download/update/delete operations and explicit upload payload keys. * **Chores** * Added a test config entry for attachment ID and updated linting per-file ignores. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2 parents 78d5e8f + 38a2e56 commit 91901a2

File tree

7 files changed

+240
-11
lines changed

7 files changed

+240
-11
lines changed

e2e_config.test.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"accounts.user.id": "USR-9673-3314",
1313
"accounts.user_group.id": "UGR-6822-0561",
1414
"audit.record.id": "AUD-3748-4760-1006-3938",
15+
"billing.journal.attachment.id": "JOA-6425-9776",
1516
"billing.journal.id": "BJO-6562-0928",
1617
"catalog.authorization.id": "AUT-9288-6146",
1718
"catalog.listing.id": "LST-5489-0806",

mpt_api_client/resources/billing/journal_attachments.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
from mpt_api_client.http import AsyncService, Service
22
from mpt_api_client.http.mixins import (
33
AsyncCollectionMixin,
4-
AsyncFilesOperationsMixin,
5-
AsyncModifiableResourceMixin,
4+
AsyncCreateFileMixin,
5+
AsyncDeleteMixin,
6+
AsyncDownloadFileMixin,
7+
AsyncGetMixin,
8+
AsyncUpdateMixin,
69
CollectionMixin,
7-
FilesOperationsMixin,
8-
ModifiableResourceMixin,
10+
CreateFileMixin,
11+
DeleteMixin,
12+
DownloadFileMixin,
13+
GetMixin,
14+
UpdateMixin,
915
)
1016
from mpt_api_client.models import Model
1117

@@ -20,11 +26,16 @@ class JournalAttachmentsServiceConfig:
2026
_endpoint = "/public/v1/billing/journals/{journal_id}/attachments"
2127
_model_class = JournalAttachment
2228
_collection_key = "data"
29+
_upload_file_key = "file"
30+
_upload_data_key = "attachment"
2331

2432

2533
class JournalAttachmentsService(
26-
FilesOperationsMixin[JournalAttachment],
27-
ModifiableResourceMixin[JournalAttachment],
34+
CreateFileMixin[JournalAttachment],
35+
UpdateMixin[JournalAttachment],
36+
DownloadFileMixin[JournalAttachment],
37+
DeleteMixin,
38+
GetMixin[JournalAttachment],
2839
CollectionMixin[JournalAttachment],
2940
Service[JournalAttachment],
3041
JournalAttachmentsServiceConfig,
@@ -33,8 +44,11 @@ class JournalAttachmentsService(
3344

3445

3546
class AsyncJournalAttachmentsService(
36-
AsyncFilesOperationsMixin[JournalAttachment],
37-
AsyncModifiableResourceMixin[JournalAttachment],
47+
AsyncCreateFileMixin[JournalAttachment],
48+
AsyncUpdateMixin[JournalAttachment],
49+
AsyncDownloadFileMixin[JournalAttachment],
50+
AsyncDeleteMixin,
51+
AsyncGetMixin[JournalAttachment],
3852
AsyncCollectionMixin[JournalAttachment],
3953
AsyncService[JournalAttachment],
4054
JournalAttachmentsServiceConfig,

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ per-file-ignores = [
119119
"mpt_api_client/mpt_client.py: WPS214 WPS235",
120120
"mpt_api_client/resources/*: WPS215",
121121
"mpt_api_client/resources/accounts/*.py: WPS202 WPS215 WPS214 WPS235 WPS453",
122-
"mpt_api_client/resources/billing/*.py: WPS202 WPS204 WPS214 WPS215",
122+
"mpt_api_client/resources/billing/*.py: WPS202 WPS204 WPS214 WPS215 WPS235",
123123
"mpt_api_client/resources/catalog/*.py: WPS110 WPS214 WPS215 WPS235",
124124
"mpt_api_client/resources/catalog/mixins.py: WPS110 WPS202 WPS214 WPS215 WPS235",
125125
"mpt_api_client/resources/catalog/products.py: WPS204 WPS214 WPS215 WPS235",
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import pytest
2+
3+
4+
@pytest.fixture
5+
def invalid_journal_attachment_id():
6+
return "JOA-0000-0000"
7+
8+
9+
@pytest.fixture
10+
def journal_attachment_id(e2e_config):
11+
return e2e_config["billing.journal.attachment.id"]
12+
13+
14+
@pytest.fixture
15+
def journal_attachment_factory():
16+
def factory(
17+
name: str = "E2E Created Journal Attachment",
18+
):
19+
return {
20+
"name": name,
21+
"description": name,
22+
}
23+
24+
return factory
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import pytest
2+
3+
from mpt_api_client.exceptions import MPTAPIError
4+
from mpt_api_client.rql.query_builder import RQLQuery
5+
6+
pytestmark = [pytest.mark.flaky]
7+
8+
9+
@pytest.fixture
10+
async def created_journal_attachment(
11+
async_mpt_ops, journal_attachment_factory, billing_journal_id, pdf_fd
12+
):
13+
new_journal_attachment_request_data = journal_attachment_factory(
14+
name="E2E Created Journal Attachment",
15+
)
16+
journal_attachments = async_mpt_ops.billing.journals.attachments(billing_journal_id)
17+
18+
created_journal = await journal_attachments.create(
19+
new_journal_attachment_request_data, file=pdf_fd
20+
)
21+
22+
yield created_journal
23+
24+
try:
25+
await journal_attachments.delete(created_journal.id)
26+
except MPTAPIError as error:
27+
print("TEARDOWN - Unable to delete journal attachment: %s", error.title) # noqa: WPS421
28+
29+
30+
@pytest.fixture
31+
def journal_attachments(async_mpt_ops, billing_journal_id):
32+
return async_mpt_ops.billing.journals.attachments(billing_journal_id)
33+
34+
35+
async def test_get_journal_attachment_by_id(journal_attachments, journal_attachment_id):
36+
result = await journal_attachments.get(journal_attachment_id)
37+
38+
assert result is not None
39+
40+
41+
async def test_get_journal_attachment_by_id_not_found(
42+
journal_attachments, invalid_journal_attachment_id
43+
):
44+
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
45+
await journal_attachments.get(invalid_journal_attachment_id)
46+
47+
48+
async def test_list_journal_attachments(journal_attachments):
49+
limit = 10
50+
51+
result = await journal_attachments.fetch_page(limit=limit)
52+
53+
assert len(result) > 0
54+
55+
56+
async def test_filter_journal_attachments(journal_attachments, journal_attachment_id):
57+
select_fields = ["-description"]
58+
filtered_attachments = (
59+
journal_attachments.filter(RQLQuery(id=journal_attachment_id))
60+
.filter(RQLQuery(name="E2E Seeded Billing Journal Attachment"))
61+
.select(*select_fields)
62+
)
63+
64+
result = [filtered_attachment async for filtered_attachment in filtered_attachments.iterate()]
65+
66+
assert len(result) == 1
67+
68+
69+
def test_create_billing_journal_attachment(created_journal_attachment):
70+
result = created_journal_attachment
71+
72+
assert result is not None
73+
74+
75+
async def test_update_journal_attachment(journal_attachments, created_journal_attachment):
76+
updated_name = "E2E Updated Journal Attachment Name"
77+
updated_attachment_data = {
78+
"name": updated_name,
79+
"description": updated_name,
80+
}
81+
82+
result = await journal_attachments.update(
83+
created_journal_attachment.id, updated_attachment_data
84+
)
85+
86+
assert result is not None
87+
88+
89+
async def test_delete_journal_attachment(journal_attachments, created_journal_attachment):
90+
result = created_journal_attachment
91+
92+
await journal_attachments.delete(result.id)
93+
94+
95+
async def test_download_journal_attachment(journal_attachments, journal_attachment_id):
96+
result = await journal_attachments.download(journal_attachment_id)
97+
98+
assert result.file_contents is not None
99+
assert result.filename is not None
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import pytest
2+
3+
from mpt_api_client.exceptions import MPTAPIError
4+
from mpt_api_client.rql.query_builder import RQLQuery
5+
6+
pytestmark = [pytest.mark.flaky]
7+
8+
9+
@pytest.fixture
10+
def created_journal_attachment(mpt_ops, journal_attachment_factory, billing_journal_id, pdf_fd):
11+
new_journal_attachment_request_data = journal_attachment_factory(
12+
name="E2E Created Journal Attachment",
13+
)
14+
journal_attachments = mpt_ops.billing.journals.attachments(billing_journal_id)
15+
16+
created_journal = journal_attachments.create(new_journal_attachment_request_data, file=pdf_fd)
17+
18+
yield created_journal
19+
20+
try:
21+
journal_attachments.delete(created_journal.id)
22+
except MPTAPIError as error:
23+
print("TEARDOWN - Unable to delete journal attachment: %s", error.title) # noqa: WPS421
24+
25+
26+
@pytest.fixture
27+
def journal_attachments(mpt_ops, billing_journal_id):
28+
return mpt_ops.billing.journals.attachments(billing_journal_id)
29+
30+
31+
def test_get_journal_attachment_by_id(journal_attachments, journal_attachment_id):
32+
result = journal_attachments.get(journal_attachment_id)
33+
34+
assert result is not None
35+
36+
37+
def test_get_journal_attachment_by_id_not_found(journal_attachments, invalid_journal_attachment_id):
38+
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
39+
journal_attachments.get(invalid_journal_attachment_id)
40+
41+
42+
def test_list_journal_attachments(journal_attachments):
43+
limit = 10
44+
45+
result = journal_attachments.fetch_page(limit=limit)
46+
47+
assert len(result) > 0
48+
49+
50+
def test_filter_journal_attachments(journal_attachments, journal_attachment_id):
51+
select_fields = ["-description"]
52+
filtered_attachments = (
53+
journal_attachments.filter(RQLQuery(id=journal_attachment_id))
54+
.filter(RQLQuery(name="E2E Seeded Billing Journal Attachment"))
55+
.select(*select_fields)
56+
)
57+
58+
result = list(filtered_attachments.iterate())
59+
60+
assert len(result) == 1
61+
62+
63+
def test_create_billing_journal_attachment(created_journal_attachment):
64+
result = created_journal_attachment
65+
66+
assert result is not None
67+
68+
69+
def test_update_journal_attachment(journal_attachments, created_journal_attachment):
70+
updated_name = "E2E Updated Journal Attachment Name"
71+
updated_attachment_data = {
72+
"name": updated_name,
73+
"description": updated_name,
74+
}
75+
76+
result = journal_attachments.update(created_journal_attachment.id, updated_attachment_data)
77+
78+
assert result is not None
79+
80+
81+
def test_delete_journal_attachment(journal_attachments, created_journal_attachment):
82+
result = created_journal_attachment
83+
84+
journal_attachments.delete(result.id)
85+
86+
87+
def test_download_journal_attachment(journal_attachments, journal_attachment_id):
88+
result = journal_attachments.download(journal_attachment_id)
89+
90+
assert result.file_contents is not None
91+
assert result.filename is not None

tests/unit/resources/billing/test_journal_attachments.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ def test_async_endpoint(async_journal_attachments_service) -> None:
3737
assert result is True
3838

3939

40-
@pytest.mark.parametrize("method", ["get", "create", "update", "delete"])
40+
@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"])
4141
def test_methods_present(journal_attachments_service, method: str) -> None:
4242
result = hasattr(journal_attachments_service, method)
4343

4444
assert result is True
4545

4646

47-
@pytest.mark.parametrize("method", ["get", "create", "update", "delete"])
47+
@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"])
4848
def test_async_methods_present(async_journal_attachments_service, method: str) -> None:
4949
result = hasattr(async_journal_attachments_service, method)
5050

0 commit comments

Comments
 (0)