[DSPX-3302] (4/5) xtest conftest: --scenario and --instance flags#453
[DSPX-3302] (4/5) xtest conftest: --scenario and --instance flags#453dmihalcik-virtru wants to merge 19 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces new pytest CLI options, --scenario and --instance, and implements logic in pytest_configure to handle scenario-based defaults and environment variable propagation. The review feedback suggests using explicit is None checks for CLI options to ensure that user-provided empty strings are respected as overrides, rather than being ignored in favor of defaults. Additionally, a redundant import os statement was identified for removal.
| import os | ||
|
|
||
| instance = config.getoption("--instance") | ||
| if instance: | ||
| os.environ["OTDF_LOCAL_INSTANCE_NAME"] = instance |
There was a problem hiding this comment.
The import os statement is redundant as it is already imported at the top of the file (line 18). Additionally, the check if instance: should be replaced with if instance is not None: to correctly handle cases where an empty string is explicitly passed via the CLI, ensuring it is respected and not ignored in favor of scenario defaults.
| import os | |
| instance = config.getoption("--instance") | |
| if instance: | |
| os.environ["OTDF_LOCAL_INSTANCE_NAME"] = instance | |
| instance = config.getoption("--instance") | |
| if instance is not None: | |
| os.environ["OTDF_LOCAL_INSTANCE_NAME"] = instance |
References
- Redundant imports should be removed to maintain code cleanliness and avoid confusion.
- When implementing configuration overrides, ensure that explicit user input (even if empty) is prioritized over default values.
| if not config.getoption("--sdks-encrypt") and scenario.sdks.encrypt: | ||
| config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys()) | ||
| if not config.getoption("--sdks-decrypt") and scenario.sdks.decrypt: | ||
| config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys()) | ||
| if not config.getoption("--containers") and scenario.suite.containers: | ||
| config.option.containers = scenario.suite.containers | ||
| if not instance and scenario.instance.metadata.name: | ||
| os.environ["OTDF_LOCAL_INSTANCE_NAME"] = scenario.instance.metadata.name |
There was a problem hiding this comment.
Use is None checks for CLI options to ensure that explicit overrides (including empty strings) are respected and not overwritten by scenario defaults. This maintains the 'CLI wins' principle described in the function's docstring.
| if not config.getoption("--sdks-encrypt") and scenario.sdks.encrypt: | |
| config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys()) | |
| if not config.getoption("--sdks-decrypt") and scenario.sdks.decrypt: | |
| config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys()) | |
| if not config.getoption("--containers") and scenario.suite.containers: | |
| config.option.containers = scenario.suite.containers | |
| if not instance and scenario.instance.metadata.name: | |
| os.environ["OTDF_LOCAL_INSTANCE_NAME"] = scenario.instance.metadata.name | |
| if config.getoption("--sdks-encrypt") is None and scenario.sdks.encrypt: | |
| config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys()) | |
| if config.getoption("--sdks-decrypt") is None and scenario.sdks.decrypt: | |
| config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys()) | |
| if config.getoption("--containers") is None and scenario.suite.containers: | |
| config.option.containers = scenario.suite.containers | |
| if instance is None and scenario.instance.metadata.name: | |
| os.environ["OTDF_LOCAL_INSTANCE_NAME"] = scenario.instance.metadata.name |
References
- When implementing configuration overrides, ensure that explicit user input (even if empty) is prioritized over default values.
There was a problem hiding this comment.
Code Review
This pull request introduces new CLI options, --scenario and --instance, to the pytest configuration and implements a pytest_configure hook to manage scenario-based defaults and environment variable propagation. The review feedback highlights that using falsiness checks for CLI options prevents users from overriding scenario defaults with empty values; it is recommended to use is None checks instead to ensure CLI-passed options always take precedence. Additionally, a redundant local import of the os module was identified.
| import os | ||
|
|
||
| instance = config.getoption("--instance") | ||
| if instance: | ||
| os.environ["OTDF_LOCAL_INSTANCE_NAME"] = instance | ||
|
|
||
| scenario_path = config.getoption("--scenario") | ||
| if not scenario_path: | ||
| return | ||
| try: | ||
| from otdf_sdk_mgr.schema import load_scenario | ||
| except ImportError: | ||
| # otdf-sdk-mgr may not be installed in a minimal pytest env. | ||
| return | ||
| scenario = load_scenario(scenario_path) | ||
| if not config.getoption("--sdks-encrypt") and scenario.sdks.encrypt: | ||
| config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys()) | ||
| if not config.getoption("--sdks-decrypt") and scenario.sdks.decrypt: | ||
| config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys()) | ||
| if not config.getoption("--containers") and scenario.suite.containers: | ||
| config.option.containers = scenario.suite.containers | ||
| if not instance and scenario.instance.metadata.name: | ||
| os.environ["OTDF_LOCAL_INSTANCE_NAME"] = scenario.instance.metadata.name |
There was a problem hiding this comment.
The current implementation uses falsiness checks (if not ...) to determine if CLI options were provided. This prevents users from explicitly overriding scenario defaults with empty values (e.g., --sdks-encrypt "" to run no encryption SDKs), as an empty string is falsy and will be replaced by the scenario's default. Using is None checks ensures that only unprovided options are populated from the scenario file, adhering to the principle that CLI-passed options always win.
Additionally, the local import os is redundant as it is already imported at the top of the file (line 18).
instance = config.getoption("--instance")
if instance is not None:
os.environ["OTDF_LOCAL_INSTANCE_NAME"] = instance
scenario_path = config.getoption("--scenario")
if not scenario_path:
return
try:
from otdf_sdk_mgr.schema import load_scenario
except ImportError:
# otdf-sdk-mgr may not be installed in a minimal pytest env.
return
scenario = load_scenario(scenario_path)
if config.getoption("--sdks-encrypt") is None and scenario.sdks.encrypt:
config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys())
if config.getoption("--sdks-decrypt") is None and scenario.sdks.decrypt:
config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys())
if config.getoption("--containers") is None and scenario.suite.containers:
config.option.containers = scenario.suite.containers
if instance is None and scenario.instance.metadata.name:
os.environ["OTDF_LOCAL_INSTANCE_NAME"] = scenario.instance.metadata.nameThere was a problem hiding this comment.
Code Review
This pull request introduces new --scenario and --instance CLI options to the pytest configuration, along with a new pytest_configure function to handle scenario-based defaults and environment variable propagation. The review identifies a potential issue where the current logic for applying scenario defaults to SDK flags ignores the general --sdks CLI flag, and provides a code suggestion to ensure that CLI-provided options correctly take precedence.
| if not config.getoption("--sdks-encrypt") and scenario.sdks.encrypt: | ||
| config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys()) | ||
| if not config.getoption("--sdks-decrypt") and scenario.sdks.decrypt: | ||
| config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys()) |
There was a problem hiding this comment.
The current logic for applying scenario defaults to --sdks-encrypt and --sdks-decrypt does not account for the general --sdks CLI flag. Since pytest_generate_tests (lines 199-203 and 214-218) uses --sdks as a fallback when the specific flags are missing, programmatically setting config.option.sdks_encrypt or config.option.sdks_decrypt from the scenario file will cause any CLI-provided --sdks value to be ignored. To ensure that "CLI-passed options always win" as stated in the PR description, these defaults should only be applied if neither the specific flag nor the general --sdks flag was provided on the command line.
| if not config.getoption("--sdks-encrypt") and scenario.sdks.encrypt: | |
| config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys()) | |
| if not config.getoption("--sdks-decrypt") and scenario.sdks.decrypt: | |
| config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys()) | |
| if not config.getoption("--sdks-encrypt") and not config.getoption("--sdks") and scenario.sdks.encrypt: | |
| config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys()) | |
| if not config.getoption("--sdks-decrypt") and not config.getoption("--sdks") and scenario.sdks.decrypt: | |
| config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys()) |
There was a problem hiding this comment.
Code Review
This pull request enhances the pytest configuration by adding --scenario and --instance CLI options. A new pytest_configure hook is implemented to propagate the instance name via environment variables and to load default values for SDK and container settings from a scenario file when provided. The review feedback points out a redundant local import of the os module that should be removed to maintain code cleanliness and follow PEP 8 standards.
| via `OTDF_LOCAL_INSTANCE_NAME` so any child `otdf-local` invocation sees | ||
| the same instance. | ||
| """ | ||
| import os |
X-Test Results✅ go-v0.15.0 |
c69afd6 to
a8ef24a
Compare
6d2e83c to
f23ccce
Compare
a8ef24a to
78b2ca6
Compare
f23ccce to
6498765
Compare
#450) ## Summary First PR in a five-part stack that introduces a multi-instance test harness and a Claude plugin for OpenTDF bug reproduction. This PR adds *only* the shared Pydantic schema in `otdf-sdk-mgr` — no consumers yet. - Adds `otdf_sdk_mgr.schema` with v2 models: `Scenario`, `Instance`, `PlatformPin`, `KasPin`, `SdkPin`, `ScenarioSdks`, `Suite`, etc. - `ScenarioSdks.encrypt` / `.decrypt` mirror xtest's existing `--sdks-encrypt` / `--sdks-decrypt` convention so a→b-only scenarios are first-class. - `python -m otdf_sdk_mgr.schema validate <path>` validates either a Scenario or an Instance file based on its `kind:`. - Adds `pydantic` + `ruamel.yaml` to `otdf-sdk-mgr/pyproject.toml`. - 6 unit tests covering round-trips, pin invariants, and unknown-field rejection. ## Stack 1. [**This PR**](#450) — Shared schema 2. [Platform installer + `install scenario`](#451) in `otdf-sdk-mgr` (builds on this) 3. `otdf-local` [multi-instance refactor](#452) + new CLI subcommands 4. `xtest/conftest.py` [integration](#453) (`--scenario`, `--instance`) 5. [Claude plugin](#454) (`.claude/skills/`, settings, plugin manifest) 6. #455 ## Test plan - [x] `cd otdf-sdk-mgr && uv run pytest tests/test_schema.py` — all 6 pass - [x] `uv run python -m otdf_sdk_mgr.schema validate <path>` accepts a valid scenarios.yaml and rejects unknown fields Jira: https://virtru.atlassian.net/browse/DSPX-3302 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added schema validation for OpenTDF Scenario and Instance YAML configurations with a new CLI command. * Introduced strict validation with cross-field constraints for SDK and platform configurations. * **Documentation** * Updated supported container formats from `nano` to `ztdf-ecwrap`. * **Dependencies** * Updated core package dependencies to support enhanced validation capabilities. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/opentdf/tests/pull/450?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
78b2ca6 to
e196e43
Compare
6498765 to
5877740
Compare
X-Test Failure Report |
X-Test Failure Report |
5b1c928 to
a1bcecc
Compare
5877740 to
fe5c8ae
Compare
X-Test Failure Report |
a1bcecc to
e7d13f5
Compare
fe5c8ae to
f4fa9a3
Compare
X-Test Failure Report |
1 similar comment
X-Test Failure Report |
200f430 to
3ba16e3
Compare
757c1d3 to
837416f
Compare
6d83353 to
b441b38
Compare
Refactors otdf-local from a single-instance CLI (one platform checkout,
fixed ports, hardcoded six KAS instances) into a multi-instance harness
where each named instance under tests/instances/<name>/ owns its own
opentdf.yaml, keys, KAS configs, and port range.
Why
---
A single bug report often describes a *combination* — platform v0.9.0
with Java SDK 0.7.8 and a KAS at a pre-release. Today a developer has
to hand-edit configs and re-checkout the platform to reproduce. After
this change:
otdf-local instance init java-078 --from-scenario .../scenario.yaml
otdf-local --instance java-078 up
brings up exactly the topology the scenario describes, using platform
binaries that otdf-sdk-mgr already provisioned (each instance, and each
KAS within an instance, can reference a different pinned version). Two
instances on disjoint ports.base can coexist on a developer laptop.
What changes
------------
otdf-local now depends on otdf-sdk-mgr via a uv path source so both
tools share the canonical Scenario/Instance schema.
Settings (otdf_local.config.settings):
- New instance_name (env-overridable via OTDF_LOCAL_INSTANCE_NAME),
instance_dir, instances_root, instance_yaml properties.
- platform_dir becomes optional; legacy sibling-discovery only kicks
in when no per-instance configuration is present.
- platform_binary_for(dist) resolves to the otdf-sdk-mgr-managed
xtest/platform/dist/<dist>/service binary.
- keys_dir, logs_dir, config_dir, platform_config, and
get_kas_config_path switch to per-instance paths whenever
instance.yaml exists; legacy behavior is preserved otherwise.
- load_instance() reads the per-instance manifest via the shared
Pydantic model.
Ports (otdf_local.config.ports):
- KAS_OFFSETS exposes the offset table (alpha=+101, beta=+202, ...,
km2=+606) so multiple instances on different bases get disjoint
port ranges. The legacy 8080-based constants are preserved as
defaults.
- get_kas_port(name, base=...) computes the port relative to base.
Services (otdf_local.services.platform / .kas):
- PlatformService.start() and KASService.start() use the pinned dist
binary at xtest/platform/dist/<dist>/service when an instance is
loaded, with cwd set to the recorded worktree so the binary finds
its embedded resources. Legacy `go run ./service` path runs
unchanged when no instance is active.
- KASService.is_key_management defers to the manifest's `mode` field
instead of the legacy name-based heuristic; per-KAS features (e.g.
ec_tdf_enabled) pass through to opentdf.yaml.
- KASManager constructs only the KAS instances listed in
instance.yaml's kas: map. start_standard / start_km filter on
is_key_management so subset topologies still work.
utils.keys.setup_golden_keys:
- Writes key files into the target directory (per-instance keys_dir
or legacy platform_dir) and uses absolute paths in the generated
keys_config so the binary finds them regardless of cwd.
CLI:
- New top-level --instance option threads through every command via
OTDF_LOCAL_INSTANCE_NAME.
- New `instance` subcommand group: init [--from-scenario PATH],
ls --json, rm.
- New `scenario` subcommand: `run <path>` translates the scenario's
suite block into `pytest --sdks-encrypt ... --sdks-decrypt ...
--containers ...` under xtest/ with OTDF_LOCAL_INSTANCE_NAME set.
Tests (otdf-local/tests/test_multi_instance.py):
- Port arithmetic at default and alternate bases.
- Settings round-trip with and without an instance.yaml.
- platform_binary_for resolves under the otdf-sdk-mgr-managed
xtest/platform/ tree.
.gitignore additions:
- tests/instances/ (per-instance config and logs)
- xtest/scenarios/*.installed.json (provisioning records)
- .claude/tmp/
Backward compatibility:
- `otdf-local up` with no --instance flag keeps working against a
sibling platform/ checkout.
Refs: https://virtru.atlassian.net/browse/DSPX-3302
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Before this change, `otdf-local instance init` only wrote `instance.yaml`
and empty subdirs. Anyone running a fresh instance had to manually copy
keys from another worktree, run `init-temp-keys.sh` by hand, and copy
`opentdf-dev.yaml` into the instance dir before `up` would succeed —
otherwise Keycloak crash-looped on a missing `truststore.jks`, and
pytest failed with `OT_ROOT_KEY environment variable is not set`.
Changes:
- utils/keys.py: add `generate_localhost_cert()` and `generate_ca_jks()`
to produce the Keycloak TLS pair + JKS truststore (matches the
platform's `init-temp-keys.sh`). `generate_ca_jks()` runs `keytool`
inside the `keycloak/keycloak:25.0` image so a local JDK isn't
required. `ensure_keys_exist()` now generates the full bootstrap
bundle, idempotently.
- cli_instance.py: `_init_from_scenario` and `_init_minimal` call a new
`_provision_instance_dir()` helper that runs `ensure_keys_exist()` and
copies the platform's `opentdf-dev.yaml` (or `opentdf-example.yaml`)
into the instance dir, overriding `services.kas.root_key` with a
freshly generated value so every instance owns its own root key.
- services/platform.py: `_generate_config()` preserves an existing
per-instance `opentdf.yaml`, only patching logger + golden-key fields
in place, so the init-time `root_key` survives restarts.
- services/docker.py: docker-compose subprocesses are now run with
`KEYS_DIR=<instance>/keys` so the compose file's `${KEYS_DIR:-./keys}`
mounts resolve to the per-instance bundle.
Users can now run:
otdf-local instance init <name> --from-scenario path/to/scenario.yaml
otdf-local --instance <name> up
eval $(otdf-local --instance <name> env)
cd xtest && uv run pytest ...
with no manual key-copying, no editing of `opentdf.yaml`, and no
shell-script fallback. Verified end-to-end against `pure-mlkem.yaml`
(PR opentdf/platform#3537): all 9 services come up healthy on the first
try and `env` exports `OT_ROOT_KEY`.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
…chema `_build_pytest_args` read `suite.select` and treated `suite.containers` as a string, but the Pydantic Suite model exposes `targets: list[str]` and `containers: list[ContainerKind]`. Any user invoking `otdf-local scenario run` hit AttributeError. Also wires `suite.kexpr` through as `-k`; it was silently dropped. Adds unit tests covering empty/multi targets, container join, kexpr, markers + extra args, and SDK token forwarding. Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
…leanup - `up` command now uses `settings.get_platform_port()` and iterates `kas_manager._instances` with `settings.get_kas_port()` for health checks so non-default instances with a different `ports.base` work correctly - Add `Settings.get_platform_port()` alongside the existing `get_kas_port()` - Simplify metadata name update: `instance.metadata.name = name` (frozen=False) - Use `shlex.join(cmd)` for display in cli_scenario.py - Add `"Instance | None"` return type to `load_instance` via TYPE_CHECKING - Drop unused `Path` import in cli.py, stale `os` import in test file Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Guard platform_dir None-access in env command; replace non-existent PlatformPin.image attribute with "unknown" fallback in ls command. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- cli_scenario: set OTDF_LOCAL_INSTANCE_NAME + clear settings cache before get_settings() so scenario-driven instance name is picked up - cli_instance: add _validate_instance_name() to guard against path traversal in init/rm; add --force flag to init to prevent silent overwrite - kas: add get_instance_names() public method; replace _instances access in cli - keys: generate_ca_jks() now imports cert only (keytool -importcert) so ca.jks is a proper truststore; ensure_keys_exist() guards include cert files alongside private keys to catch partial-init broken state Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Reverts the keytool -importcert change from the previous commit. The PKCS12 + importkeystore approach mirrors init-temp-keys.sh in the platform repo exactly (lines 65-90); Keycloak requires this form of ca.jks and the cert-only truststore broke it. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…stance() on Settings
…gs.resolve_binary_worktree
… to settings.resolve_binary_worktree
…o _provision_instance_dir()
Refactors otdf-local from a single-instance CLI (one platform checkout,
fixed ports, hardcoded six KAS instances) into a multi-instance harness
where each named instance under tests/instances/<name>/ owns its own
opentdf.yaml, keys, KAS configs, and port range.
Why
---
A single bug report often describes a *combination* — platform v0.9.0
with Java SDK 0.7.8 and a KAS at a pre-release. Today a developer has
to hand-edit configs and re-checkout the platform to reproduce. After
this change:
otdf-local instance init java-078 --from-scenario .../scenario.yaml
otdf-local --instance java-078 up
brings up exactly the topology the scenario describes, using platform
binaries that otdf-sdk-mgr already provisioned (each instance, and each
KAS within an instance, can reference a different pinned version). Two
instances on disjoint ports.base can coexist on a developer laptop.
What changes
------------
otdf-local now depends on otdf-sdk-mgr via a uv path source so both
tools share the canonical Scenario/Instance schema.
Settings (otdf_local.config.settings):
- New instance_name (env-overridable via OTDF_LOCAL_INSTANCE_NAME),
instance_dir, instances_root, instance_yaml properties.
- platform_dir becomes optional; legacy sibling-discovery only kicks
in when no per-instance configuration is present.
- platform_binary_for(dist) resolves to the otdf-sdk-mgr-managed
xtest/platform/dist/<dist>/service binary.
- keys_dir, logs_dir, config_dir, platform_config, and
get_kas_config_path switch to per-instance paths whenever
instance.yaml exists; legacy behavior is preserved otherwise.
- load_instance() reads the per-instance manifest via the shared
Pydantic model.
Ports (otdf_local.config.ports):
- KAS_OFFSETS exposes the offset table (alpha=+101, beta=+202, ...,
km2=+606) so multiple instances on different bases get disjoint
port ranges. The legacy 8080-based constants are preserved as
defaults.
- get_kas_port(name, base=...) computes the port relative to base.
Services (otdf_local.services.platform / .kas):
- PlatformService.start() and KASService.start() use the pinned dist
binary at xtest/platform/dist/<dist>/service when an instance is
loaded, with cwd set to the recorded worktree so the binary finds
its embedded resources. Legacy `go run ./service` path runs
unchanged when no instance is active.
- KASService.is_key_management defers to the manifest's `mode` field
instead of the legacy name-based heuristic; per-KAS features (e.g.
ec_tdf_enabled) pass through to opentdf.yaml.
- KASManager constructs only the KAS instances listed in
instance.yaml's kas: map. start_standard / start_km filter on
is_key_management so subset topologies still work.
utils.keys.setup_golden_keys:
- Writes key files into the target directory (per-instance keys_dir
or legacy platform_dir) and uses absolute paths in the generated
keys_config so the binary finds them regardless of cwd.
CLI:
- New top-level --instance option threads through every command via
OTDF_LOCAL_INSTANCE_NAME.
- New `instance` subcommand group: init [--from-scenario PATH],
ls --json, rm.
- New `scenario` subcommand: `run <path>` translates the scenario's
suite block into `pytest --sdks-encrypt ... --sdks-decrypt ...
--containers ...` under xtest/ with OTDF_LOCAL_INSTANCE_NAME set.
Tests (otdf-local/tests/test_multi_instance.py):
- Port arithmetic at default and alternate bases.
- Settings round-trip with and without an instance.yaml.
- platform_binary_for resolves under the otdf-sdk-mgr-managed
xtest/platform/ tree.
.gitignore additions:
- tests/instances/ (per-instance config and logs)
- xtest/scenarios/*.installed.json (provisioning records)
- .claude/tmp/
Backward compatibility:
- `otdf-local up` with no --instance flag keeps working against a
sibling platform/ checkout.
Refs: https://virtru.atlassian.net/browse/DSPX-3302
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Adds two new pytest CLI options so xtest can be driven by a scenarios.yaml
and run against a specific otdf-local instance.
--scenario PATH When set, defaults --sdks-encrypt, --sdks-decrypt,
and --containers from the scenario's `sdks` and
`suite` blocks. Options explicitly passed on the
CLI always override.
--instance NAME Propagated to OTDF_LOCAL_INSTANCE_NAME so child
`otdf-local` invocations within the test see the
same instance the scenario expects.
If otdf-sdk-mgr is not installed (minimal pytest environments), the
--scenario flag silently no-ops via an ImportError guard. The flag
shape is invariant either way so CI configs don't fork.
This is the consumer side of the PR 3 / scenario-driven flow: the
authoritative entry point remains `otdf-local scenario run <path>`,
which sets these flags for you; this PR lets pytest accept them
directly when running scenario-aware sessions outside the wrapper.
Refs: https://virtru.atlassian.net/browse/DSPX-3302
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
837416f to
8a22603
Compare
|
b441b38 to
327f045
Compare



Summary
Fourth PR in the five-part stack. Adds two new pytest CLI options to
xtest/conftest.py:--scenario PATH— when set, defaults--sdks-encrypt,--sdks-decrypt, and--containersfrom the scenario file'ssdksandsuiteblocks. CLI-passed options always win.--instance NAME— propagated toOTDF_LOCAL_INSTANCE_NAMEso any nestedotdf-localinvocation agrees on the instance.The authoritative entry point remains
otdf-local scenario run <path>(PR 3), which sets these flags for you. This PR lets pytest accept them directly when running scenario-aware sessions outside the wrapper (e.g., a developer iterating withpytest --scenario foo.yaml -k some_test).The
--scenarioimport ofotdf_sdk_mgr.schemais guarded with a try/except so minimal pytest environments that don't haveotdf-sdk-mgrinstalled silently no-op.Stack
Test plan
cd xtest && uv run pytest --collect-only -q→ 103 tests still collect cleanlyuv run pytest --helpshows--scenarioand--instanceunder custom options--sdks-encrypt/--sdks-decrypt/--containersparametrizationJira: https://virtru.atlassian.net/browse/DSPX-3302
🤖 Generated with Claude Code
Stack (
a60d3302):Generated by
wgo stack. Edit text above or below this block, not inside it.