Skip to content

MPT-21557 handle orphaned parameter during parameter creation in sync and async tests#337

Merged
jentyk merged 1 commit into
mainfrom
bugfix/MPT-21557
Jun 8, 2026
Merged

MPT-21557 handle orphaned parameter during parameter creation in sync and async tests#337
jentyk merged 1 commit into
mainfrom
bugfix/MPT-21557

Conversation

@jentyk

@jentyk jentyk commented Jun 8, 2026

Copy link
Copy Markdown
Member

Closes MPT-21557

Release Notes

  • Add EXTERNAL_ID constant to tests/e2e/program/program/parameter/conftest.py and use it in the parameter_data fixture.
  • Update created_parameter fixture in both sync and async tests to catch MPTAPIError during creation.
  • On HTTP 400 errors indicating a parameter with the same external identifier already exists, fetch the existing parameter via service.filter(RQLQuery(externalId=EXTERNAL_ID)).fetch_one() instead of failing.
  • Preserve teardown behavior: attempt to delete the created/found parameter and log if deletion fails.

@jentyk jentyk requested a review from a team as a code owner June 8, 2026 17:30
@jentyk jentyk requested review from robcsegal and svazquezco June 8, 2026 17:30
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 68d1aced-9fe2-4a79-9d5b-b0aa0e123cd4

📥 Commits

Reviewing files that changed from the base of the PR and between 61c73c8 and 612a538.

📒 Files selected for processing (3)
  • tests/e2e/program/program/parameter/conftest.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
  • tests/e2e/program/program/parameter/test_sync_parameter.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/e2e/program/program/parameter/conftest.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
  • tests/e2e/program/program/parameter/test_sync_parameter.py
📜 Recent review details
⏰ 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

📝 Walkthrough

Walkthrough

Both created_parameter test fixtures (async and sync) now handle parameter creation conflicts by catching HTTP 400 errors for duplicate external identifiers and fetching the existing parameter instead of failing, while re-raising all other errors.

Changes

Parameter Fixture Resilience

Layer / File(s) Summary
Shared EXTERNAL_ID constant and fixture data
tests/e2e/program/program/parameter/conftest.py
Adds EXTERNAL_ID = "e2eCreatedProgramParameter" and updates parameter_data to reference it.
Async parameter fixture error recovery
tests/e2e/program/program/parameter/test_async_parameter.py
The async fixture wraps service.create(parameter_data) in try/except catching MPTAPIError. On HTTP 400 with an "already exists" detail it fetches the existing parameter via service.filter(RQLQuery(externalId=EXTERNAL_ID)).fetch_one(), otherwise re-raises. Teardown deletion unchanged.
Sync parameter fixture error recovery
tests/e2e/program/program/parameter/test_sync_parameter.py
The sync fixture applies identical error handling: catching MPTAPIError on creation and recovering by fetching the existing parameter for HTTP 400 duplicate externalId errors, while re-raising other errors.

🎯 2 (Simple) | ⏱️ ~8 minutes


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (2 errors)

Check name Status Explanation Resolution
Pr And Commit Formatting ❌ Error Commit lacks body explaining changes and rationale. PR description lacks explanation of what changed. While PR title (MPT-21557) and commit title follow proper formats, bodies are required. Add commit body with explanation of changes. Update PR description to explain what was changed and why.
Ai Warning Line In Pr Description ❌ Error The PR description is missing the required warning line. It starts with "The PR references ticket..." instead of "🤖 AI-generated PR — Please review carefully." Add the exact warning line at the very beginning of the PR description: 🤖 AI-generated PR — Please review carefully.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
tests/e2e/program/program/parameter/test_async_parameter.py (1)

17-17: 💤 Low value

Consider extracting the external ID to a module-level constant.

The external ID "e2eCreatedProgramParameter" appears in both the error message check (line 17) and the RQLQuery filter (line 22). Extracting it to a constant would improve maintainability if this value needs to change.

♻️ Suggested refactor
+PARAMETER_EXTERNAL_ID = "e2eCreatedProgramParameter"
+
+
 `@pytest.fixture`
 async def created_parameter(async_mpt_vendor, program_id, parameter_data):
     service = async_mpt_vendor.program.programs.parameters(program_id)
     try:
         parameter = await service.create(parameter_data)
     except MPTAPIError as error:
         if (
             error.status_code == 400
-            and "Parameter with given external identifier 'e2eCreatedProgramParameter' already "
+            and f"Parameter with given external identifier '{PARAMETER_EXTERNAL_ID}' already "
             "exists for given program."
             in error.detail
         ):
             parameter = await service.filter(
-                RQLQuery(externalId="e2eCreatedProgramParameter")
+                RQLQuery(externalId=PARAMETER_EXTERNAL_ID)
             ).fetch_one()

Also applies to: 22-22

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/e2e/program/program/parameter/test_async_parameter.py` at line 17,
Extract the external identifier "e2eCreatedProgramParameter" into a module-level
constant (e.g., EXTERNAL_ID) and replace the literal occurrences used in the
error message assertion and the RQLQuery filter with that constant; update the
test in tests/e2e/program/program/parameter/test_async_parameter.py so the
assertion that checks for "Parameter with given external identifier
'e2eCreatedProgramParameter' already " and the RQLQuery(...) filter both
reference EXTERNAL_ID instead of the hard-coded string.
tests/e2e/program/program/parameter/test_sync_parameter.py (2)

17-17: 💤 Low value

Consider extracting the external ID to a module-level constant.

The external ID "e2eCreatedProgramParameter" is duplicated in the error message check (line 17) and the RQLQuery filter (line 22). Extracting it to a constant would improve maintainability.

♻️ Suggested refactor
+PARAMETER_EXTERNAL_ID = "e2eCreatedProgramParameter"
+
+
 `@pytest.fixture`
 def created_parameter(mpt_vendor, program_id, parameter_data):
     service = mpt_vendor.program.programs.parameters(program_id)
     try:
         parameter = service.create(parameter_data)
     except MPTAPIError as error:
         if (
             error.status_code == 400
-            and "Parameter with given external identifier 'e2eCreatedProgramParameter' already "
+            and f"Parameter with given external identifier '{PARAMETER_EXTERNAL_ID}' already "
             "exists for given program."
             in error.detail
         ):
             parameter = service.filter(
-                RQLQuery(externalId="e2eCreatedProgramParameter")
+                RQLQuery(externalId=PARAMETER_EXTERNAL_ID)
             ).fetch_one()

Also applies to: 22-22

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/e2e/program/program/parameter/test_sync_parameter.py` at line 17,
Extract the duplicated external ID string "e2eCreatedProgramParameter" to a
module-level constant (e.g., E2E_CREATED_PROGRAM_PARAMETER_EXTERNAL_ID) and
replace the two occurrences: the error message assertion that contains
"Parameter with given external identifier 'e2eCreatedProgramParameter' already "
and the RQLQuery filter that uses the same literal; update any imports or
references in the test_sync_parameter.py module so both places reference the new
constant.

12-25: ⚖️ Poor tradeoff

Consider extracting duplicate error handling to a shared conftest helper.

The error recovery logic (lines 12-25) is duplicated between test_async_parameter.py and test_sync_parameter.py. If this pattern is needed elsewhere or if the logic changes, extracting it to a shared helper in conftest.py would reduce maintenance burden.

💡 Potential approach

Create sync and async helper functions in tests/e2e/program/program/parameter/conftest.py:

PARAMETER_EXTERNAL_ID = "e2eCreatedProgramParameter"

def _handle_duplicate_parameter_error(error: MPTAPIError, service):
    """Fetch existing parameter if creation fails due to duplicate external ID."""
    if (
        error.status_code == 400
        and f"Parameter with given external identifier '{PARAMETER_EXTERNAL_ID}' already "
        "exists for given program."
        in error.detail
    ):
        return service.filter(RQLQuery(externalId=PARAMETER_EXTERNAL_ID)).fetch_one()
    raise

async def _handle_duplicate_parameter_error_async(error: MPTAPIError, service):
    """Async version of _handle_duplicate_parameter_error."""
    if (
        error.status_code == 400
        and f"Parameter with given external identifier '{PARAMETER_EXTERNAL_ID}' already "
        "exists for given program."
        in error.detail
    ):
        return await service.filter(RQLQuery(externalId=PARAMETER_EXTERNAL_ID)).fetch_one()
    raise

Then simplify the fixtures:

try:
    parameter = service.create(parameter_data)
except MPTAPIError as error:
    parameter = _handle_duplicate_parameter_error(error, service)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/e2e/program/program/parameter/test_sync_parameter.py` around lines 12 -
25, Extract the duplicated duplicate-parameter recovery logic into shared
helpers in tests/e2e/program/program/parameter/conftest.py: add a constant
PARAMETER_EXTERNAL_ID and two helpers _handle_duplicate_parameter_error(error,
service) and async _handle_duplicate_parameter_error_async(error, service) which
check MPTAPIError.status_code and error.detail, and on match return
service.filter(RQLQuery(externalId=PARAMETER_EXTERNAL_ID)).fetch_one() (or await
...fetch_one() for the async variant); then replace the try/except in
test_sync_parameter.py (the service.create call and its except MPTAPIError
block) with a call to the new helper to fetch the existing parameter on
duplicate instead of duplicating the logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/e2e/program/program/parameter/test_async_parameter.py`:
- Line 17: Extract the external identifier "e2eCreatedProgramParameter" into a
module-level constant (e.g., EXTERNAL_ID) and replace the literal occurrences
used in the error message assertion and the RQLQuery filter with that constant;
update the test in tests/e2e/program/program/parameter/test_async_parameter.py
so the assertion that checks for "Parameter with given external identifier
'e2eCreatedProgramParameter' already " and the RQLQuery(...) filter both
reference EXTERNAL_ID instead of the hard-coded string.

In `@tests/e2e/program/program/parameter/test_sync_parameter.py`:
- Line 17: Extract the duplicated external ID string
"e2eCreatedProgramParameter" to a module-level constant (e.g.,
E2E_CREATED_PROGRAM_PARAMETER_EXTERNAL_ID) and replace the two occurrences: the
error message assertion that contains "Parameter with given external identifier
'e2eCreatedProgramParameter' already " and the RQLQuery filter that uses the
same literal; update any imports or references in the test_sync_parameter.py
module so both places reference the new constant.
- Around line 12-25: Extract the duplicated duplicate-parameter recovery logic
into shared helpers in tests/e2e/program/program/parameter/conftest.py: add a
constant PARAMETER_EXTERNAL_ID and two helpers
_handle_duplicate_parameter_error(error, service) and async
_handle_duplicate_parameter_error_async(error, service) which check
MPTAPIError.status_code and error.detail, and on match return
service.filter(RQLQuery(externalId=PARAMETER_EXTERNAL_ID)).fetch_one() (or await
...fetch_one() for the async variant); then replace the try/except in
test_sync_parameter.py (the service.create call and its except MPTAPIError
block) with a call to the new helper to fetch the existing parameter on
duplicate instead of duplicating the logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 743716be-eba6-4a6f-919d-4743df36fbbc

📥 Commits

Reviewing files that changed from the base of the PR and between aca3e02 and 0fc7b2b.

📒 Files selected for processing (2)
  • tests/e2e/program/program/parameter/test_async_parameter.py
  • tests/e2e/program/program/parameter/test_sync_parameter.py
📜 Review details
⏰ 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 context used
📓 Path-based instructions (2)
**/*

⚙️ CodeRabbit configuration file

**/*: For each subsequent commit in this PR, explicitly verify if previous review comments have been resolved

Files:

  • tests/e2e/program/program/parameter/test_sync_parameter.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
**

⚙️ CodeRabbit configuration file

**: # AGENTS.md

Working protocol for any task in this repository:

  1. Identify the task type and select only the local repository files that are relevant to that task.
  2. Read only those relevant local files before making changes.
  3. If any selected local file references shared standards or shared operational guidance that are relevant to the same task, read those shared documents too before proceeding.
  4. Treat repository-local documents as repository-specific additions, restrictions, or overrides to shared guidance.
  5. If a repository-local rule conflicts with a shared rule, the local repository rule takes precedence.

Python API client for the SoftwareONE Marketplace Platform (MPT) API. Provides synchronous
(MPTClient) and asynchronous (AsyncMPTClient) clients built on httpx, with typed
resource services, mixin-based HTTP operations, and an RQL query builder.

Documentation Reading Order

When applicable, read the repository documentation in this order:

  1. README.md — repository overview, quick start, and documentation map
  2. docs/usage.md — installation, configuration, Python usage examples, and supported Docker-based commands
  3. docs/architecture.md — layered architecture, directory structure, and key abstractions
  4. docs/local-development.md — Docker-only setup and execution model
  5. docs/testing.md — repository-specific testing strategy and command mapping
  6. docs/contributing.md — repository-specific workflow and links to shared standards
  7. docs/documentation.md — repository-specific documentation rules

Then inspect the code paths relevant to the task:

  • mpt_api_client/mpt_client.py — public sync and async client entry points
  • mpt_api_client/http/ — HTTP clients, services, query state, and reusable mixins
  • mpt_api_client/resources/ — domain resource groups such as catalog, commerce, billing, and integration
  • mpt_api_client/models/ — response model layer and collection wrappers
  • mpt_api_client/rql/ — fluent RQL query ...

Files:

  • tests/e2e/program/program/parameter/test_sync_parameter.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
🧠 Learnings (11)
📓 Common learnings
Learnt from: albertsola
Repo: softwareone-platform/mpt-api-python-client PR: 183
File: tests/e2e/catalog/price_lists/conftest.py:29-30
Timestamp: 2026-01-08T08:34:13.551Z
Learning: In the mpt-api-python-client E2E tests, the team intentionally reuses existing API resources (via e2e_config) for read-only operations and safe mutations (e.g., updating the "notes" field) to improve test execution speed. Only destructive/dangerous tests use isolated fixtures (created_price_list or async_created_price_list) that are created and cleaned up per test.
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.
Learnt from: CR
Repo: softwareone-platform/mpt-api-python-client PR: 0
File: docs/unit_tests.md:0-0
Timestamp: 2026-06-02T14:06:45.662Z
Learning: Applies to docs/tests/unit/conftest.py : Shared test fixtures (http_client, async_http_client, DummyModel) should be defined in `tests/unit/conftest.py`
Learnt from: albertsola
Repo: softwareone-platform/mpt-api-python-client PR: 277
File: tests/e2e/extensibility/extensions/conftest.py:37-40
Timestamp: 2026-04-06T09:03:39.708Z
Learning: In the softwareone-platform/mpt-api-python-client repository, E2E conftest teardown fixtures intentionally catch all `MPTAPIError` exceptions (not just 404) and print a diagnostic message instead of re-raising. This is by design: in some scenarios the resource cannot be deleted, and the broad catch keeps fixtures reusable without failing the test suite during teardown. Do not suggest narrowing the except clause (e.g., checking `error.status == 404`) in E2E conftest teardown blocks.
📚 Learning: 2026-04-06T09:03:39.708Z
Learnt from: albertsola
Repo: softwareone-platform/mpt-api-python-client PR: 277
File: tests/e2e/extensibility/extensions/conftest.py:37-40
Timestamp: 2026-04-06T09:03:39.708Z
Learning: In the softwareone-platform/mpt-api-python-client repository, E2E conftest teardown fixtures intentionally catch all `MPTAPIError` exceptions (not just 404) and print a diagnostic message instead of re-raising. This is by design: in some scenarios the resource cannot be deleted, and the broad catch keeps fixtures reusable without failing the test suite during teardown. Do not suggest narrowing the except clause (e.g., checking `error.status == 404`) in E2E conftest teardown blocks.

Applied to files:

  • tests/e2e/program/program/parameter/test_sync_parameter.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2026-01-08T08:34:05.465Z
Learnt from: albertsola
Repo: softwareone-platform/mpt-api-python-client PR: 183
File: tests/e2e/catalog/price_lists/conftest.py:29-30
Timestamp: 2026-01-08T08:34:05.465Z
Learning: In end-to-end tests (e.g., tests/e2e/...), reuse existing API resources for read-only operations and safe mutations to speed up test execution. Reserve isolated fixtures (e.g., created_price_list) for destructive tests that require per-test creation and cleanup. Ensure tests document when a fixture creates/destroys data and clearly indicate which operations are destructive, so tests stay fast and properly isolated.

Applied to files:

  • tests/e2e/program/program/parameter/test_sync_parameter.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2026-04-16T17:40:16.671Z
Learnt from: robcsegal
Repo: softwareone-platform/mpt-api-python-client PR: 305
File: tests/e2e/program/program/media/test_async_media.py:26-33
Timestamp: 2026-04-16T17:40:16.671Z
Learning: In the mpt-api-python-client repository, when creating program media of type "Video" via URL (e.g., a YouTube URL), the API still requires a file upload (e.g., a thumbnail/logo file). The `created_media_from_url` fixture correctly passes both `media_data["url"]` and `file=logo_fd` to `.create()`. Do not flag the `file` argument as unnecessary in URL-based media creation fixtures under `tests/e2e/program/program/media/`.

Applied to files:

  • tests/e2e/program/program/parameter/test_sync_parameter.py
📚 Learning: 2026-04-16T11:49:56.583Z
Learnt from: jentyk
Repo: softwareone-platform/mpt-api-python-client PR: 303
File: tests/e2e/helpdesk/chats/participants/conftest.py:25-31
Timestamp: 2026-04-16T11:49:56.583Z
Learning: In the mpt-api-python-client repo, the helpdesk chat participants E2E fixtures (`created_chat_participant`, `async_created_chat_participant` in `tests/e2e/helpdesk/chats/participants/conftest.py`) do not need teardown. The parent `created_chat` fixture handles cascade-deletion of the chat and all its relations (including participants) during teardown. Do not flag missing participant cleanup as an issue.

Applied to files:

  • tests/e2e/program/program/parameter/test_sync_parameter.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2025-12-12T15:02:20.732Z
Learnt from: robcsegal
Repo: softwareone-platform/mpt-api-python-client PR: 160
File: tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py:55-58
Timestamp: 2025-12-12T15:02:20.732Z
Learning: In pytest with pytest-asyncio, if a test function uses async fixtures but contains no await, declare the test function as def (synchronous) instead of async def. Pytest-asyncio will resolve the async fixtures automatically; this avoids linter complaints about unnecessary async functions. This pattern applies to any test file under the tests/ directory that uses such fixtures.

Applied to files:

  • tests/e2e/program/program/parameter/test_sync_parameter.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2026-04-02T09:35:03.825Z
Learnt from: jentyk
Repo: softwareone-platform/mpt-api-python-client PR: 269
File: tests/e2e/helpdesk/chats/links/test_sync_links.py:18-18
Timestamp: 2026-04-02T09:35:03.825Z
Learning: In this repository’s test suite, flake8-aaa/flake8-aaa codes use short two-digit suffixes (e.g., `# noqa: AAA01`), not three-digit variants like `AAA001`. If you see `# noqa: AAA01` in a test file (e.g., when the Act step is performed via a pytest fixture rather than inline code), treat it as valid and intentional—do not flag it as an invalid/no-longer-needed noqa, and do not suggest removing it even if Ruff reports `RUF102`, since `AAA` is configured under `tool.ruff.lint.external` and these noqa directives are expected to be preserved.

Applied to files:

  • tests/e2e/program/program/parameter/test_sync_parameter.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2026-01-08T23:38:19.565Z
Learnt from: robcsegal
Repo: softwareone-platform/mpt-api-python-client PR: 186
File: tests/e2e/billing/ledger/charge/test_sync_ledger_charge.py:33-39
Timestamp: 2026-01-08T23:38:19.565Z
Learning: In Python e2e tests under tests/e2e, hardcoded external IDs (e.g., INV12345) are intentional because they come from seeded test data. Ensure the test data seeds consistently include these IDs; if seeds change, update tests accordingly. Prefer using a named constant for such IDs (e.g., INV_EXTERNAL_ID) and document the dependency on seed data to avoid brittle tests.

Applied to files:

  • tests/e2e/program/program/parameter/test_sync_parameter.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2026-02-02T13:05:41.144Z
Learnt from: albertsola
Repo: softwareone-platform/mpt-api-python-client PR: 201
File: tests/unit/resources/accounts/mixins/test_activatable_mixin.py:132-139
Timestamp: 2026-02-02T13:05:41.144Z
Learning: In the mpt-api-python-client repository, tests are configured to use pytest asyncio mode auto, which auto-detects async test functions and runs them without requiring pytest.mark.asyncio. Reviewers should rely on this behavior for all Python test files under tests/, and avoid adding unnecessary asyncio markers in async tests. Ensure test files in tests/ adhere to this convention unless a specific test requires an explicit marker.

Applied to files:

  • tests/e2e/program/program/parameter/test_sync_parameter.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2026-04-16T13:00:41.320Z
Learnt from: jentyk
Repo: softwareone-platform/mpt-api-python-client PR: 303
File: tests/e2e/helpdesk/chats/participants/conftest.py:25-31
Timestamp: 2026-04-16T13:00:41.320Z
Learning: In mpt-api-python-client, do not treat list-wrapped arguments to CreateMixin.create() / AsyncCreateMixin.create() as an error. ResourceData is intentionally typed as Resource | list[Resource] (see mpt_api_client/models/model.py), so create() should accept either a single resource dict or a list of resource dicts (e.g., create([chat_participant_data])) to perform a batch create and return a ModelCollection. Therefore, reviewers should only flag list-wrapped create() arguments when there is evidence they violate the expected API contract beyond this documented batch-create behavior.

Applied to files:

  • tests/e2e/program/program/parameter/test_sync_parameter.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2025-11-27T00:05:54.701Z
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.

Applied to files:

  • tests/e2e/program/program/parameter/test_async_parameter.py
🔇 Additional comments (2)
tests/e2e/program/program/parameter/test_async_parameter.py (1)

12-25: LGTM!

tests/e2e/program/program/parameter/test_sync_parameter.py (1)

12-25: LGTM!

@jentyk

jentyk commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@jentyk jentyk force-pushed the bugfix/MPT-21557 branch 2 times, most recently from 9f87e17 to 61c73c8 Compare June 8, 2026 18:43
coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 8, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/e2e/program/program/parameter/test_async_parameter.py`:
- Around line 13-24: The existing except block catches MPTAPIError for the case
where create() fails due to duplicate externalId but then calls
service.filter(...).fetch_one() which actually raises ValueError for 0 or >1
results; change the error handling so you inspect the create() exception safely
(guarding error.detail for None before doing "in"), and when falling back call
fetch_one() inside a try/except that catches ValueError (or let it bubble with
an augmented message including EXTERNAL_ID/RQLQuery), i.e. keep the check for
MPTAPIError on service.create and its detail, but wrap
service.filter(RQLQuery(externalId=EXTERNAL_ID)).fetch_one() in a try/except
ValueError to handle not-found/multiple-match cases (or re-raise a clearer error
mentioning EXTERNAL_ID).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: e08f5add-703a-4922-a371-eb144ab5020d

📥 Commits

Reviewing files that changed from the base of the PR and between 0fc7b2b and 61c73c8.

📒 Files selected for processing (3)
  • tests/e2e/program/program/parameter/conftest.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
  • tests/e2e/program/program/parameter/test_sync_parameter.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/e2e/program/program/parameter/test_sync_parameter.py
📜 Review details
⏰ 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 context used
📓 Path-based instructions (2)
**/*

⚙️ CodeRabbit configuration file

**/*: For each subsequent commit in this PR, explicitly verify if previous review comments have been resolved

Files:

  • tests/e2e/program/program/parameter/conftest.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
**

⚙️ CodeRabbit configuration file

**: # AGENTS.md

Working protocol for any task in this repository:

  1. Identify the task type and select only the local repository files that are relevant to that task.
  2. Read only those relevant local files before making changes.
  3. If any selected local file references shared standards or shared operational guidance that are relevant to the same task, read those shared documents too before proceeding.
  4. Treat repository-local documents as repository-specific additions, restrictions, or overrides to shared guidance.
  5. If a repository-local rule conflicts with a shared rule, the local repository rule takes precedence.

Python API client for the SoftwareONE Marketplace Platform (MPT) API. Provides synchronous
(MPTClient) and asynchronous (AsyncMPTClient) clients built on httpx, with typed
resource services, mixin-based HTTP operations, and an RQL query builder.

Documentation Reading Order

When applicable, read the repository documentation in this order:

  1. README.md — repository overview, quick start, and documentation map
  2. docs/usage.md — installation, configuration, Python usage examples, and supported Docker-based commands
  3. docs/architecture.md — layered architecture, directory structure, and key abstractions
  4. docs/local-development.md — Docker-only setup and execution model
  5. docs/testing.md — repository-specific testing strategy and command mapping
  6. docs/contributing.md — repository-specific workflow and links to shared standards
  7. docs/documentation.md — repository-specific documentation rules

Then inspect the code paths relevant to the task:

  • mpt_api_client/mpt_client.py — public sync and async client entry points
  • mpt_api_client/http/ — HTTP clients, services, query state, and reusable mixins
  • mpt_api_client/resources/ — domain resource groups such as catalog, commerce, billing, and integration
  • mpt_api_client/models/ — response model layer and collection wrappers
  • mpt_api_client/rql/ — fluent RQL query ...

Files:

  • tests/e2e/program/program/parameter/conftest.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
🧠 Learnings (8)
📚 Learning: 2025-12-12T15:02:20.732Z
Learnt from: robcsegal
Repo: softwareone-platform/mpt-api-python-client PR: 160
File: tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py:55-58
Timestamp: 2025-12-12T15:02:20.732Z
Learning: In pytest with pytest-asyncio, if a test function uses async fixtures but contains no await, declare the test function as def (synchronous) instead of async def. Pytest-asyncio will resolve the async fixtures automatically; this avoids linter complaints about unnecessary async functions. This pattern applies to any test file under the tests/ directory that uses such fixtures.

Applied to files:

  • tests/e2e/program/program/parameter/conftest.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2026-04-02T09:35:03.825Z
Learnt from: jentyk
Repo: softwareone-platform/mpt-api-python-client PR: 269
File: tests/e2e/helpdesk/chats/links/test_sync_links.py:18-18
Timestamp: 2026-04-02T09:35:03.825Z
Learning: In this repository’s test suite, flake8-aaa/flake8-aaa codes use short two-digit suffixes (e.g., `# noqa: AAA01`), not three-digit variants like `AAA001`. If you see `# noqa: AAA01` in a test file (e.g., when the Act step is performed via a pytest fixture rather than inline code), treat it as valid and intentional—do not flag it as an invalid/no-longer-needed noqa, and do not suggest removing it even if Ruff reports `RUF102`, since `AAA` is configured under `tool.ruff.lint.external` and these noqa directives are expected to be preserved.

Applied to files:

  • tests/e2e/program/program/parameter/conftest.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2026-01-08T08:34:05.465Z
Learnt from: albertsola
Repo: softwareone-platform/mpt-api-python-client PR: 183
File: tests/e2e/catalog/price_lists/conftest.py:29-30
Timestamp: 2026-01-08T08:34:05.465Z
Learning: In end-to-end tests (e.g., tests/e2e/...), reuse existing API resources for read-only operations and safe mutations to speed up test execution. Reserve isolated fixtures (e.g., created_price_list) for destructive tests that require per-test creation and cleanup. Ensure tests document when a fixture creates/destroys data and clearly indicate which operations are destructive, so tests stay fast and properly isolated.

Applied to files:

  • tests/e2e/program/program/parameter/conftest.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2026-01-08T23:38:19.565Z
Learnt from: robcsegal
Repo: softwareone-platform/mpt-api-python-client PR: 186
File: tests/e2e/billing/ledger/charge/test_sync_ledger_charge.py:33-39
Timestamp: 2026-01-08T23:38:19.565Z
Learning: In Python e2e tests under tests/e2e, hardcoded external IDs (e.g., INV12345) are intentional because they come from seeded test data. Ensure the test data seeds consistently include these IDs; if seeds change, update tests accordingly. Prefer using a named constant for such IDs (e.g., INV_EXTERNAL_ID) and document the dependency on seed data to avoid brittle tests.

Applied to files:

  • tests/e2e/program/program/parameter/conftest.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2026-02-02T13:05:41.144Z
Learnt from: albertsola
Repo: softwareone-platform/mpt-api-python-client PR: 201
File: tests/unit/resources/accounts/mixins/test_activatable_mixin.py:132-139
Timestamp: 2026-02-02T13:05:41.144Z
Learning: In the mpt-api-python-client repository, tests are configured to use pytest asyncio mode auto, which auto-detects async test functions and runs them without requiring pytest.mark.asyncio. Reviewers should rely on this behavior for all Python test files under tests/, and avoid adding unnecessary asyncio markers in async tests. Ensure test files in tests/ adhere to this convention unless a specific test requires an explicit marker.

Applied to files:

  • tests/e2e/program/program/parameter/conftest.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
📚 Learning: 2026-04-02T16:26:46.138Z
Learnt from: albertsola
Repo: softwareone-platform/mpt-api-python-client PR: 275
File: tests/e2e/exchange/currencies/conftest.py:43-43
Timestamp: 2026-04-02T16:26:46.138Z
Learning: In this repo’s Python E2E conftest files, line-specific flake8/wemake suppressions like `# noqa: WPS421` placed inline on individual statements (e.g., on specific `print()` calls in teardown blocks) are intentional and acceptable. When reviewing, avoid suggesting converting these to a file-wide ignore (e.g., adding per-file ignores under `[tool.flake8]` in `pyproject.toml`), since that would over-suppress WPS421 for the entire file. Also treat Ruff `RUF102` about an unknown rule code as expected here when suppressing `WPS421` from wemake-python-styleguide.

Applied to files:

  • tests/e2e/program/program/parameter/conftest.py
📚 Learning: 2026-04-06T09:03:34.466Z
Learnt from: albertsola
Repo: softwareone-platform/mpt-api-python-client PR: 277
File: tests/e2e/extensibility/extensions/conftest.py:37-40
Timestamp: 2026-04-06T09:03:34.466Z
Learning: In this repo’s E2E test conftest teardown fixtures, it is intentional to catch `MPTAPIError` broadly (not just 404) and to print a diagnostic message instead of re-raising. This avoids failing the test suite during teardown when a resource cannot be deleted in some scenarios. Do not recommend narrowing the `except` clause in these teardown blocks (e.g., checking `error.status == 404`), unless the fixture is clearly not intended as an E2E teardown diagnostic.

Applied to files:

  • tests/e2e/program/program/parameter/conftest.py
📚 Learning: 2026-04-16T13:00:41.320Z
Learnt from: jentyk
Repo: softwareone-platform/mpt-api-python-client PR: 303
File: tests/e2e/helpdesk/chats/participants/conftest.py:25-31
Timestamp: 2026-04-16T13:00:41.320Z
Learning: In mpt-api-python-client, do not treat list-wrapped arguments to CreateMixin.create() / AsyncCreateMixin.create() as an error. ResourceData is intentionally typed as Resource | list[Resource] (see mpt_api_client/models/model.py), so create() should accept either a single resource dict or a list of resource dicts (e.g., create([chat_participant_data])) to perform a batch create and return a ModelCollection. Therefore, reviewers should only flag list-wrapped create() arguments when there is evidence they violate the expected API contract beyond this documented batch-create behavior.

Applied to files:

  • tests/e2e/program/program/parameter/conftest.py
  • tests/e2e/program/program/parameter/test_async_parameter.py
🔇 Additional comments (2)
tests/e2e/program/program/parameter/conftest.py (1)

3-3: LGTM!

Also applies to: 19-19

tests/e2e/program/program/parameter/test_async_parameter.py (1)

5-5: LGTM!

Comment thread tests/e2e/program/program/parameter/test_async_parameter.py
@jentyk jentyk force-pushed the bugfix/MPT-21557 branch from 61c73c8 to 612a538 Compare June 8, 2026 19:08
@sonarqubecloud

sonarqubecloud Bot commented Jun 8, 2026

Copy link
Copy Markdown

@jentyk jentyk dismissed coderabbitai[bot]’s stale review June 8, 2026 20:10

I responded, you agreed

@jentyk

jentyk commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

1 similar comment
@jentyk

jentyk commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@jentyk

jentyk commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai approve

@jentyk

jentyk commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai resolve

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
✅ Action performed

Comments resolved and changes approved.

@jentyk jentyk merged commit c8a78aa into main Jun 8, 2026
4 checks passed
@jentyk jentyk deleted the bugfix/MPT-21557 branch June 8, 2026 20:37
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.

2 participants