Skip to content

Support path-based test references in scenario files - #994

Open
shreyaskommuri wants to merge 1 commit into
NVIDIA:mainfrom
shreyaskommuri:issue/path-based-test-references
Open

Support path-based test references in scenario files#994
shreyaskommuri wants to merge 1 commit into
NVIDIA:mainfrom
shreyaskommuri:issue/path-based-test-references

Conversation

@shreyaskommuri

Copy link
Copy Markdown
Contributor

Summary

  • Adds an optional path field on TestRunModel, an alternative to test_name for referencing a test in a scenario file, resolved relative to the scenario file's own directory (confirmed shape in Support relative-path test references and lazy-load only referenced test/hook tomls #985).
  • test_name and path are mutually exclusive, same rules test_name already has with test_template_name.
  • TestScenarioParser._prepare_tdef gets a new branch: when path is set, it loads that toml file directly instead of doing a name lookup against test_mapping, then merges scenario-level overrides the same way the test_name branch already does.
  • Raises a clear TestScenarioParsingError if the resolved path does not exist.
  • This is PR1 of the two-PR plan discussed in Support relative-path test references and lazy-load only referenced test/hook tomls #985. PR2 (skip eager glob-and-parse of --tests-dir/hooks when a scenario's tests are all path-referenced) depends on this one and is not included here, keeping this PR small and reviewable on its own.
  • Fully backward compatible: test_name and fully-inline (test_template_name + name + description) scenarios are unaffected.

Test Plan

7 new tests in tests/test_test_scenario.py (TestPathReference), covering: path/test_name mutual exclusion, path/test_template_name mutual exclusion, relative-path resolution against the scenario file's directory, scenario-level override merging over the referenced file, a missing-file error, and a full scenario TOML parsed end to end with a path reference.

$ uv run pytest tests/test_test_scenario.py -q -k "TestPathReference"
.......
7 passed, 74 deselected in 0.04s

$ uv run pytest -q -m "not ci_only"
1848 passed, 5 skipped, 511 deselected in 4.47s

$ uv run pre-commit run --all-files
check for added large files..............................................Passed
check python ast.........................................................Passed
check for merge conflicts.................................................Passed
check toml................................................................Passed
check yaml.................................................................Passed
debug statements (python)..................................................Passed
fix end of files...........................................................Passed
mixed line ending..........................................................Passed
trim trailing whitespace...................................................Passed
pyright.....................................................................Passed
ruff check...................................................................Passed
ruff format...................................................................Passed
vulture.......................................................................Passed
import-linter..................................................................Passed
taplo...........................................................................Passed

Also updated the wording of two pre-existing validation error messages (in models/scenario.py) that referenced only test_name, so they stay accurate now that path is a second way to satisfy the same requirement. Updated the two existing tests in tests/test_test_scenario.py that asserted on the old wording.

Additional Notes

Design confirmed in #985 before writing any code, per CONTRIBUTING.md's "communicate with the main developers before starting work." Not touching the eager-loading behavior itself, sweeping, or anything unrelated, this PR is purely additive.

Add an optional 'path' field on TestRunModel, mutually exclusive with
'test_name', resolved relative to the scenario file's own directory.
When set, _prepare_tdef loads that file directly instead of doing a
name lookup against test_mapping, with the same scenario-level
override merging test_name already supports.

Ref: NVIDIA#985
Signed-off-by: shreyaskommuri <shreyaskommuri@gmail.com>
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

TestRunModel now accepts a scenario-relative TOML path as an alternative test reference. The parser resolves the path, loads the definition, applies scenario overrides, and reports missing files. Tests cover validation, loading, merging, and end-to-end parsing.

Changes

Scenario test path references

Layer / File(s) Summary
Test reference contract
src/cloudai/models/scenario.py
TestRunModel adds the optional path field. Validation prevents conflicting references and updates inline-definition and template-name rules.
Path loading and validation
src/cloudai/test_scenario_parser.py, tests/test_test_scenario.py
TestScenarioParser resolves and loads scenario-relative test TOML files, merges overrides, and raises TestScenarioParsingError for missing files. Tests cover validation, path resolution, merging, errors, and full scenario parsing.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers: podkidyshev, jj10306

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: support for path-based test references in scenario files.
Description check ✅ Passed The description directly explains the path reference feature, validation rules, parser behavior, tests, and compatibility impact.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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: 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 `@src/cloudai/models/scenario.py`:
- Around line 74-80: Update the Optional path field in TestRunModel to enforce
min_length=1, rejecting empty strings during validation while preserving None as
valid. Add a regression test covering path="" and verify validation fails before
_prepare_tdef is reached.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 5c512b3e-96c4-4630-84b6-898ffd08b847

📥 Commits

Reviewing files that changed from the base of the PR and between 5b5c0f7 and 1b62b6c.

📒 Files selected for processing (3)
  • src/cloudai/models/scenario.py
  • src/cloudai/test_scenario_parser.py
  • tests/test_test_scenario.py

Comment on lines +74 to +80
path: Optional[str] = Field(
default=None,
description=(
"Path to a test TOML file, resolved relative to this scenario file's own directory. "
"Alternative to 'test_name': references a test by file location instead of by name."
),
)

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For Pydantic 2.12, does Field(min_length=1) reject an empty string while allowing None for a str | None field?

💡 Result:

Yes, in Pydantic 2.12, Field(min_length=1) will reject an empty string while allowing None for a str | None field [1][2][3]. When you define a field as str | None (or Optional[str]), Pydantic understands that the field accepts either a string or a None value [2]. Constraints applied via Field—such as min_length=1—are automatically applied specifically to the string member of the union [1][2]. Consequently: - An input of None will satisfy the validation (assuming the field is nullable) [1][3]. - An input of "" (empty string) will be validated against the min_length=1 constraint, which it will fail, triggering a validation error [4][3]. This behavior is standard for Pydantic V2 when working with Union types containing None [2]. If you ever need an empty string to be treated as None, you must handle that conversion explicitly using a validator, as Pydantic treats an empty string as a valid string value, not a missing value [5][6].

Citations:


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(scenario\.py|pyproject\.toml|.*test.*scenario.*|.*parser.*\.py)$' | head -100
printf '%s\n' '--- scenario outline ---'
ast-grep outline src/cloudai/models/scenario.py 2>/dev/null || true
printf '%s\n' '--- relevant symbols ---'
rg -n -C 5 'class TestScenarioParser|def _prepare_tdef|test_info\.path|path: Optional\[str\]|min_length|pydantic' src pyproject.toml 2>/dev/null | head -240

Repository: NVIDIA/cloudai

Length of output: 19582


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- scenario model ---'
sed -n '54,175p' src/cloudai/models/scenario.py
printf '%s\n' '--- parser method ---'
sed -n '205,270p' src/cloudai/test_scenario_parser.py
printf '%s\n' '--- scenario tests ---'
rg -n -C 6 'path|test_name|TestRunModel|TestScenarioParsingError|ValueError' tests/test_test_scenario.py tests/test_test_scenario_parser.py
printf '%s\n' '--- isolated Pydantic behavior ---'
python3 - <<'PY'
try:
    import pydantic
    from typing import Optional
    from pydantic import BaseModel, Field, ValidationError
    print("pydantic_version=", pydantic.__version__)

    class Probe(BaseModel):
        path: Optional[str] = Field(default=None, min_length=1)

    for value in (None, "", "x"):
        try:
            result = Probe(path=value)
            print(repr(value), "accepted", repr(result.path))
        except ValidationError as exc:
            print(repr(value), "rejected", exc.errors()[0]["type"])
except Exception as exc:
    print(type(exc).__name__ + ":", exc)
PY

Repository: NVIDIA/cloudai

Length of output: 45874


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- parser callers and error handling ---'
rg -n -C 8 '_prepare_tdef|TestScenarioParsingError|ValidationError|parse_data|_create_test_run' src/cloudai/test_scenario_parser.py src/cloudai/parser.py src/cloudai/_core tests/test_test_scenario.py | head -320
printf '%s\n' '--- exception definitions ---'
rg -n -C 8 'class TestScenarioParsingError|class .*ParsingError' src/cloudai

Repository: NVIDIA/cloudai

Length of output: 25181


Reject empty path values.

When path="", TestRunModel accepts the value, but _prepare_tdef selects no branch and raises an unwrapped ValueError. Set min_length=1 and add a regression test.

🤖 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 `@src/cloudai/models/scenario.py` around lines 74 - 80, Update the Optional
path field in TestRunModel to enforce min_length=1, rejecting empty strings
during validation while preserving None as valid. Add a regression test covering
path="" and verify validation fails before _prepare_tdef is reached.

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.

1 participant