Skip to content

feat: allow configurable providers.json file path - #323

Merged
myakove merged 2 commits into
mainfrom
feat/issue-322-configurable-providers-json-path
Mar 9, 2026
Merged

myakove merged 2 commits into
mainfrom
feat/issue-322-configurable-providers-json-path

Conversation

@myakove

@myakove myakove commented Mar 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Closes #322

Allow users to configure the providers JSON file path instead of hardcoding .providers.json.

New configuration options (priority order)

  1. --providers-json /path/to/file.json — pytest CLI argument
  2. PROVIDERS_JSON_PATH=/path/to/file.json — environment variable
  3. .providers.json — default (unchanged)

Changes

  • utilities/utils.py: Added resolve_providers_json_path() helper with file validation (is_file() check). load_source_providers() now accepts optional path parameter and raises ProviderEmptyContentError for empty files, FileNotFoundError for missing files.
  • conftest.py: Added --providers-json CLI option. Updated source_providers and source_provider_data fixtures. Moved module-level provider-type skip logic into pytest_collection_modifyitems hook (guarded by is_dry_run for --collect-only support). Added documented pattern for adding new skip rules.
  • exceptions/exceptions.py: Added ProviderEmptyContentError. Updated MissingProvidersFileError to accept path parameter.
  • pytest.ini: Registered copyoffload_snapshots marker.
  • Test files: Removed module-level _SOURCE_PROVIDER_TYPE from 3 test files. Replaced skipif with @pytest.mark.copyoffload_snapshots marker.
  • CLAUDE.md: Added "No Module-Level Provider Loading" rule, updated Test Markers table, added marker and naming conventions.
  • README.md: Documented new configuration options.

Test plan

  • pre-commit run --all-files passes
  • uv run pytest --collect-only -m warm — correctly collects warm tests
  • uv run pytest --collect-only -m copyoffload_snapshots — correctly collects snapshot tests
  • uv run pytest --collect-only -m warm --tc=source_provider:openstack-psi — skip markers applied for unsupported providers
  • Verified collection hook adds skip markers correctly via marker inspection

Summary by CodeRabbit

  • New Features

    • CLI option to specify providers JSON, environment-variable override, and new default-loading order (CLI > env > default).
    • New test marker for vSphere copy-offload snapshots and marker-based collection behavior; updated test naming guidance.
  • Documentation

    • Expanded providers JSON guidance with examples and precedence notes; updated test marker docs.
  • Bug Fixes

    • Clearer errors and validation for missing/empty providers configuration.

Add --providers-json pytest CLI arg and PROVIDERS_JSON_PATH env var
to override the default .providers.json path.

- Add resolve_providers_json_path() with priority: CLI > env > default
- Add ProviderEmptyContentError for empty provider files
- Move module-level provider-type skip logic to pytest_collection_modifyitems
- Register copyoffload_snapshots marker for vSphere-only snapshot tests
- Update copy-offload error messages to use resolved path
- Guard collection hook with is_dry_run for --collect-only support
- Update CLAUDE.md with provider loading and marker naming rules
- Update README.md with new configuration options

Closes #322
@coderabbitai

coderabbitai Bot commented Mar 9, 2026 •

Copy link
Copy Markdown
Contributor

Caution

Review failed

Failed to post review comments

Walkthrough

Adds configurable providers JSON path resolution (CLI arg > env var > default) with validation and new exceptions; moves provider-loading to fixtures and collection-time gating via pytest markers; adds copyoffload_snapshots marker and replaces module-scope provider-type gating in several tests with marker-based annotations.

Changes

Cohort / File(s) Summary
Utilities & Exceptions
utilities/utils.py, exceptions/exceptions.py
Add DEFAULT_PROVIDERS_JSON_PATH, resolve_providers_json_path(), update load_source_providers() to accept/resolve a path and validate content; add ProviderEmptyContentError; make MissingProvidersFileError accept path.
Pytest hooks & fixtures
conftest.py
Add --providers-json CLI option; use resolve_providers_json_path() across fixtures; update signatures to accept request; implement pytest_collection_modifyitems() to apply marker/skip logic based on resolved providers.
Tests
tests/test_copyoffload_migration.py, tests/test_mtv_warm_migration.py, tests/test_warm_migration_comprehensive.py
Remove module-level provider-type detection and conditional skip/Jira marks; replace with explicit @pytest.mark.copyoffload_snapshots where applicable; add some explicit type annotations to test methods.
Configuration & Markers
pytest.ini, README.md, CLAUDE.md
Add copyoffload_snapshots marker; document CLI/env/default precedence for providers JSON, provide vSphere .providers.json example, and clarify fixture/marker-based collection-time skipping and naming guidance.
Minor manifest changes
MANIFEST (implicit from summaries)
Adjustments reflecting added/edited files and docs (line-count deltas noted).

Sequence Diagram(s)

sequenceDiagram
    participant User as Test Runner (pytest CLI)
    participant Pytest as Pytest Collector
    participant Conftest as conftest.py hooks
    participant Utils as utilities/utils.py
    participant FS as Filesystem

    User->>Pytest: start collection (--providers-json?)
    Pytest->>Conftest: call pytest_addoption / collection hooks
    Conftest->>Utils: resolve_providers_json_path(cli_path)
    Utils->>FS: check CLI path → env → default `.providers.json`
    FS-->>Utils: return resolved path / error
    Utils-->>Conftest: return providers dict (or raise)
    Conftest->>Pytest: pytest_collection_modifyitems applies markers/skips based on providers
    Pytest->>Tests: run marked/skipped tests accordingly
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #294 — Modifies provider-loading utilities and pytest fixtures; touches load_source_providers and related fixture patterns that overlap with this change.

Suggested labels

size/L, Review effort 3/5

Suggested reviewers

  • krcmarik
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR introduces changes beyond issue #322 scope: moving provider-type skip logic from test modules into pytest_collection_modifyitems hook in conftest.py, and adding copyoffload_snapshots marker. While supporting the stated goal, these represent architectural shifts not explicitly required by the issue. Either: (1) scope issue #322 explicitly to include collection-time skip refactoring as a prerequisite, or (2) split the skip-logic refactoring into a separate issue/PR and revert those changes from this PR to maintain focused scope.
Docstring Coverage ⚠️ Warning Docstring coverage is 72.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: allow configurable providers.json file path' is specific and directly describes the main change: adding CLI/env-var configuration for the providers file path instead of hardcoding .providers.json.
Linked Issues check ✅ Passed Code changes comprehensively implement issue #322 requirements: added --providers-json CLI option, PROVIDERS_JSON_PATH env var support with correct priority order, default .providers.json fallback, and proper file validation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/issue-322-configurable-providers-json-path

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Allow configurable providers.json file path with CLI and environment variable support

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add configurable providers JSON file path via CLI arg and environment variable
• Move provider-type skip logic from module level to pytest collection hook
• Register copyoffload_snapshots marker for vSphere-only snapshot tests
• Update error messages to display resolved provider file path
• Remove module-level provider loading from test files to respect CLI arguments
Diagram
flowchart LR
  CLI["--providers-json CLI arg"]
  ENV["PROVIDERS_JSON_PATH env var"]
  DEFAULT[".providers.json default"]
  RESOLVE["resolve_providers_json_path()"]
  LOAD["load_source_providers()"]
  HOOK["pytest_collection_modifyitems hook"]
  SKIP["Apply skip markers"]
  
  CLI --> RESOLVE
  ENV --> RESOLVE
  DEFAULT --> RESOLVE
  RESOLVE --> LOAD
  LOAD --> HOOK
  HOOK --> SKIP
Loading

Grey Divider

File Changes

1. conftest.py ✨ Enhancement +96/-16

Add CLI option and collection-time skip logic

conftest.py


2. exceptions/exceptions.py ✨ Enhancement +7/-2

Add ProviderEmptyContentError and update MissingProvidersFileError

exceptions/exceptions.py


3. utilities/utils.py ✨ Enhancement +45/-7

Add resolve_providers_json_path helper and update load_source_providers

utilities/utils.py


View more (6)
4. tests/test_copyoffload_migration.py ✨ Enhancement +2/-12

Replace skipif with copyoffload_snapshots marker

tests/test_copyoffload_migration.py


5. tests/test_mtv_warm_migration.py ✨ Enhancement +1/-17

Remove module-level provider loading and skip logic

tests/test_mtv_warm_migration.py


6. tests/test_warm_migration_comprehensive.py ✨ Enhancement +1/-17

Remove module-level provider loading and skip logic

tests/test_warm_migration_comprehensive.py


7. pytest.ini ⚙️ Configuration changes +1/-0

Register copyoffload_snapshots marker

pytest.ini


8. CLAUDE.md 📝 Documentation +40/-6

Document provider loading rules and marker requirements

CLAUDE.md


9. README.md 📝 Documentation +7/-0

Document configurable providers JSON path options

README.md


Grey Divider

Qodo Logo

@redhat-qe-bot1

Copy link
Copy Markdown

Report bugs in Issues

Welcome! 🎉

This pull request will be automatically processed with the following features:

🔄 Automatic Actions

  • Reviewer Assignment: Reviewers are automatically assigned based on the OWNERS file in the repository root
  • Size Labeling: PR size labels (XS, S, M, L, XL, XXL) are automatically applied based on changes
  • Issue Creation: Disabled for this repository
  • Branch Labeling: Branch-specific labels are applied to track the target branch
  • Auto-verification: Auto-verified users have their PRs automatically marked as verified
  • Labels: All label categories are enabled (default configuration)

📋 Available Commands

PR Status Management

  • /wip - Mark PR as work in progress (adds WIP: prefix to title)
  • /wip cancel - Remove work in progress status
  • /hold - Block PR merging (approvers only)
  • /hold cancel - Unblock PR merging
  • /verified - Mark PR as verified
  • /verified cancel - Remove verification status
  • /reprocess - Trigger complete PR workflow reprocessing (useful if webhook failed or configuration changed)
  • /regenerate-welcome - Regenerate this welcome message

Review & Approval

  • /lgtm - Approve changes (looks good to me)
  • /approve - Approve PR (approvers only)
  • /automerge - Enable automatic merging when all requirements are met (maintainers and approvers only)
  • /assign-reviewers - Assign reviewers based on OWNERS file
  • /assign-reviewer @username - Assign specific reviewer
  • /check-can-merge - Check if PR meets merge requirements

Testing & Validation

  • /retest tox - Run Python test suite with tox
  • /retest build-container - Rebuild and test container image
  • /retest conventional-title - Validate commit message format
  • /retest all - Run all available tests

Container Operations

  • /build-and-push-container - Build and push container image (tagged with PR number)
    • Supports additional build arguments: /build-and-push-container --build-arg KEY=value

Cherry-pick Operations

  • /cherry-pick <branch> - Schedule cherry-pick to target branch when PR is merged
    • Multiple branches: /cherry-pick branch1 branch2 branch3

Label Management

  • /<label-name> - Add a label to the PR
  • /<label-name> cancel - Remove a label from the PR

✅ Merge Requirements

This PR will be automatically approved when the following conditions are met:

  1. Approval: /approve from at least one approver
  2. Status Checks: All required status checks must pass
  3. No Blockers: No wip, hold, has-conflicts labels and PR must be mergeable (no conflicts)
  4. Verified: PR must be marked as verified

📊 Review Process

Approvers and Reviewers

Approvers:

  • myakove

Reviewers:

  • krcmarik
  • myakove
Available Labels
  • hold
  • verified
  • wip
  • lgtm
  • approve
  • automerge

💡 Tips

  • WIP Status: Use /wip when your PR is not ready for review
  • Verification: The verified label is automatically removed on each new commit
  • Cherry-picking: Cherry-pick labels are processed when the PR is merged
  • Container Builds: Container images are automatically tagged with the PR number
  • Permission Levels: Some commands require approver permissions
  • Auto-verified Users: Certain users have automatic verification and merge privileges

For more information, please refer to the project documentation or contact the maintainers.

@qodo-code-review

qodo-code-review Bot commented Mar 9, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (14) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Provider load aborts collection ☑ 🐞 Bug ⛯ Reliability
Description
pytest_collection_modifyitems() calls load_source_providers() during collection for non-dry runs; if
the providers JSON is missing/empty/invalid, an exception is raised in the hook and aborts the
entire pytest session during collection. This prevents the rest of collection/fixtures from running
and turns a configuration problem into a collection-time crash.
Code

conftest.py[R284-287]

+    if not is_dry_run(config):
+        providers_json_path = config.getoption("providers_json", default=None)
+        providers = load_source_providers(providers_json_path=providers_json_path)
+        # .get() with default is intentional: source_provider may not be configured (e.g., partial config),
Evidence
The collection hook unconditionally loads providers for non-dry runs, and load_source_providers
resolves/validates the path and raises on missing/empty files; because the hook does not catch these
exceptions, pytest will error out during collection.

conftest.py[261-318]
utilities/utils.py[46-71]
utilities/utils.py[74-101]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pytest_collection_modifyitems` currently loads `providers.json` during collection and allows exceptions (missing/empty/invalid file) to propagate, aborting the entire test session during collection.
### Issue Context
This hook is used for provider-type based skipping/marking. Configuration problems should surface as a clear user-facing configuration error (or be deferred to fixture-time validation), not as an uncaught collection-time exception.
### Fix Focus Areas
- conftest.py[261-318]
- utilities/utils.py[46-101]
Suggested approach:
- In `pytest_collection_modifyitems`, first check whether any `items` have keywords that require provider-type gating (e.g., `warm`, `copyoffload`). If none do, return without loading providers.
- Wrap `load_source_providers(...)` in a `try/except` catching at least `FileNotFoundError`, `ProviderEmptyContentError`, `json.JSONDecodeError`, and `ValueError`.
- Re-raise as `pytest.UsageError` with actionable guidance (set `--providers-json` or `PROVIDERS_JSON_PATH`) or skip gating and allow downstream fixtures to raise the more contextual errors.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. copyoffload_config lacks annotations ☑ 📘 Rule violation ✓ Correctness
Description
The modified copyoffload_config fixture signature has no parameter or return type annotations.
This violates the requirement that signature-changed functions be fully typed, reducing
maintainability and type safety.
Code

conftest.py[R1312-1314]

@pytest.fixture(scope="session")
-def copyoffload_config(source_provider, source_provider_data):
+def copyoffload_config(source_provider, source_provider_data, request):
  """
Evidence
PR Compliance ID 3 requires complete type annotations for signature-changed functions;
copyoffload_config was changed to accept request but still has no type annotations.

CLAUDE.md
conftest.py[1312-1314]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`copyoffload_config` had its signature changed (added `request`) but the fixture still lacks parameter and return type annotations.
## Issue Context
Compliance requires all new or signature-changed functions to be fully type-annotated.
## Fix Focus Areas
- conftest.py[1312-1314]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. copyoffload_config lacks annotations ☑ 📘 Rule violation ✓ Correctness
Description
The modified copyoffload_config fixture signature has no parameter or return type annotations.
This violates the requirement that signature-changed functions be fully typed, reducing
maintainability and type safety.
Code

conftest.py[R1312-1314]

@pytest.fixture(scope="session")
-def copyoffload_config(source_provider, source_provider_data):
+def copyoffload_config(source_provider, source_provider_data, request):
 """
Evidence
PR Compliance ID 3 requires complete type annotations for signature-changed functions;
copyoffload_config was changed to accept request but still has no type annotations.

CLAUDE.md
conftest.py[1312-1314]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`copyoffload_config` had its signature changed (added `request`) but the fixture still lacks parameter and return type annotations.
## Issue Context
Compliance requires all new or signature-changed functions to be fully type-annotated.
## Fix Focus Areas
- conftest.py[1312-1314]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (8)
4. copyoffload_storage_secret untyped ☑ 📘 Rule violation ✓ Correctness
Description
The modified copyoffload_storage_secret fixture signature includes an added request parameter
but lacks type annotations for parameters and return type. This violates the requirement that
signature-changed functions be fully typed.
Code

conftest.py[R1400-1404]

  target_namespace,
  source_provider_data,
  copyoffload_config,
+    request,
):
Evidence
PR Compliance ID 3 requires complete type annotations for signature-changed functions;
copyoffload_storage_secret was modified to include request but remains untyped.

CLAUDE.md
conftest.py[1396-1404]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`copyoffload_storage_secret` had its signature changed (added `request`) but does not include parameter or return type annotations.
## Issue Context
Compliance requires complete type annotations for signature-changed functions.
## Fix Focus Areas
- conftest.py[1396-1404]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. copyoffload_storage_secret untyped ☑ 📘 Rule violation ✓ Correctness
Description
The modified copyoffload_storage_secret fixture signature includes an added request parameter
but lacks type annotations for parameters and return type. This violates the requirement that
signature-changed functions be fully typed.
Code

conftest.py[R1400-1404]

 target_namespace,
 source_provider_data,
 copyoffload_config,
+    request,
):
Evidence
PR Compliance ID 3 requires complete type annotations for signature-changed functions;
copyoffload_storage_secret was modified to include request but remains untyped.

CLAUDE.md
conftest.py[1396-1404]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`copyoffload_storage_secret` had its signature changed (added `request`) but does not include parameter or return type annotations.
## Issue Context
Compliance requires complete type annotations for signature-changed functions.
## Fix Focus Areas
- conftest.py[1396-1404]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Fixture uses pytest.fail() 📘 Rule violation ⛯ Reliability
Description
The copyoffload_config fixture performs configuration validation via pytest.fail(). This
violates the rule prohibiting pytest.fail()/pytest.skip() for validation inside fixtures/tests,
and can lead to inconsistent skip/fail behavior.
Code

conftest.py[R1324-1338]

+    providers_path = resolve_providers_json_path(cli_path=request.config.getoption("providers_json"))
+
  # Validate that this is a vSphere provider
  if source_provider.type != Provider.ProviderType.VSPHERE:
      pytest.fail(
          f"Copy-offload tests require vSphere provider, but got '{source_provider.type}'. "
-            "Check your provider configuration in .providers.json"
+            f"Check your provider configuration in {providers_path}"
      )
  # Validate copy-offload configuration exists
  if "copyoffload" not in source_provider_data:
      pytest.fail(
          "Copy-offload configuration not found in source provider data. "
-            "Add 'copyoffload' section to your provider in .providers.json"
+            f"Add 'copyoffload' section to your provider in {providers_path}"
      )
Evidence
PR Compliance ID 5 forbids using pytest.fail() for validation inside fixtures; the modified
fixture still calls pytest.fail() in its validation paths.

CLAUDE.md
conftest.py[1324-1338]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`copyoffload_config` uses `pytest.fail()` for validation inside a fixture, which is disallowed by the compliance checklist.
## Issue Context
Skipping should be done declaratively (e.g., collection-time markers / `@pytest.mark.skipif`), and configuration validation should use appropriate exceptions rather than `pytest.fail()` inside fixtures.
## Fix Focus Areas
- conftest.py[1324-1338]
- conftest.py[1351-1356]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Fixture uses pytest.fail() 📘 Rule violation ⛯ Reliability
Description
The copyoffload_config fixture performs configuration validation via pytest.fail(). This
violates the rule prohibiting pytest.fail()/pytest.skip() for validation inside fixtures/tests,
and can lead to inconsistent skip/fail behavior.
Code

conftest.py[R1324-1338]

+    providers_path = resolve_providers_json_path(cli_path=request.config.getoption("providers_json"))
+
 # Validate that this is a vSphere provider
 if source_provider.type != Provider.ProviderType.VSPHERE:
     pytest.fail(
         f"Copy-offload tests require vSphere provider, but got '{source_provider.type}'. "
-            "Check your provider configuration in .providers.json"
+            f"Check your provider configuration in {providers_path}"
     )
 # Validate copy-offload configuration exists
 if "copyoffload" not in source_provider_data:
     pytest.fail(
         "Copy-offload configuration not found in source provider data. "
-            "Add 'copyoffload' section to your provider in .providers.json"
+            f"Add 'copyoffload' section to your provider in {providers_path}"
     )
Evidence
PR Compliance ID 5 forbids using pytest.fail() for validation inside fixtures; the modified
fixture still calls pytest.fail() in its validation paths.

CLAUDE.md
conftest.py[1324-1338]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`copyoffload_config` uses `pytest.fail()` for validation inside a fixture, which is disallowed by the compliance checklist.
## Issue Context
Skipping should be done declaratively (e.g., collection-time markers / `@pytest.mark.skipif`), and configuration validation should use appropriate exceptions rather than `pytest.fail()` inside fixtures.
## Fix Focus Areas
- conftest.py[1324-1338]
- conftest.py[1351-1356]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. py_config.get() has default ☑ 📘 Rule violation ✓ Correctness
Description
pytest_collection_modifyitems uses py_config.get("source_provider", ""), providing a default for
controlled configuration. This violates the requirement to avoid defaults for project-controlled
config keys to prevent silent misconfiguration.
Code

conftest.py[R285-288]

+        providers_json_path = config.getoption("providers_json", default=None)
+        providers = load_source_providers(providers_json_path=providers_json_path)
+        source_provider_type = providers.get(py_config.get("source_provider", ""), {}).get("type")
+
Evidence
PR Compliance ID 23 forbids using .get() defaults for controlled configuration like py_config;
the new logic supplies a default empty string for source_provider.

CLAUDE.md
conftest.py[285-288]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Controlled configuration (`py_config`) is accessed using `.get(..., default)` in the collection hook, which can mask misconfiguration.
## Issue Context
Compliance requires direct key access for project-controlled configuration to avoid silently falling back to defaults.
## Fix Focus Areas
- conftest.py[285-288]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. py_config.get() has default ☑ 📘 Rule violation ✓ Correctness
Description
pytest_collection_modifyitems uses py_config.get("source_provider", ""), providing a default for
controlled configuration. This violates the requirement to avoid defaults for project-controlled
config keys to prevent silent misconfiguration.
Code

conftest.py[R285-288]

+        providers_json_path = config.getoption("providers_json", default=None)
+        providers = load_source_providers(providers_json_path=providers_json_path)
+        source_provider_type = providers.get(py_config.get("source_provider", ""), {}).get("type")
+
Evidence
PR Compliance ID 23 forbids using .get() defaults for controlled configuration like py_config;
the new logic supplies a default empty string for source_provider.

CLAUDE.md
conftest.py[285-288]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Controlled configuration (`py_config`) is accessed using `.get(..., default)` in the collection hook, which can mask misconfiguration.
## Issue Context
Compliance requires direct key access for project-controlled configuration to avoid silently falling back to defaults.
## Fix Focus Areas
- conftest.py[285-288]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. New __init__ lacks docstring ☑ 📘 Rule violation ✓ Correctness
Description
The new ProviderEmptyContentError.__init__ method lacks a docstring with the required Args,
Returns, and Raises sections. This violates the docstring requirements for newly added
functions.
Code

exceptions/exceptions.py[R54-56]

+class ProviderEmptyContentError(Exception):
+    def __init__(self, path: str) -> None:
+        super().__init__(f"Providers JSON file is empty: '{path}'")
Evidence
PR Compliance ID 17 requires newly added functions to include complete docstrings; the newly added
__init__ method has none.

CLAUDE.md
exceptions/exceptions.py[54-56]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A newly added function (`ProviderEmptyContentError.__init__`) is missing the required docstring structure.
## Issue Context
Compliance requires `Args:`, `Returns:`, and `Raises:` sections for all newly added functions.
## Fix Focus Areas
- exceptions/exceptions.py[54-56]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. New __init__ lacks docstring ☑ 📘 Rule violation ✓ Correctness
Description
The new ProviderEmptyContentError.__init__ method lacks a docstring with the required Args,
Returns, and Raises sections. This violates the docstring requirements for newly added
functions.
Code

exceptions/exceptions.py[R54-56]

+class ProviderEmptyContentError(Exception):
+    def __init__(self, path: str) -> None:
+        super().__init__(f"Providers JSON file is empty: '{path}'")
Evidence
PR Compliance ID 17 requires newly added functions to include complete docstrings; the newly added
__init__ method has none.

CLAUDE.md
exceptions/exceptions.py[54-56]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A newly added function (`ProviderEmptyContentError.__init__`) is missing the required docstring structure.
## Issue Context
Compliance requires `Args:`, `Returns:`, and `Raises:` sections for all newly added functions.
## Fix Focus Areas
- exceptions/exceptions.py[54-56]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

12. Utility validates config path 📘 Rule violation ⛯ Reliability
Description
resolve_providers_json_path() performs required configuration validation by raising
FileNotFoundError in a utility function. The checklist requires required inputs/config to be
validated in fixtures (at the source) rather than utilities.
Code

utilities/utils.py[R46-70]

+def resolve_providers_json_path(cli_path: str | None = None) -> str:
+    """Resolve and validate the providers JSON file path.
+
+    Resolution order:
+        1. ``cli_path`` argument (e.g. from ``--providers-json`` pytest CLI arg).
+        2. ``PROVIDERS_JSON_PATH`` environment variable.
+        3. Default ``.providers.json`` in the current working directory.
+
+    Args:
+        cli_path (str | None): Explicit path from CLI argument.
+
+    Returns:
+        str: The resolved file path.
+
+    Raises:
+        FileNotFoundError: If the resolved file does not exist.
+    """
+    if cli_path is not None:
+        resolved = cli_path
+    else:
+        resolved = os.environ.get("PROVIDERS_JSON_PATH", DEFAULT_PROVIDERS_JSON_PATH)
+
+    if not Path(resolved).is_file():
+        raise FileNotFoundError(f"Providers JSON file not found: '{resolved}'")
+
Evidence
PR Compliance ID 18 states validation of required config should occur in fixtures, but the newly
added utility helper validates existence of the required providers file and raises directly from the
utility layer.

CLAUDE.md
utilities/utils.py[46-70]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A utility function (`resolve_providers_json_path`) performs required configuration validation, which the checklist prefers to be validated in fixtures at the point of configuration origin.
## Issue Context
Centralizing required-config validation in fixtures ensures consistent early failure behavior and keeps utilities focused on reusable logic.
## Fix Focus Areas
- utilities/utils.py[46-70]
- conftest.py[425-427]
- conftest.py[670-673]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


13. Repeated providers path resolution 📘 Rule violation ⛯ Reliability
Description
The same resolve_providers_json_path(cli_path=request.config.getoption("providers_json")) line is
duplicated across multiple fixtures. This violates the no-duplicate-code requirement and increases
maintenance cost.
Code

conftest.py[R672-673]

+    providers_path = resolve_providers_json_path(cli_path=request.config.getoption("providers_json"))
+
Evidence
PR Compliance ID 15 requires extracting identical logic used 2+ times; the providers path resolution
statement appears in multiple fixtures unchanged.

CLAUDE.md
conftest.py[672-673]
conftest.py[1330-1331]
conftest.py[1428-1430]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Providers JSON path resolution is duplicated across multiple fixtures.
## Issue Context
The exact same statement is repeated in `source_provider_data`, `copyoffload_config`, and `copyoffload_storage_secret`. Centralizing it will reduce duplication and ensure consistent behavior.
## Fix Focus Areas
- conftest.py[652-686]
- conftest.py[1315-1331]
- conftest.py[1402-1430]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


14. Docs miss dry-run caveat 🐞 Bug ✓ Correctness
Description
CLAUDE.md states provider-type skip logic runs in pytest_collection_modifyitems and that markers are
required for collection-time skipping, but conftest.py skips that logic entirely when pytest is in
dry-run mode (collect-only/setupplan). This can mislead contributors validating skip behavior during
collection-only runs, because the documented MUST behavior will not occur in those modes.
Code

CLAUDE.md[R603-610]

+**Marker requirements for collection-time skipping (MUST):**
+
+Provider-type skip logic runs in `pytest_collection_modifyitems` (in `conftest.py`) and matches tests by marker keywords. Tests MUST use the correct markers for skipping to work:
+
+- Warm migration tests → `@pytest.mark.warm`
+- Copy-offload snapshot tests → `@pytest.mark.copyoffload_snapshots`
+
+See the documented hook in `conftest.py` for how to add new provider-type skip rules.
Evidence
CLAUDE.md asserts provider-type skip logic runs in pytest_collection_modifyitems and relies on
marker keywords, but conftest.py wraps all provider-type marking/skipping in `if not
is_dry_run(config). is_dry_run() explicitly returns True for --collect-only`, so the documented
collection-time skipping guidance does not apply in that mode.

CLAUDE.md[593-610]
conftest.py[261-316]
utilities/pytest_utils.py[40-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`CLAUDE.md` states that provider-type collection-time skipping runs in `pytest_collection_modifyitems` and that markers MUST be applied for skipping to work, but the actual implementation in `conftest.py` deliberately disables that logic during dry-run modes (`--collect-only`, `--setup-plan`). This makes the contributor documentation misleading for those workflows.
### Issue Context
The skip logic is wrapped in `if not is_dry_run(config):`, and `is_dry_run()` returns true for `collectonly`.
### Fix Focus Areas
- CLAUDE.md[603-610]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (4)
15. Hook mixes skip and naming 📘 Rule violation ⛯ Reliability
Description
New provider-type skipping logic was added into pytest_collection_modifyitems, which already
performs unrelated item renaming and VM collection. This increases complexity and makes the hook
harder to maintain.
Code

conftest.py[R262-318]

+    # -------------------------------------------------------------------
+    # Provider-type based test skipping at collection time.
+    #
+    # This section conditionally skips or marks tests based on the source
+    # provider type (e.g., vSphere, RHV, OpenStack). It runs during
+    # pytest collection so that incompatible tests are excluded before
+    # execution begins.
+    #
+    # Why here (not at module level): The provider JSON path comes from
+    # the --providers-json CLI arg, which is only available via `config`
+    # inside pytest hooks.
+    #
+    # How to add a new skip rule:
+    #   1. Register the marker in pytest.ini under [pytest] > markers.
+    #   2. Add a condition block below following the existing pattern:
+    #        - Check source_provider_type against Provider.ProviderType.*
+    #        - Create a skip/jira marker with a descriptive reason
+    #        - Iterate over items and add the marker where the keyword matches
+    #   3. Apply the marker to the relevant test class, e.g.:
+    #        @pytest.mark.my_new_marker
+    #        class TestMyFeature: ...
+    # -------------------------------------------------------------------
+    if not is_dry_run(config):
+        providers_json_path = config.getoption("providers_json", default=None)
+        providers = load_source_providers(providers_json_path=providers_json_path)
+        # .get() with default is intentional: source_provider may not be configured (e.g., partial config),
+        # in which case provider-type gating is silently skipped.
+        source_provider_type = providers.get(py_config.get("source_provider", ""), {}).get("type")
+
+        if source_provider_type:
+            # Skip warm migration tests for providers that do not support warm migration.
+            warm_unsupported = (
+                Provider.ProviderType.OPENSTACK,
+                Provider.ProviderType.OPENSHIFT,
+                Provider.ProviderType.OVA,
+            )
+            if source_provider_type in warm_unsupported:
+                warm_skip = pytest.mark.skip(reason=f"{source_provider_type} warm migration is not supported.")
+                for item in items:
+                    if "warm" in item.keywords:
+                        item.add_marker(warm_skip)
+
+            # Mark RHV warm migration tests with a Jira issue (disables execution until resolved).
+            if source_provider_type == Provider.ProviderType.RHV:
+                for item in items:
+                    if "warm" in item.keywords:
+                        item.add_marker(pytest.mark.jira("MTV-2846", run=False))
+
+            # Skip copy-offload tests for non-vSphere providers (vSphere-only feature).
+            if source_provider_type != Provider.ProviderType.VSPHERE:
+                copyoffload_skip = pytest.mark.skip(
+                    reason="Copy-offload tests are only applicable to vSphere source providers"
+                )
+                for item in items:
+                    if "copyoffload" in item.keywords:
+                        item.add_marker(copyoffload_skip)
+
Evidence
PR Compliance ID 9 expects single-responsibility functions and recommends extracting sub-tasks;
pytest_collection_modifyitems now contains both provider-type skip logic and separate
item-renaming/VM-aggregation logic in the same hook.

CLAUDE.md
conftest.py[261-318]
conftest.py[319-342]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pytest_collection_modifyitems` now performs multiple unrelated tasks (provider-type skip/mark logic plus test renaming and VM aggregation), making it harder to maintain.
## Issue Context
This PR added a substantial new section for provider gating into an already non-trivial hook. Per guidelines, sub-tasks should be extracted into focused helpers.
## Fix Focus Areas
- conftest.py[261-346]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


16. Skips skipped in collect-only 🐞 Bug ✓ Correctness
Description
Provider-type skip/jira markers are not applied under --collect-only because
pytest_collection_modifyitems() gates this logic behind not is_dry_run(config) and is_dry_run()
returns True when collectonly is set. As a result, collection-only output/marker inspection cannot
reflect provider compatibility gating even when a valid providers file/path is provided.
Code

conftest.py[R284-285]

+    if not is_dry_run(config):
+        providers_json_path = config.getoption("providers_json", default=None)
Evidence
The hook explicitly skips provider-type gating whenever is_dry_run() is true, and is_dry_run()
includes collect-only; therefore no skip/jira markers are added during collect-only runs.

conftest.py[261-291]
utilities/pytest_utils.py[40-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
During `--collect-only`, provider-type gating markers are never applied because `pytest_collection_modifyitems` is guarded by `not is_dry_run(config)` and `is_dry_run` treats `collectonly` as dry-run.
### Issue Context
This prevents collection-only workflows from reflecting provider compatibility gating (skip/jira markers) even when users explicitly pass `--providers-json`/`PROVIDERS_JSON_PATH`.
### Fix Focus Areas
- conftest.py[261-318]
- utilities/pytest_utils.py[40-50]
Suggested approach:
- Replace `if not is_dry_run(config):` with a more targeted guard (e.g., skip only `setupplan`), OR
- In collect-only mode, try to apply gating when a providers path is explicitly provided; if loading fails (missing file), skip the gating rather than disabling it unconditionally.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


17. Utility validates config path 📘 Rule violation ⛯ Reliability
Description
resolve_providers_json_path() performs required configuration validation by raising
FileNotFoundError in a utility function. The checklist requires required inputs/config to be
validated in fixtures (at the source) rather than utilities.
Code

utilities/utils.py[R46-70]

+def resolve_providers_json_path(cli_path: str | None = None) -> str:
+    """Resolve and validate the providers JSON file path.
+
+    Resolution order:
+        1. ``cli_path`` argument (e.g. from ``--providers-json`` pytest CLI arg).
+        2. ``PROVIDERS_JSON_PATH`` environment variable.
+        3. Default ``.providers.json`` in the current working directory.
+
+    Args:
+        cli_path (str | None): Explicit path from CLI argument.
+
+    Returns:
+        str: The resolved file path.
+
+    Raises:
+        FileNotFoundError: If the resolved file does not exist.
+    """
+    if cli_path is not None:
+        resolved = cli_path
+    else:
+        resolved = os.environ.get("PROVIDERS_JSON_PATH", DEFAULT_PROVIDERS_JSON_PATH)
+
+    if not Path(resolved).is_file():
+        raise FileNotFoundError(f"Providers JSON file not found: '{resolved}'")
+
Evidence
PR Compliance ID 18 states validation of required config should occur in fixtures, but the newly
added utility helper validates existence of the required providers file and raises directly from the
utility layer.

CLAUDE.md
utilities/utils.py[46-70]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A utility function (`resolve_providers_json_path`) performs required configuration validation, which the checklist prefers to be validated in fixtures at the point of configuration origin.
## Issue Context
Centralizing required-config validation in fixtures ensures consistent early failure behavior and keeps utilities focused on reusable logic.
## Fix Focus Areas
- utilities/utils.py[46-70]
- conftest.py[425-427]
- conftest.py[670-673]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


18. Docs miss dry-run caveat 🐞 Bug ✓ Correctness
Description
CLAUDE.md states provider-type skip logic runs in pytest_collection_modifyitems and that markers are
required for collection-time skipping, but conftest.py skips that logic entirely when pytest is in
dry-run mode (collect-only/setupplan). This can mislead contributors validating skip behavior during
collection-only runs, because the documented MUST behavior will not occur in those modes.
Code

CLAUDE.md[R603-610]

+**Marker requirements for collection-time skipping (MUST):**
+
+Provider-type skip logic runs in `pytest_collection_modifyitems` (in `conftest.py`) and matches tests by marker keywords. Tests MUST use the correct markers for skipping to work:
+
+- Warm migration tests → `@pytest.mark.warm`
+- Copy-offload snapshot tests → `@pytest.mark.copyoffload_snapshots`
+
+See the documented hook in `conftest.py` for how to add new provider-type skip rules.
Evidence
CLAUDE.md asserts provider-type skip logic runs in pytest_collection_modifyitems and relies on
marker keywords, but conftest.py wraps all provider-type marking/skipping in `if not
is_dry_run(config). is_dry_run() explicitly returns True for --collect-only`, so the documented
collection-time skipping guidance does not apply in that mode.

CLAUDE.md[593-610]
conftest.py[261-316]
utilities/pytest_utils.py[40-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`CLAUDE.md` states that provider-type collection-time skipping runs in `pytest_collection_modifyitems` and that markers MUST be applied for skipping to work, but the actual implementation in `conftest.py` deliberately disables that logic during dry-run modes (`--collect-only`, `--setup-plan`). This makes the contributor documentation misleading for those workflows.
### Issue Context
The skip logic is wrapped in `if not is_dry_run(config):`, and `is_dry_run()` returns true for `collectonly`.
### Fix Focus Areas
- CLAUDE.md[603-610]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment thread conftest.py Resolved
Comment thread conftest.py Resolved
Comment thread conftest.py Resolved
Comment thread conftest.py Resolved
Comment thread exceptions/exceptions.py Resolved

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLAUDE.md`:
- Around line 612-620: The document currently mandates class-name rules with
"MUST" under "Test class naming for marker-based features (MUST)"; change the
wording to a recommendation or add an enforcement mechanism: either replace
"MUST" with "SHOULD" (or "Recommended") and rephrase the bullets (e.g., Warm →
include `Warm`, Copy-offload snapshots → include `Copyoffload` and `Snapshot`,
Copy-offload → include `Copyoffload`) to make them discoverability guidance, or
keep "MUST" and add a clear enforcement note and link to the suite check (e.g.,
add a test/linter that validates class names like TestSanityWarmMtvMigration,
TestCopyoffloadThinSnapshotsMigration, TestCopyoffloadThinMigration); ensure the
header and examples are updated accordingly so the intent is unambiguous.

In `@conftest.py`:
- Around line 308-315: The collection hook currently only skips items with the
"copyoffload_snapshots" keyword, so copy-offload tests marked with
pytest.mark.copyoffload or using the copyoffload_config fixture still get
collected and later fail; update the hook to also detect and skip items with the
"copyoffload" marker and/or items that list the copyoffload_config fixture
(check item.keywords for "copyoffload" and/or "copyoffload_config") when
source_provider_type != Provider.ProviderType.VSPHERE, and apply the same
snapshot_skip marker (rename to a generic copyoffload_skip if you like) to avoid
runtime pytest.fail() in the copyoffload_config fixture.
- Line 1313: Add full type annotations to the changed fixture signatures:
annotate copyoffload_config(source_provider: str, source_provider_data:
Dict[str, Any], request: pytest.FixtureRequest) -> Dict[str, Any] and likewise
annotate copyoffload_storage_secret(source_provider: str, source_provider_data:
Dict[str, Any], request: pytest.FixtureRequest) -> Dict[str, Any]; import typing
types (from typing import Any, Dict) and pytest.FixtureRequest (or
_pytest.fixtures.FixtureRequest) as needed so both parameters and the return
types are fully typed per the guideline.

In `@README.md`:
- Around line 146-153: The README currently reintroduces a hardcoded expectation
by instructing users to "Create a `.providers.json` file in your current
directory"; change this to clarify that the default is a file named
`.providers.json` in the current working directory but that it can be overridden
by the CLI flag `--providers-json` or the environment variable
`PROVIDERS_JSON_PATH` (with priority CLI > ENV > default), and add a short note
for container/OpenShift users to ensure the mounted file path matches the
runtime path provided to `--providers-json` or `PROVIDERS_JSON_PATH` rather than
assuming the repository root.

In `@utilities/utils.py`:
- Around line 90-94: The function currently returns parsed JSON from
json.loads(content) without validating its shape, which can lead to later
failures when callers expect a mapping; after calling json.loads(content) (near
resolved_path and ProviderEmptyContentError), verify the result is a dict
(isinstance(result, dict)) and if not raise a ValueError with a clear message
that the provider config must be a JSON object (include the resolved_path and
the actual top-level type in the message) so callers like conftest.py don't
receive lists/null/strings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b8bc5d53-f4e4-4f28-aa56-a8b076c11853

📥 Commits

Reviewing files that changed from the base of the PR and between 4e0f502 and d458e09.

📒 Files selected for processing (9)
  • CLAUDE.md
  • README.md
  • conftest.py
  • exceptions/exceptions.py
  • pytest.ini
  • tests/test_copyoffload_migration.py
  • tests/test_mtv_warm_migration.py
  • tests/test_warm_migration_comprehensive.py
  • utilities/utils.py

Comment thread CLAUDE.md Resolved
Comment thread conftest.py Resolved
Comment thread conftest.py Resolved
Comment thread README.md Resolved
Comment thread utilities/utils.py Resolved
coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 9, 2026
- Add type annotations to copyoffload fixtures
- Skip all copyoffload tests for non-vSphere providers (not just snapshots)
- Add JSON shape validation in load_source_providers()
- Add docstrings to custom exception classes
- Add inline comment explaining py_config.get() default usage
- Change test class naming rule from MUST to SHOULD
- Improve README provider file documentation wording
@qodo-code-review

qodo-code-review Bot commented Mar 9, 2026 •

Copy link
Copy Markdown

Persistent review updated to latest commit 6ed8f10

Comment thread conftest.py Resolved
@qodo-code-review

qodo-code-review Bot commented Mar 9, 2026 •

Copy link
Copy Markdown

Persistent review updated to latest commit 6ed8f10

@myakove
myakove merged commit 576d8d6 into main Mar 9, 2026
5 of 7 checks passed
@myakove
myakove deleted the feat/issue-322-configurable-providers-json-path branch March 9, 2026 15:41
@redhat-qe-bot1

Copy link
Copy Markdown

Failed to build and push ghcr.io/redhatqe/mtv-api-tests:latest

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Allow users to configure the providers.json file path instead of hardcoding .providers.json

5 participants