refactor(conftest): lift orch fixture into cvs/tests/conftest.py + fix report_plugins JSON-escape regression [AIMVT-172]#162
Merged
Conversation
No-op for current consumers (rvs_cvs.py still has its own local fixture which pytest resolves first). Pairs with the follow-up that removes the local fixture so the conftest one wins.
Deletes the module-local orch fixture and its now-unused OrchestratorConfig / OrchestratorFactory imports. The conftest fixture from cvs/tests/conftest.py is auto-injected unchanged.
… JSON escapes
`_update_environment_config_links` mutated pytest-html's embedded
`data-jsonblob` dict (to add clickable cluster/config file links), then
re-serialized it with `re.sub(pattern, f'data-jsonblob="{encoded_json}"',
html_content)`. Python's `re.sub` processes backslash escapes in STRING
replacements (per re docs: "If repl is a string, any backslash escapes
in it are processed. That is, `\n` is converted to a single newline
character..."). The replacement here contains the JSON-encoded log
content from `json.dumps`, where multi-line strings appear as 2-char
`\n` / `\t` escape sequences. `re.sub` collapsed those into raw 0x0A /
0x09 bytes inside JSON string values, violating RFC 8259 §7 and
breaking `JSON.parse` in the browser. Net visible effect: the
pytest-html results table renders with only its header row -- no test
rows -- because the JS renderer aborts before injecting them.
Function-form replacements are NOT processed for backslash escapes
(per re docs: "If repl is a function ... it is called for every
non-overlapping occurrence of pattern ... and returns the replacement
string"). Switching to a 1-arg lambda preserves the encoded JSON
verbatim.
Verified by /tmp/verify_report_plugins_fix.py (standalone unit test):
buggy path produces blob with 4 raw 0x0A + 3 raw 0x09 -> JSONDecodeError
at offset 463 ("Invalid control character at"); fixed path produces
blob with 0 control chars, json.loads succeeds, log content and env
links both preserved. RESULT = PASS.
This is the regression that emptied the results table in rvs_cvs
container-orchestrator HTML reports; baremetal escaped the bug because
its empty `_config_files` triggered the early-exit at line 301, so the
buggy `re.sub` never ran on baremetal reports.
No siblings of this bug pattern exist elsewhere in the CVS python tree
(audited via 4 total `re.sub` call sites; this was the only HIGH-risk
one).
cijohnson
requested changes
May 12, 2026
cijohnson
approved these changes
May 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Jira: AIMVT-172
Summary
orchpytest fixture into a newcvs/tests/conftest.py. Pytest walks the conftest hierarchy upward, so suites undercvs/tests/pick it up automatically with zero per-suiteboilerplate.orchfixture and its now-unusedOrchestratorConfig/OrchestratorFactoryimports fromcvs/tests/health/rvs_cvs.py; the conftest one takesover.re.subreplacement incvs/lib/report_plugins.pywith a function-form lambda so JSON\n/\tescapes insidedata-jsonblobare preserved verbatim. Without this, the rvs_cvs HTML report in container mode renders with an empty results table.Why
The
orchfixture is the single seam between a pytest suite and the orchestrator factory introduced by #135. Astransferbench_cvs,agfhc_cvs, and friends migrate offphdl/Pssh(AIMVT-168 through AIMVT-171), every one of them would otherwise copy-paste the same ~15-line fixture body + the same two imports + the same container setup/teardown wiring. Lifting it once keeps that surface single-sourced; subsequent suite migrations become a mechanicalphdl→orchrename. The lifted fixture is identical to the original except it gains an explicit CLI-arg guard:The previous local fixture raised a NoneType deep inside
OrchestratorConfig.from_configswhen invoked without--cluster_file/--config_file; the explicitpytest.fail(...)turns that into an actionable message at the fixture boundary.The
report_pluginschange addresses a latent regression in_update_environment_config_links: the function mutates pytest-html's embeddeddata-jsonblobdict to inject clickable cluster/config-file links, then re-serializes viare.sub(pattern, replacement_string, html_content). Per theredocs, string-form replacements have backslash escapes processed, which collapses the JSON-encoded\n/\t2-char escapes into raw0x0A/0x09bytes inside JSON string values, breakingJSON.parsein the browser. Function-form (lambda) replacements skip that processing.What is NOT changed
--cluster_file/--config_fileare still registered incvs/conftest.py:31-53; the newcvs/tests/conftest.pyinherits them via pytest's conftest walk.cvs/tests/is touched.transferbench_cvs/agfhc_cvs/others are queued under AIMVT-168 through AIMVT-171.docs/how-to/run-with-containers.rst,docs/reference/configuration-files/cluster-file.rst,cvs/input/cluster_file/README.md).addfinalizer, no eagerget_container_name, nodetailed=True/hosts=/exit-code-check additions onorch.exec(...).cluster_file/config_filefixtures inrvs_cvs.py:24-31left alone —cluster_dict/config_dictstill consume them.Verification
python -m pytest cvs/core/orchestrators/unittests/ cvs/core/runtimes/unittests/ -q— green.rg -n '\bdef orch\b|OrchestratorConfig|OrchestratorFactory' cvs/tests/health/rvs_cvs.py— 0 hits.cvs run rvs_cvslevel-4 on an 8x MI300X node, bothorchestrator: baremetalandorchestrator: container—both2 passed, 6 skipped, HTML reports render cleanly. Run zips (auto-bundles) are attached to AIMVT-172.Commits
053fe60test: lift orch fixture into cvs/tests/conftest.py2e7bcb4test(rvs_cvs): consume conftest-lifted orch fixture79cab56fix(report_plugins): use function-form re.sub replacement to preserve JSON escapes