From 5b6f799acd131c8031ed498c8baad7548bb44797 Mon Sep 17 00:00:00 2001 From: Chris Reed Date: Wed, 16 Sep 2026 08:55:02 -0500 Subject: [PATCH] feat(xtest): add independent platform feature overrides Signed-off-by: Chris Reed --- .github/workflows/xtest.yml | 11 ++++++++ AGENTS.md | 4 +++ xtest/AGENTS.md | 1 + xtest/README.md | 7 +++++ xtest/tdfs.py | 27 ++++++++++++++---- xtest/test_tdfs_units.py | 56 +++++++++++++++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 5 deletions(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 6bbd042bb..c238066d6 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -48,6 +48,11 @@ on: type: string default: "" description: "Comma-separated feature names to treat as supported regardless of what each SDK's `cli.sh supports` reports. Use when evaluating a fix that has not been released yet: the version gates live in this repo and answer 'no' for exactly those unreleased builds, so the cells would otherwise skip. An unknown name fails the run rather than being ignored." + force-platform-supports: + required: false + type: string + default: "" + description: "Comma-separated platform features to treat as supported. Does not override SDK gates or enable service configuration. Unknown names fail the run." workflow_call: inputs: platform-ref: @@ -86,6 +91,11 @@ on: required: false type: string default: "" + force-platform-supports: + required: false + type: string + default: "" + description: "Comma-separated platform features to treat as supported. Does not override SDK gates or enable service configuration. Unknown names fail the run." schedule: - cron: "30 6 * * *" # 0630 UTC - cron: "0 5 * * 1,3" # 500 UTC (Monday, Wednesday) @@ -101,6 +111,7 @@ concurrency: # nothing" -- so the PR gate and the nightlies are unaffected. env: XT_FORCE_SUPPORTS: ${{ inputs.force-supports }} + XT_FORCE_PLATFORM_SUPPORTS: ${{ inputs.force-platform-supports }} jobs: resolve-versions: diff --git a/AGENTS.md b/AGENTS.md index ffa864447..bad4c54b9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,6 +61,10 @@ See `xtest/AGENTS.md` for the full table of `--sdks`, `--containers`, - `XT_FORCE_SUPPORTS` — comma-separated feature names to treat as supported regardless of what each SDK's `cli.sh supports` reports. See below. +- `XT_FORCE_PLATFORM_SUPPORTS` — comma-separated platform features to treat as + supported, independent of SDK overrides. It does not enable service settings. + In CI, use `force-platform-supports`. See `xtest/README.md` for usage. + ### Evaluating an unreleased fix: `XT_FORCE_SUPPORTS` `SDK.supports(feature)` answers from the `supports` case statements in diff --git a/xtest/AGENTS.md b/xtest/AGENTS.md index 5997f8096..db4f8ff40 100644 --- a/xtest/AGENTS.md +++ b/xtest/AGENTS.md @@ -37,6 +37,7 @@ Beyond the repo-wide ones in `../AGENTS.md`: |----------|---------| | `XT_TMP_DIR` | Root for generated fixtures and ciphertexts (default `tmp/`). Point at a large volume for `medium`/`large` runs. | | `XT_FORCE_SUPPORTS` | Comma-separated features to treat as supported, bypassing the `cli.sh supports` gate. For evaluating a fix before it releases — see `../AGENTS.md`. Unknown names raise. | +| `XT_FORCE_PLATFORM_SUPPORTS` | Comma-separated platform features to treat as supported. Independent of SDK overrides; does not enable service configuration. Unknown names raise. | ## Authoring a New Test diff --git a/xtest/README.md b/xtest/README.md index 6bdfcc400..ddb0035de 100644 --- a/xtest/README.md +++ b/xtest/README.md @@ -116,6 +116,13 @@ uv sync --extra dev pytest ``` +#### Testing unreleased platform features + +Set `XT_FORCE_PLATFORM_SUPPORTS` to a comma-separated list of platform features +to bypass their test gates. In CI, use the `force-platform-supports` input. +SDK overrides use `XT_FORCE_SUPPORTS` separately. These overrides do not enable +service configuration. + #### Run TDF Tests ```shell diff --git a/xtest/tdfs.py b/xtest/tdfs.py index 8f3bf8e05..2cf1053a7 100644 --- a/xtest/tdfs.py +++ b/xtest/tdfs.py @@ -10,7 +10,7 @@ import zipfile from collections.abc import Callable from pathlib import Path -from typing import Any, Literal, TypeIs, get_args +from typing import Any, Literal, TypeIs, cast, get_args import jsonschema import pytest @@ -207,8 +207,10 @@ def is_sdk_type(val: str) -> TypeIs[sdk_type]: ] -def _parse_forced_supports(raw: str) -> frozenset[str]: - """Parse ``XT_FORCE_SUPPORTS`` into a set of feature names. +def _parse_forced_supports( + raw: str, *, source: str = "XT_FORCE_SUPPORTS" +) -> frozenset[feature_type]: + """Parse an SDK or platform override into validated feature names. An unrecognised name is a hard error rather than a no-op. The override exists to turn a skip into a real result, so a typo that quietly left the @@ -220,10 +222,10 @@ def _parse_forced_supports(raw: str) -> frozenset[str]: unknown = names - known if unknown: raise ValueError( - f"XT_FORCE_SUPPORTS names unknown feature(s) {sorted(unknown)}; " + f"{source} names unknown feature(s) {sorted(unknown)}; " f"valid features are {sorted(known)}" ) - return frozenset(names) + return cast(frozenset[feature_type], frozenset(names)) #: Features to treat as supported no matter what the SDK reports. @@ -248,6 +250,20 @@ def _parse_forced_supports(raw: str) -> frozenset[str]: ) +# Platform overrides are independent of SDK overrides and service configuration. +FORCED_PLATFORM_SUPPORTS = _parse_forced_supports( + os.environ.get("XT_FORCE_PLATFORM_SUPPORTS", ""), + source="XT_FORCE_PLATFORM_SUPPORTS", +) + +if FORCED_PLATFORM_SUPPORTS: + logger.warning( + "XT_FORCE_PLATFORM_SUPPORTS is set: treating %s as supported by the " + "platform. SDK gates and service configuration are unchanged.", + ", ".join(sorted(FORCED_PLATFORM_SUPPORTS)), + ) + + container_version = Literal["4.2.2", "4.3.0"] policy_type = Literal["plaintext", "encrypted"] @@ -266,6 +282,7 @@ class PlatformFeatureSet(BaseModel): def __init__(self, **kwargs: dict[str, Any]): super().__init__(**kwargs) + self.features.update(FORCED_PLATFORM_SUPPORTS) v = os.getenv("PLATFORM_VERSION") if not v: print("PLATFORM_VERSION unset or empty; defaulting to 0.9.0") diff --git a/xtest/test_tdfs_units.py b/xtest/test_tdfs_units.py index e6d86ed72..d79da777e 100644 --- a/xtest/test_tdfs_units.py +++ b/xtest/test_tdfs_units.py @@ -41,6 +41,62 @@ def test_unknown_name_raises(self): tdfs._parse_forced_supports("hexles") +# --- Platform forced support ------------------------------------------------ + + +@pytest.mark.parametrize("platform_version", ["0.12.0", "main", ""]) +@pytest.mark.parametrize("forced", [False, True]) +def test_platform_forced_supports( + monkeypatch: pytest.MonkeyPatch, platform_version: str, forced: bool +): + monkeypatch.setenv("PLATFORM_VERSION", platform_version) + # A source tag alone must not imply feature support. + monkeypatch.setenv("PLATFORM_TAG", "main") + monkeypatch.setattr(tdfs, "FORCED_SUPPORTS", tdfs._parse_forced_supports("dpop")) + monkeypatch.setattr( + tdfs, + "FORCED_PLATFORM_SUPPORTS", + tdfs._parse_forced_supports("dpop_nonce_challenge" if forced else ""), + ) + monkeypatch.setattr(tdfs, "_fetch_well_known", lambda: None) + + features = tdfs.PlatformFeatureSet() + assert ("dpop_nonce_challenge" in features.features) is forced + # An SDK override must not force the corresponding platform feature. + assert "dpop" not in features.features + + +def test_platform_forced_supports_do_not_leak(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setenv("PLATFORM_VERSION", "main") + monkeypatch.setattr( + tdfs, + "FORCED_PLATFORM_SUPPORTS", + tdfs._parse_forced_supports("dpop_nonce_challenge"), + ) + forced = tdfs.PlatformFeatureSet() + monkeypatch.setattr(tdfs, "FORCED_PLATFORM_SUPPORTS", frozenset()) + unforced = tdfs.PlatformFeatureSet() + assert "dpop_nonce_challenge" in forced.features + assert "dpop_nonce_challenge" not in unforced.features + + +def test_platform_override_does_not_override_sdk(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(tdfs, "FORCED_SUPPORTS", frozenset()) + monkeypatch.setattr( + tdfs, + "FORCED_PLATFORM_SUPPORTS", + tdfs._parse_forced_supports("dpop_nonce_challenge"), + ) + sdk = object.__new__(tdfs.SDK) + sdk._supports = {"dpop_nonce_challenge": False} + assert not sdk.supports("dpop_nonce_challenge") + + +def test_unknown_platform_override_names_source(): + with pytest.raises(ValueError, match="XT_FORCE_PLATFORM_SUPPORTS names unknown"): + tdfs._parse_forced_supports("dpop_typo", source="XT_FORCE_PLATFORM_SUPPORTS") + + # --- tdfs.zip64_reader_is_broken ----------------------------------------------