Skip to content

Conversation

@robcsegal
Copy link
Contributor

@robcsegal robcsegal commented Dec 4, 2025

Fixed agreement attachments endpoints, created e2e tests

https://softwareone.atlassian.net/browse/MPT-14905

Summary by CodeRabbit

  • New Features

    • Agreement attachments now support update in addition to create/read/delete/download.
  • Tests

    • Added end-to-end test coverage (sync + async) for agreement attachment lifecycle: create, retrieve, list, filter, update, delete, and download.
    • Unit tests extended to validate update and iteration behaviors.
  • Chores

    • Added two new public test configuration entries: commerce.agreement.attachment.id and commerce.order.id.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 4, 2025

Walkthrough

Replaced consolidated file-operation mixins with explicit per-operation mixins for agreement attachments, added two commerce-related e2e configuration keys, and introduced new e2e tests and fixtures plus unit-test updates validating the attachment service API surface.

Changes

Cohort / File(s) Change Summary
Configuration updates
e2e_config.test.json
Added two public config keys: commerce.agreement.attachment.id and commerce.order.id. The existing catalog.product.document.id was moved within the file with no value change.
Service mixin refactor
mpt_api_client/resources/commerce/agreements_attachments.py
Replaced FilesOperationsMixin/AsyncFilesOperationsMixin with per-operation mixins: CreateFileMixin/AsyncCreateFileMixin, UpdateMixin/AsyncUpdateMixin, DownloadFileMixin/AsyncDownloadFileMixin; updated imports and service base classes; added AgreementsAttachmentServiceConfig keys _upload_file_key = "file" and _upload_data_key = "attachment".
E2E test fixtures
tests/e2e/commerce/agreement/attachment/conftest.py
Added fixtures: invalid_agreement_attachment_id(), agreement_attachment_id(e2e_config) (reads new config), and agreement_attachment_factory() (factory producing attachment dicts with keys name, description, type, licenseKey).
E2E test suites
tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py, tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py
Added comprehensive async and sync e2e tests covering attachment lifecycle: get (success/404), list/paginate, filter, create, update, delete, download. Modules marked flaky.
Unit test update
tests/unit/resources/commerce/test_agreements_attachments.py
Extended unit tests to assert presence of "update" and iteration-related behavior alongside existing "get", "create", "delete", and "download" checks for sync and async attachment services.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Verify class inheritance/composition changes in mpt_api_client/resources/commerce/agreements_attachments.py.
  • Confirm _upload_file_key and _upload_data_key align with API/form expectations and e2e test usage.
  • Review new e2e fixtures and test flows for flakiness markers and resource cleanup.
  • Ensure unit tests' expectations match the new public API surface.

Suggested reviewers

  • d3rky
  • ruben-sebrango
  • alephsur
  • jentyk
  • svazquezco

Poem

🐰 I nudged the mixins into neat little stacks,
Tests hopped in with files and tiny flak flags.
Two config keys tucked into the nest,
Attachments now behave and pass the quest.
Carrots clapped softly — code well-pressed. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: fixing agreement attachments endpoints and creating e2e tests, which aligns with the changeset modifications.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch MPT-14905-add-e-2-e-tests-for-commerce-agreements-attachments

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Dec 4, 2025

✅ Found Jira issue key in the title: MPT-14905

Generated by 🚫 dangerJS against 5e40b58

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (1)

79-86: Remove duplicate filename assertion in async download test

test_download_agreement_attachment asserts result.filename == "empty.pdf" twice; the second assert is redundant and can be dropped to keep the test concise.

-    assert result.file_contents is not None
-    assert result.filename == "empty.pdf"
-    assert result.filename == "empty.pdf"
+    assert result.file_contents is not None
+    assert result.filename == "empty.pdf"
tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py (1)

72-79: Make pdf_fd a shared, closing fixture

The pdf_fd fixture (opening empty.pdf in "rb") would be safer and more reusable if implemented as a yield fixture that closes the file, and ideally moved into conftest.py so the async tests can depend on the same fixture.

For example:

- def pdf_fd():
-     icon_path = pathlib.Path(__file__).parent / "empty.pdf"
-     return pathlib.Path.open(icon_path, "rb")
+ @pytest.fixture
+ def pdf_fd():
+     icon_path = pathlib.Path(__file__).parent / "empty.pdf"
+     with icon_path.open("rb") as fd:
+         yield fd

Then relocate this fixture into tests/e2e/commerce/agreement/attachment/conftest.py so both sync and async suites can use it.

mpt_api_client/resources/commerce/agreements_attachments.py (2)

2-15: Remove or adjust unused # noqa: WPS235 directive

Static analysis (Ruff) reports RUF100 for # noqa: WPS235 on the mixin import line, meaning that code isn’t recognized by the configured checkers. Unless you rely on a separate tool that actually needs this, it’s better to drop it to avoid confusion.

-from mpt_api_client.http.mixins import (  # noqa: WPS235
+from mpt_api_client.http.mixins import (

If you do still need to silence a specific rule, consider configuring it in your lint setup rather than keeping an apparently unused noqa.


23-25: Docstring refers to “Orders” instead of agreements attachments

AgreementsAttachmentServiceConfig currently has the docstring """Orders service config.""", which is misleading for an agreements attachments service.

-class AgreementsAttachmentServiceConfig:
-    """Orders service config."""
+class AgreementsAttachmentServiceConfig:
+    """Agreements attachments service config."""
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff6b291 and eb10dde.

📒 Files selected for processing (6)
  • e2e_config.test.json (1 hunks)
  • mpt_api_client/resources/commerce/agreements_attachments.py (3 hunks)
  • tests/e2e/commerce/agreement/attachment/conftest.py (1 hunks)
  • tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (1 hunks)
  • tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py (1 hunks)
  • tests/unit/resources/commerce/test_agreements_attachments.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py (6)
mpt_api_client/exceptions.py (1)
  • MPTAPIError (20-42)
mpt_api_client/rql/query_builder.py (1)
  • RQLQuery (115-524)
tests/e2e/conftest.py (2)
  • mpt_ops (31-32)
  • pdf_fd (73-75)
tests/e2e/commerce/agreement/attachment/conftest.py (3)
  • agreement_attachment_factory (15-28)
  • agreement_attachment_id (10-11)
  • invalid_agreement_attachment_id (5-6)
tests/e2e/commerce/conftest.py (1)
  • agreement_id (5-6)
mpt_api_client/models/file_model.py (2)
  • file_contents (35-45)
  • filename (13-32)
tests/e2e/commerce/agreement/attachment/conftest.py (1)
tests/e2e/conftest.py (1)
  • e2e_config (89-92)
🪛 Ruff (0.14.7)
mpt_api_client/resources/commerce/agreements_attachments.py

2-2: Unused noqa directive (unknown: WPS235)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (4)
tests/unit/resources/commerce/test_agreements_attachments.py (1)

35-46: Unit coverage for update method looks correct

Including "update" in the parametrized method list for both sync and async services matches the new UpdateMixin/AsyncUpdateMixin bases and keeps the public API smoke‑tested.

tests/e2e/commerce/agreement/attachment/conftest.py (1)

4-28: Shared attachment fixtures are well structured

The invalid ID, config‑backed ID, and agreement_attachment_factory give the E2E tests a clean, reusable way to construct attachment payloads and IDs; this keeps individual tests focused on behavior rather than setup.

mpt_api_client/resources/commerce/agreements_attachments.py (1)

29-37: Per‑operation mixins and upload keys look consistent with tests

Configuring _upload_file_key = "file" and _upload_data_key = "attachment" plus inheriting CreateFileMixin/AsyncCreateFileMixin, UpdateMixin/AsyncUpdateMixin, and DownloadFileMixin/AsyncDownloadFileMixin lines up with how the tests call create(..., file=pdf_fd) and update(...), and should give you a clear, explicit attachment API surface.

tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (1)

9-20: No action required. The pdf_fd fixture is already defined in tests/e2e/conftest.py and is properly accessible to this async module through pytest's standard fixture scope inheritance. No fixture relocation is necessary.

Likely an incorrect or invalid review comment.

@robcsegal robcsegal force-pushed the MPT-14905-add-e-2-e-tests-for-commerce-agreements-attachments branch from eb10dde to a514d5f Compare December 4, 2025 20:55
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (1)

74-76: Consider adding an assertion to verify deletion.

The test deletes the attachment but doesn't verify that the deletion was successful. Consider adding an assertion that a subsequent get() call raises a 404 error, similar to test_get_agreement_attachment_by_id_not_found.

 async def test_delete_agreement_attachment(agreement_attachments, created_agreement_attachment):
     result = created_agreement_attachment
     await agreement_attachments.delete(result.id)
+
+    with pytest.raises(MPTAPIError, match=r"404 Not Found"):
+        await agreement_attachments.get(result.id)
tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py (2)

9-15: Minor inconsistency with the async counterpart fixture.

This fixture creates agreement_attachments inline, while the async version (line 10 in test_async_agreement_attachment.py) uses the agreement_attachments fixture directly. Consider using the agreement_attachments fixture here as well for consistency between sync and async test modules.

 @pytest.fixture
-def created_agreement_attachment(mpt_ops, agreement_attachment_factory, agreement_id, pdf_fd):
+def created_agreement_attachment(agreement_attachments, agreement_attachment_factory, 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)

75-78: Consider adding an assertion to verify deletion.

Similar to the async test, this test deletes the attachment but doesn't verify that the deletion succeeded. Consider asserting that a subsequent get() call raises a 404 error.

 def test_delete_agreement_attachment(agreement_attachments, created_agreement_attachment):
     result = created_agreement_attachment
 
     agreement_attachments.delete(result.id)
+
+    with pytest.raises(MPTAPIError, match=r"404 Not Found"):
+        agreement_attachments.get(result.id)
mpt_api_client/resources/commerce/agreements_attachments.py (2)

2-15: Re‑align noqa directive with current linting (RUF100 warning).

Ruff reports RUF100 for # noqa: WPS235, which suggests this code is no longer a recognized rule in the active lint config. If you’ve fully migrated to Ruff and are no longer running wemake/Flake8 WPS rules, you can safely drop this noqa to keep the imports clean:

-from mpt_api_client.http.mixins import (  # noqa: WPS235
+from mpt_api_client.http.mixins import (

If you still rely on WPS235 from another tool, consider disabling RUF100 or scoping the noqa differently so the tools don’t fight each other.


23-30: Config upload keys look good; consider fixing misleading docstring.

The new _upload_file_key = "file" and _upload_data_key = "attachment" align well with a typical file + metadata upload shape and should integrate cleanly with the new CreateFileMixin/AsyncCreateFileMixin logic.

The class docstring, however, still states "Orders service config.", which is confusing in an agreements attachments context. Consider updating for clarity:

-class AgreementsAttachmentServiceConfig:
-    """Orders service config."""
+class AgreementsAttachmentServiceConfig:
+    """Agreements attachment service config."""
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb10dde and a514d5f.

📒 Files selected for processing (6)
  • e2e_config.test.json (1 hunks)
  • mpt_api_client/resources/commerce/agreements_attachments.py (3 hunks)
  • tests/e2e/commerce/agreement/attachment/conftest.py (1 hunks)
  • tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (1 hunks)
  • tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py (1 hunks)
  • tests/unit/resources/commerce/test_agreements_attachments.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • e2e_config.test.json
  • tests/e2e/commerce/agreement/attachment/conftest.py
  • tests/unit/resources/commerce/test_agreements_attachments.py
🧰 Additional context used
🧬 Code graph analysis (1)
tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (7)
mpt_api_client/exceptions.py (1)
  • MPTAPIError (20-42)
mpt_api_client/rql/query_builder.py (1)
  • RQLQuery (115-524)
tests/e2e/commerce/agreement/attachment/conftest.py (1)
  • agreement_attachment_factory (15-28)
tests/e2e/conftest.py (1)
  • pdf_fd (73-75)
tests/e2e/commerce/conftest.py (1)
  • agreement_id (5-6)
mpt_api_client/models/model.py (1)
  • id (119-121)
mpt_api_client/models/file_model.py (2)
  • file_contents (35-45)
  • filename (13-32)
🪛 Ruff (0.14.7)
mpt_api_client/resources/commerce/agreements_attachments.py

2-2: Unused noqa directive (unknown: WPS235)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (5)
tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (2)

1-6: Well-structured e2e test coverage for async attachment operations.

The test module provides comprehensive coverage for the agreement attachment lifecycle including get, list, filter, create, update, and download operations. The use of pytest.mark.flaky is appropriate for e2e tests that may have transient failures.

Also applies to: 9-52, 61-85


55-58: Missing async keyword on test function.

test_create_agreement_attachment is a synchronous function but uses created_agreement_attachment, which is an async fixture defined on lines 9-14. This test should be marked async for consistency with the module and to ensure pytest-asyncio handles the fixture correctly.

-def test_create_agreement_attachment(created_agreement_attachment):
+async def test_create_agreement_attachment(created_agreement_attachment):
⛔ Skipped due to learnings
Learnt from: robcsegal
Repo: softwareone-platform/mpt-api-python-client PR: 131
File: tests/e2e/accounts/accounts_users/test_async_accounts_users.py:0-0
Timestamp: 2025-11-27T00:05:54.701Z
Learning: In pytest-asyncio, async fixtures are automatically resolved before being passed to test functions (both sync and async). Test functions receive the yielded value from async fixtures, not coroutines, so fixture parameters should never be awaited. A sync test function can use async fixtures without any special handling.
tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py (1)

1-6: Good parity with async test module.

The sync test module mirrors the async counterpart well, providing equivalent coverage for all attachment operations. The test structure is clean and follows pytest conventions.

Also applies to: 18-72, 81-85

mpt_api_client/resources/commerce/agreements_attachments.py (2)

33-42: Sync service mixin composition matches new per‑operation design.

The switch to explicit CreateFileMixin, UpdateMixin, and DownloadFileMixin on AgreementsAttachmentService alongside DeleteMixin, GetMixin, and CollectionMixin is coherent and makes the capabilities of the service explicit. The generic parameterization with AgreementAttachment and inclusion of AgreementsAttachmentServiceConfig as a base are consistent with the rest of the client’s pattern.


46-55: Async service mirrors sync surface correctly.

AsyncAgreementsAttachmentService mirrors the sync service with AsyncCreateFileMixin, AsyncUpdateMixin, and AsyncDownloadFileMixin plus the async collection/delete/get/service mixins and shared config. This keeps the async API aligned with the sync one and avoids surprises for consumers.

@robcsegal robcsegal force-pushed the MPT-14905-add-e-2-e-tests-for-commerce-agreements-attachments branch 2 times, most recently from b3acd0e to 9518377 Compare December 5, 2025 14:15
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
e2e_config.test.json (1)

16-16: Re-evaluate commerce.order.id until there are E2E consumers

commerce.order.id is defined here but, as previously noted, there still appear to be no E2E fixtures/tests using it. Either add corresponding E2E coverage that reads this key, or remove it from the test config for now to keep it minimal and focused.

🧹 Nitpick comments (3)
mpt_api_client/resources/commerce/agreements_attachments.py (1)

23-30: Clarify service config docstring and acknowledge new upload keys

The new _upload_file_key / _upload_data_key fields make the upload contract explicit and align with the file mixins; the only nit is the outdated docstring mentioning “Orders”.

-class AgreementsAttachmentServiceConfig:
-    """Orders service config."""
+class AgreementsAttachmentServiceConfig:
+    """Agreement attachments service config."""
tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (1)

61-77: Strengthen update/delete assertions for better E2E guarantees

test_update_agreement_attachment only asserts that the response is not None, and test_delete_agreement_attachment only checks that no exception is raised. To catch more regressions, you could assert the actual behavior:

  • After update, assert that the returned object reflects the new values.
  • After delete, assert that a subsequent get yields a 404.

For example:

 async def test_update_agreement_attachment(agreement_attachments, created_agreement_attachment):
@@
-    result = await agreement_attachments.update(attachment.id, updated_attachment_data)
-
-    assert result is not None
+    result = await agreement_attachments.update(attachment.id, updated_attachment_data)
+
+    assert result is not None
+    assert result.name == updated_name
+    assert result.description == updated_name
@@
 async def test_delete_agreement_attachment(agreement_attachments, created_agreement_attachment):
-    result = created_agreement_attachment
-    await agreement_attachments.delete(result.id)
+    result = created_agreement_attachment
+    await agreement_attachments.delete(result.id)
+
+    with pytest.raises(MPTAPIError, match=r"404 Not Found"):
+        await agreement_attachments.get(result.id)
tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py (1)

63-79: Improve update/delete assertions to validate behavior, not just success

As with the async tests, the sync test_update_agreement_attachment only asserts non-None, and test_delete_agreement_attachment only checks that no exception is raised. You can make these tests more meaningful by asserting the observable effects:

 def test_update_agreement_attachment(agreement_attachments, created_agreement_attachment):
@@
-    result = agreement_attachments.update(created_agreement_attachment.id, updated_attachment_data)
-
-    assert result is not None
+    result = agreement_attachments.update(created_agreement_attachment.id, updated_attachment_data)
+
+    assert result is not None
+    assert result.name == updated_name
+    assert result.description == updated_name
@@
 def test_delete_agreement_attachment(agreement_attachments, created_agreement_attachment):
     result = created_agreement_attachment
 
-    agreement_attachments.delete(result.id)
+    agreement_attachments.delete(result.id)
+
+    with pytest.raises(MPTAPIError, match=r"404 Not Found"):
+        agreement_attachments.get(result.id)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a514d5f and b3acd0e.

📒 Files selected for processing (6)
  • e2e_config.test.json (1 hunks)
  • mpt_api_client/resources/commerce/agreements_attachments.py (3 hunks)
  • tests/e2e/commerce/agreement/attachment/conftest.py (1 hunks)
  • tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (1 hunks)
  • tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py (1 hunks)
  • tests/unit/resources/commerce/test_agreements_attachments.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/e2e/commerce/agreement/attachment/conftest.py
  • tests/unit/resources/commerce/test_agreements_attachments.py
🧰 Additional context used
🧬 Code graph analysis (1)
tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (3)
mpt_api_client/exceptions.py (1)
  • MPTAPIError (20-42)
tests/e2e/commerce/agreement/attachment/conftest.py (3)
  • agreement_attachment_factory (15-28)
  • agreement_attachment_id (10-11)
  • invalid_agreement_attachment_id (5-6)
tests/e2e/conftest.py (1)
  • async_mpt_ops (36-39)
🪛 Ruff (0.14.7)
mpt_api_client/resources/commerce/agreements_attachments.py

2-2: Unused noqa directive (unknown: WPS235)

Remove unused noqa directive

(RUF100)

🔇 Additional comments (5)
mpt_api_client/resources/commerce/agreements_attachments.py (2)

33-55: Per-operation mixin composition for attachments looks correct

Using CreateFileMixin/DownloadFileMixin plus UpdateMixin and the standard CRUD/collection mixins (and their async counterparts) gives a clear, explicit API surface and matches how other resources are structured. No issues spotted with the base-class ordering or generics.


2-15: Remove unused noqa on mixins import

Ruff reports RUF100 because # noqa: WPS235 does not correspond to any active warning here; it should be dropped (or updated to the actually suppressed rule, if still needed) to keep lint config clean.

-from mpt_api_client.http.mixins import (  # noqa: WPS235
+from mpt_api_client.http.mixins import (
⛔ Skipped due to learnings
Learnt from: jentyk
Repo: softwareone-platform/mpt-api-python-client PR: 136
File: tests/e2e/notifications/subscribers/conftest.py:14-25
Timestamp: 2025-12-01T10:48:52.586Z
Learning: The repository softwareone-platform/mpt-api-python-client uses wemake-python-styleguide linter, so noqa directives for WPS rules should not be flagged as unused.
e2e_config.test.json (1)

14-15: Attachment config key matches new E2E fixtures

commerce.agreement.attachment.id hooks up cleanly with the agreement_attachment_id fixture and the new attachment E2E tests; this addition looks good.

tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (1)

1-85: Solid async E2E coverage for agreement attachments

The async tests nicely cover the main lifecycle and error paths (CRUD, list, filter, download, and 404 handling via MPTAPIError), and the dedicated created_agreement_attachment fixture keeps seeded and test-created data separate. No functional issues spotted.

tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py (1)

1-85: Synchronous E2E attachment tests mirror async suite effectively

This sync suite cleanly mirrors the async tests and exercises the full attachment lifecycle with the same fixtures and RQL-based filtering. Structure and use of fixtures look sound.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
e2e_config.test.json (1)

16-16: commerce.order.id still unused in E2E tests

"commerce.order.id" remains defined here but there are still no corresponding E2E fixtures or tests consuming it in this PR. Either remove it from the test config for now or add the missing E2E coverage that uses this key.

🧹 Nitpick comments (3)
mpt_api_client/resources/commerce/agreements_attachments.py (2)

2-15: Remove unused/unknown noqa on mixins import

Ruff reports RUF100 on # noqa: WPS235; since there’s no such rule configured, the directive is unused and should be dropped to keep lint clean.

-from mpt_api_client.http.mixins import (  # noqa: WPS235
+from mpt_api_client.http.mixins import (

23-36: Clarify AgreementsAttachmentServiceConfig docstring

The class is specific to agreement attachments, but the docstring still says "Orders service config.", which is confusing in this module. Consider renaming to something like "Agreement attachments service config." for clarity.

tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py (1)

63-72: Optionally assert updated fields in E2E update test

The update test only checks that update(...) returns a non-None result. If the model exposes name/description attributes, you could assert they match updated_name to better validate the update behavior end-to-end.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3acd0e and 9518377.

📒 Files selected for processing (6)
  • e2e_config.test.json (1 hunks)
  • mpt_api_client/resources/commerce/agreements_attachments.py (3 hunks)
  • tests/e2e/commerce/agreement/attachment/conftest.py (1 hunks)
  • tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (1 hunks)
  • tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py (1 hunks)
  • tests/unit/resources/commerce/test_agreements_attachments.py (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-27T00:05:36.356Z
Learnt from: robcsegal
Repo: softwareone-platform/mpt-api-python-client PR: 131
File: tests/e2e/conftest.py:0-0
Timestamp: 2025-11-27T00:05:36.356Z
Learning: When reviewing PRs that add fixtures to a parent conftest that may duplicate fixtures in child conftest files, do not suggest removing the duplicates from child conftest files that are outside the scope of the PR's changes.

Applied to files:

  • tests/e2e/commerce/agreement/attachment/conftest.py
🧬 Code graph analysis (4)
tests/e2e/commerce/agreement/attachment/conftest.py (1)
tests/e2e/conftest.py (1)
  • e2e_config (89-92)
tests/unit/resources/commerce/test_agreements_attachments.py (16)
tests/unit/resources/catalog/test_price_list_items.py (1)
  • test_methods_present (40-43)
tests/unit/resources/catalog/test_pricing_policy_attachments.py (1)
  • test_methods_present (44-47)
tests/unit/resources/accounts/test_accounts_users.py (1)
  • test_methods_present (43-46)
tests/unit/resources/accounts/test_account_users.py (1)
  • test_methods_present (27-30)
tests/unit/resources/billing/test_credit_memo_attachments.py (1)
  • test_methods_present (42-45)
tests/unit/resources/billing/test_custom_ledger_attachments.py (1)
  • test_methods_present (40-43)
tests/unit/resources/billing/test_custom_ledger_charges.py (1)
  • test_methods_present (40-43)
tests/unit/resources/billing/test_invoice_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/billing/test_journal_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/billing/test_invoices.py (1)
  • test_methods_present (27-30)
tests/unit/resources/billing/test_journal_charges.py (1)
  • test_methods_present (38-41)
tests/unit/resources/billing/test_journal_sellers.py (1)
  • test_methods_present (38-41)
tests/unit/resources/billing/test_journal_upload.py (1)
  • test_methods_present (36-39)
tests/unit/resources/billing/test_ledger_charges.py (1)
  • test_methods_present (36-39)
tests/unit/resources/billing/test_ledger_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/billing/test_statement_charges.py (1)
  • test_methods_present (40-43)
tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (3)
mpt_api_client/exceptions.py (1)
  • MPTAPIError (20-42)
tests/e2e/commerce/agreement/attachment/conftest.py (3)
  • agreement_attachment_factory (15-28)
  • agreement_attachment_id (10-11)
  • invalid_agreement_attachment_id (5-6)
mpt_api_client/models/file_model.py (2)
  • file_contents (35-45)
  • filename (13-32)
mpt_api_client/resources/commerce/agreements_attachments.py (1)
mpt_api_client/http/mixins.py (10)
  • AsyncCollectionMixin (538-605)
  • AsyncCreateFileMixin (187-216)
  • AsyncDeleteMixin (273-283)
  • AsyncDownloadFileMixin (303-325)
  • CollectionMixin (469-535)
  • CreateFileMixin (115-144)
  • DeleteMixin (29-38)
  • DownloadFileMixin (58-80)
  • GetMixin (361-377)
  • UpdateMixin (41-55)
🪛 Biome (2.1.2)
e2e_config.test.json

[error] 15-15: The key commerce.agreement.id was already declared.

This where a duplicated key was declared again.

If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.

(lint/suspicious/noDuplicateObjectKeys)


[error] 17-17: The key commerce.product.id was already declared.

This where a duplicated key was declared again.

If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.

(lint/suspicious/noDuplicateObjectKeys)


[error] 18-18: The key commerce.product.item.id was already declared.

This where a duplicated key was declared again.

If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.

(lint/suspicious/noDuplicateObjectKeys)


[error] 19-19: The key commerce.product.listing.id was already declared.

This where a duplicated key was declared again.

If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.

(lint/suspicious/noDuplicateObjectKeys)


[error] 20-20: The key commerce.product.template.id was already declared.

This where a duplicated key was declared again.

If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.

(lint/suspicious/noDuplicateObjectKeys)


[error] 21-21: The key commerce.authorization.id was already declared.

This where a duplicated key was declared again.

If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.

(lint/suspicious/noDuplicateObjectKeys)


[error] 22-22: The key commerce.client.id was already declared.

This where a duplicated key was declared again.

If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.

(lint/suspicious/noDuplicateObjectKeys)


[error] 25-25: The key catalog.product.item.id was already declared.

This where a duplicated key was declared again.

If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.

(lint/suspicious/noDuplicateObjectKeys)

🪛 Ruff (0.14.7)
mpt_api_client/resources/commerce/agreements_attachments.py

2-2: Unused noqa directive (unknown: WPS235)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
tests/unit/resources/commerce/test_agreements_attachments.py (1)

35-46: Method surface checks now cover full API

Including "update" and "iterate" in both sync and async parametrized lists correctly matches the new mixins (UpdateMixin and (Async)CollectionMixin). This keeps unit coverage aligned with the attachment services’ public surface.

tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (1)

9-15: Verify async fixture support for @pytest.fixture

created_agreement_attachment is declared as async def under @pytest.fixture. This relies on your pytest/pytest-asyncio setup to await async fixtures via the standard decorator; in some configurations you must use pytest_asyncio.fixture instead.

tests/e2e/commerce/agreement/attachment/conftest.py (1)

4-28: Fixtures cleanly model agreement attachment test data

The invalid ID, config-driven ID, and small factory for attachment payloads give the E2E tests a clear, reusable setup surface and correctly consume commerce.agreement.attachment.id from the config.

@robcsegal robcsegal force-pushed the MPT-14905-add-e-2-e-tests-for-commerce-agreements-attachments branch from 9518377 to 5e40b58 Compare December 5, 2025 14:34
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (1)

79-85: Invoke file_contents() to actually validate the download payload

file_contents is a method; result.file_contents is not None only checks that the attribute exists, not that bytes were retrieved. Call the method and assert on the returned content instead.

 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"
+    content = result.file_contents()
+    assert isinstance(content, bytes)
+    assert content  # non-empty payload
+    assert result.filename == "empty.pdf"
🧹 Nitpick comments (3)
tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (2)

61-71: Strengthen the update test by asserting the updated fields

Right now the update e2e test only checks result is not None. Asserting that the name was actually changed would better validate the endpoint behaviour.

 async def test_update_agreement_attachment(agreement_attachments, created_agreement_attachment):
     updated_name = "E2E Updated Agreement Attachment Name"
@@
-    result = await agreement_attachments.update(attachment.id, updated_attachment_data)
-
-    assert result is not None
+    result = await agreement_attachments.update(attachment.id, updated_attachment_data)
+
+    assert result is not None
+    assert result.name == updated_name
+    assert result.description == updated_name

9-15: Check async fixture usage with the sync test_create_agreement_attachment

created_agreement_attachment is defined as an async def fixture but test_create_agreement_attachment is synchronous. Depending on your pytest-asyncio configuration, this can result in the test receiving an unawaited coroutine instead of a created attachment.

If your other async e2e tests use async def tests with async fixtures, consider making this test async as well for consistency:

-async def created_agreement_attachment(agreement_attachments, agreement_attachment_factory, pdf_fd):
+async def created_agreement_attachment(agreement_attachments, agreement_attachment_factory, pdf_fd):
@@
-def test_create_agreement_attachment(created_agreement_attachment):
-    result = created_agreement_attachment
+async def test_create_agreement_attachment(created_agreement_attachment):
+    result = created_agreement_attachment

Please double-check how async fixtures are handled in your current pytest / pytest-asyncio setup to ensure this test is exercising the create call as intended.

Also applies to: 55-59

mpt_api_client/resources/commerce/agreements_attachments.py (1)

23-31: Mixin composition and upload config look consistent; consider updating the config docstring

The switch to {Create,Update,Download}FileMixin (and async counterparts) plus _upload_file_key = "file" / _upload_data_key = "attachment" aligns with the generic file mixin contracts and should support upload/download correctly for this resource.

Minor nit: AgreementsAttachmentServiceConfig still has the docstring "Orders service config.", which is slightly misleading now that this is clearly for agreement attachments.

-class AgreementsAttachmentServiceConfig:
-    """Orders service config."""
+class AgreementsAttachmentServiceConfig:
+    """Agreements attachment service config."""

Also applies to: 33-56

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9518377 and 5e40b58.

📒 Files selected for processing (6)
  • e2e_config.test.json (2 hunks)
  • mpt_api_client/resources/commerce/agreements_attachments.py (3 hunks)
  • tests/e2e/commerce/agreement/attachment/conftest.py (1 hunks)
  • tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (1 hunks)
  • tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py (1 hunks)
  • tests/unit/resources/commerce/test_agreements_attachments.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • e2e_config.test.json
  • tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-27T00:05:36.356Z
Learnt from: robcsegal
Repo: softwareone-platform/mpt-api-python-client PR: 131
File: tests/e2e/conftest.py:0-0
Timestamp: 2025-11-27T00:05:36.356Z
Learning: When reviewing PRs that add fixtures to a parent conftest that may duplicate fixtures in child conftest files, do not suggest removing the duplicates from child conftest files that are outside the scope of the PR's changes.

Applied to files:

  • tests/e2e/commerce/agreement/attachment/conftest.py
🧬 Code graph analysis (4)
mpt_api_client/resources/commerce/agreements_attachments.py (1)
mpt_api_client/http/mixins.py (11)
  • AsyncCollectionMixin (538-605)
  • AsyncCreateFileMixin (187-216)
  • AsyncDeleteMixin (273-283)
  • AsyncDownloadFileMixin (303-325)
  • AsyncUpdateMixin (286-300)
  • CollectionMixin (469-535)
  • CreateFileMixin (115-144)
  • DeleteMixin (29-38)
  • DownloadFileMixin (58-80)
  • GetMixin (361-377)
  • UpdateMixin (41-55)
tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py (4)
mpt_api_client/exceptions.py (1)
  • MPTAPIError (20-42)
tests/e2e/commerce/agreement/attachment/conftest.py (3)
  • agreement_attachment_factory (15-28)
  • agreement_attachment_id (10-11)
  • invalid_agreement_attachment_id (5-6)
tests/e2e/conftest.py (2)
  • pdf_fd (73-75)
  • async_mpt_ops (36-39)
mpt_api_client/models/file_model.py (2)
  • file_contents (35-45)
  • filename (13-32)
tests/unit/resources/commerce/test_agreements_attachments.py (16)
tests/unit/resources/catalog/test_price_list_items.py (1)
  • test_methods_present (40-43)
tests/unit/resources/catalog/test_pricing_policy_attachments.py (1)
  • test_methods_present (44-47)
tests/unit/resources/accounts/test_account_users.py (1)
  • test_methods_present (27-30)
tests/unit/resources/accounts/test_accounts_users.py (1)
  • test_methods_present (43-46)
tests/unit/resources/billing/test_credit_memo_attachments.py (1)
  • test_methods_present (42-45)
tests/unit/resources/billing/test_custom_ledger_attachments.py (1)
  • test_methods_present (40-43)
tests/unit/resources/billing/test_custom_ledger_charges.py (1)
  • test_methods_present (40-43)
tests/unit/resources/billing/test_invoice_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/billing/test_invoices.py (1)
  • test_methods_present (27-30)
tests/unit/resources/billing/test_journal_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/billing/test_journal_charges.py (1)
  • test_methods_present (38-41)
tests/unit/resources/billing/test_journal_sellers.py (1)
  • test_methods_present (38-41)
tests/unit/resources/billing/test_journal_upload.py (1)
  • test_methods_present (36-39)
tests/unit/resources/billing/test_ledger_charges.py (1)
  • test_methods_present (36-39)
tests/unit/resources/billing/test_ledger_attachments.py (1)
  • test_methods_present (41-44)
tests/unit/resources/billing/test_statement_charges.py (1)
  • test_methods_present (40-43)
tests/e2e/commerce/agreement/attachment/conftest.py (1)
tests/e2e/conftest.py (1)
  • e2e_config (89-92)
🪛 Ruff (0.14.7)
mpt_api_client/resources/commerce/agreements_attachments.py

2-2: Unused noqa directive (unknown: WPS235)

Remove unused noqa directive

(RUF100)

🔇 Additional comments (2)
tests/unit/resources/commerce/test_agreements_attachments.py (1)

35-42: Method presence tests now aligned with service API

Including update and iterate (alongside get, create, delete, download) on both sync and async services matches the new mixin composition and keeps these tests consistent with other attachment resources.

tests/e2e/commerce/agreement/attachment/conftest.py (1)

4-28: Fixture set for agreement attachments is clear and reusable

The invalid ID, config-driven ID, and payload factory fixtures are well-factored and align with the existing e2e config pattern, making the async/sync attachment tests easy to compose and extend.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Dec 5, 2025

@robcsegal robcsegal merged commit 231c8e1 into main Dec 5, 2025
6 checks passed
@robcsegal robcsegal deleted the MPT-14905-add-e-2-e-tests-for-commerce-agreements-attachments branch December 5, 2025 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants