Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2f94428
docs: Add AGENTS.md and CLAUDE.md
pflynn-virtru Jan 15, 2026
76f65f8
chore(ci): Improve X-Test workflow performance
pflynn-virtru Jan 15, 2026
bc3a027
fix(xtest): Guard branch replacement in post-checkout-java.sh
pflynn-virtru Jan 15, 2026
b9faaca
fix(xtest): Use version-specific platform.branch in post-checkout-jav…
pflynn-virtru Jan 15, 2026
8ead1a3
fix(xtest): Replace incorrect platform.branch values in Java SDK pom.xml
pflynn-virtru Jan 15, 2026
6bb560f
fix(xtest): Directly replace branch=main when no properties section e…
pflynn-virtru Jan 15, 2026
62641c4
chore(xtest): Update LTS versions to summer 2025 releases
pflynn-virtru Jan 15, 2026
995538e
fix(xtest): Preserve existing protocol/go branch values in Java pom.xml
pflynn-virtru Jan 15, 2026
ccfcad9
chore(ci): Update buf-setup-action to match platform config
pflynn-virtru Jan 15, 2026
09602dd
chore(ci): upgrade to buf-action v1.3.0 with BUF_TOKEN for BSR auth
pflynn-virtru Jan 15, 2026
46f21af
chore(ci): add GCP authentication and secret retrieval to xtest workflow
pflynn-virtru Jan 20, 2026
acb0762
chore(ci): use vars for GCP auth and secrets in xtest workflow
pflynn-virtru Jan 20, 2026
2921610
chore(ci): upgrade GCP auth action to v3 and update secrets handling
pflynn-virtru Jan 20, 2026
7b1285a
chore(ci): remove unused GCP auth steps from xtest workflow
pflynn-virtru Jan 20, 2026
ce1757b
chore(ci): simplify DEFAULT_TAGS in xtest workflow
pflynn-virtru Jan 20, 2026
00823f3
Merge branch 'main' into chore/ci-improve-xtest-workflow-377
pflynn-virtru Jan 20, 2026
66f9223
chore(ci): enable parallel test execution with pytest-xdist
pflynn-virtru Jan 21, 2026
8fc3405
feat(xtest): add download-artifact.sh scripts for published SDK packages
pflynn-virtru Jan 21, 2026
3020717
fix(xtest): remove Java from test-artifacts (cmdline.jar not published)
pflynn-virtru Jan 21, 2026
45c9e65
fix(xtest): capture SCRIPT_DIR before cd in JS download script
pflynn-virtru Jan 21, 2026
c047252
feat(xtest): optimize Java cmdline build using released SDK
pflynn-virtru Jan 21, 2026
3fc7e09
fix(xtest): remove -am flag to use released SDK from Maven Central
pflynn-virtru Jan 21, 2026
ccfaff1
fix(xtest): use single quotes in trap to satisfy shellcheck
pflynn-virtru Jan 21, 2026
e8eb995
fix(xtest): skip Maven enforcer plugin for cmdline-only build
pflynn-virtru Jan 21, 2026
eda3097
feat(xtest): use download-artifact.sh for released SDK versions
pflynn-virtru Jan 21, 2026
8b5ea22
feat(xtest): centralize SDK config and add clean version field
pflynn-virtru Jan 21, 2026
c04394c
fix(xtest): format resolve-version.py with ruff
pflynn-virtru Jan 21, 2026
fc8ea67
fix(xtest): convert DIST_DIR to absolute path before cd
pflynn-virtru Jan 21, 2026
6f9d207
fix(xtest): checkout PR branch in resolve-versions job
pflynn-virtru Jan 21, 2026
1e5b1ba
fix(xtest): update Java to 17 with Temurin distribution
pflynn-virtru Jan 21, 2026
72e865b
feat(xtest): use dev releases for HEAD builds (Java/JS)
pflynn-virtru Jan 21, 2026
7f91397
fix(xtest): default SDK refs to 'main latest' for broader test coverage
pflynn-virtru Jan 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 155 additions & 25 deletions .github/workflows/xtest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ on:
otdfctl-ref:
required: false
type: string
default: main
default: main latest
description: "The branch or commit to use for otdfctl"
js-ref:
required: false
type: string
default: main
default: main latest
description: "The branch or commit to use for the web-sdk"
java-ref:
required: false
type: string
default: main
default: main latest
description: "The branch or commit to use for the java-sdk"
focus-sdk:
required: false
Expand All @@ -37,15 +37,15 @@ on:
otdfctl-ref:
required: false
type: string
default: main
default: main latest
js-ref:
required: false
type: string
default: main
default: main latest
java-ref:
required: false
type: string
default: main
default: main latest
focus-sdk:
required: false
type: string
Expand All @@ -54,6 +54,11 @@ on:
- cron: "30 6 * * *" # 0630 UTC
- cron: "0 5 * * 1,3" # 500 UTC (Monday, Wednesday)
- cron: "0 18 * * 0" # 1800 UTC (Sunday)

concurrency:
group: ${{ github.workflow }}-pr-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

jobs:
resolve-versions:
timeout-minutes: 10
Expand All @@ -69,7 +74,7 @@ jobs:
java: ${{ steps.version-info.outputs.java-version-info }}
js: ${{ steps.version-info.outputs.js-version-info }}
env:
PLATFORM_REF: "${{ inputs.platform-ref || 'main lts' }}"
PLATFORM_REF: "${{ inputs.platform-ref }}"
JS_REF: "${{ inputs.js-ref }}"
OTDFCTL_REF: "${{ inputs.otdfctl-ref }}"
JAVA_REF: "${{ inputs.java-ref }}"
Expand All @@ -91,13 +96,13 @@ jobs:
echo "DEFAULT_TAGS=main latest" >> "$GITHUB_ENV"
elif [[ $CRON_MONDAY_WEDNESDAY == 'true' ]]; then
echo "Running Monday/Wednesday tests"
echo "DEFAULT_TAGS=main lts" >> "$GITHUB_ENV"
echo "DEFAULT_TAGS=main" >> "$GITHUB_ENV"
elif [[ $CRON_WEEKLY == 'true' ]]; then
echo "Running weekly tests"
echo "DEFAULT_TAGS=main latest lts" >> "$GITHUB_ENV"
echo "DEFAULT_TAGS=main latest" >> "$GITHUB_ENV"
else
echo "Running PR, Workflow Dispatch, or manual trigger"
echo "DEFAULT_TAGS=main" >> "$GITHUB_ENV"
echo "DEFAULT_TAGS=main latest" >> "$GITHUB_ENV"
fi
env:
CRON_NIGHTLY: ${{ github.event.schedule == '30 6 * * *' }}
Expand All @@ -108,6 +113,7 @@ jobs:
path: otdf-sdk
persist-credentials: false
repository: opentdf/tests
ref: ${{ github.head_ref || github.ref }}
sparse-checkout: xtest/sdk
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b
with:
Expand Down Expand Up @@ -245,15 +251,17 @@ jobs:
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b
with:
python-version: "3.14"
- uses: bufbuild/buf-setup-action@2211e06e8cf26d628cda2eea15c95f8c42b080b3
- uses: bufbuild/buf-action@8f4a1456a0ab6a1eb80ba68e53832e6fcfacc16c # v1.3.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
setup_only: true
token: ${{ secrets.BUF_TOKEN }}
version: "1.56.0"

- name: Set up JDK
uses: actions/setup-java@5896cecc08fd8a1fbdfaf517e29b571164b031f7
with:
java-version: "11"
distribution: "adopt"
java-version: "17"
distribution: "temurin"
server-id: github

- name: Set up Node 22
Expand All @@ -269,12 +277,39 @@ jobs:
sdk: js
version-info: "${{ needs.resolve-versions.outputs.js }}"

- name: Cache npm
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('otdftests/xtest/sdk/js/src/**/package-lock.json') }}
restore-keys: |
npm-${{ runner.os }}-

######## SETUP THE JS CLI #############
- name: build and setup the web-sdk cli
id: build-web-sdk
run: |
make
# Use download-artifact.sh for released versions and dev (from dev release)
for row in $(echo "$JS_VERSION_INFO" | jq -c '.[]'); do
TAG=$(echo "$row" | jq -r '.tag')
HEAD=$(echo "$row" | jq -r '.head')
VERSION=$(echo "$row" | jq -r '.version // empty')

chmod +x ./download-artifact.sh
if [[ "$HEAD" == "true" ]]; then
echo "Downloading $TAG from dev release..."
./download-artifact.sh dev "dist/$TAG"
elif [[ -n "$VERSION" ]]; then
echo "Using npm package for $TAG (version $VERSION)..."
./download-artifact.sh "$VERSION" "dist/$TAG"
else
echo "Building $TAG from source (no version info)..."
VERSIONS="$TAG" make
fi
done
working-directory: otdftests/xtest/sdk/js
env:
JS_VERSION_INFO: ${{ needs.resolve-versions.outputs.js }}

######## CHECKOUT GO CLI #############
- name: Configure otdfctl
Expand All @@ -285,6 +320,16 @@ jobs:
sdk: go
version-info: "${{ needs.resolve-versions.outputs.go }}"

- name: Cache Go modules
uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: go-${{ runner.os }}-${{ hashFiles('otdftests/xtest/sdk/go/src/*/go.sum') }}
restore-keys: |
go-${{ runner.os }}-

- name: Resolve otdfctl heads
id: resolve-otdfctl-heads
run: |-
Expand Down Expand Up @@ -313,9 +358,28 @@ jobs:

######## SETUP THE GO CLI #############
- name: Prepare go cli
run: |-
make
run: |
# Use download-artifact.sh for released versions, make for HEAD
for row in $(echo "$GO_VERSION_INFO" | jq -c '.[]'); do
TAG=$(echo "$row" | jq -r '.tag')
HEAD=$(echo "$row" | jq -r '.head')
VERSION=$(echo "$row" | jq -r '.version // empty')

if [[ "$HEAD" == "true" ]]; then
echo "Building $TAG from source (HEAD version)..."
VERSIONS="$TAG" make
elif [[ -n "$VERSION" ]]; then
echo "Using pre-built binary for $TAG (version $VERSION)..."
chmod +x ./download-artifact.sh
./download-artifact.sh "$VERSION" "dist/$TAG"
else
echo "Building $TAG from source (no version info)..."
VERSIONS="$TAG" make
fi
done
working-directory: otdftests/xtest/sdk/go
env:
GO_VERSION_INFO: ${{ needs.resolve-versions.outputs.go }}

####### CHECKOUT JAVA SDK ##############

Expand All @@ -326,6 +390,14 @@ jobs:
sdk: java
version-info: "${{ needs.resolve-versions.outputs.java }}"

- name: Cache Maven repository
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: maven-${{ runner.os }}-${{ hashFiles('otdftests/xtest/sdk/java/src/**/pom.xml') }}
restore-keys: |
maven-${{ runner.os }}-

- name: pre-release protocol buffers for java-sdk
if: |
(env.FOCUS_SDK == 'go' || env.FOCUS_SDK == 'java') && contains(fromJSON(needs.resolve-versions.outputs.heads), matrix.platform-tag)
Expand All @@ -351,11 +423,27 @@ jobs:
####### SETUP JAVA CLI ##############
- name: Prepare java cli
run: |
make
env:
BUF_INPUT_HTTPS_USERNAME: opentdf-bot
BUF_INPUT_HTTPS_PASSWORD: ${{ secrets.PERSONAL_ACCESS_TOKEN_OPENTDF }}
# Use download-artifact.sh for released versions and dev (from dev release)
for row in $(echo "$JAVA_VERSION_INFO" | jq -c '.[]'); do
TAG=$(echo "$row" | jq -r '.tag')
HEAD=$(echo "$row" | jq -r '.head')
VERSION=$(echo "$row" | jq -r '.version // empty')

chmod +x ./download-artifact.sh
if [[ "$HEAD" == "true" ]]; then
echo "Downloading $TAG from dev release..."
./download-artifact.sh dev "dist/$TAG"
elif [[ -n "$VERSION" ]]; then
echo "Using optimized build for $TAG (version $VERSION)..."
./download-artifact.sh "$VERSION" "dist/$TAG"
else
echo "Building $TAG from source (no version info)..."
VERSIONS="$TAG" make
fi
done
working-directory: otdftests/xtest/sdk/java
env:
JAVA_VERSION_INFO: ${{ needs.resolve-versions.outputs.java }}

######## Configure test environment #############
- name: Lookup current platform version
Expand Down Expand Up @@ -422,7 +510,7 @@ jobs:
######## RUN THE TESTS #############
- name: Run legacy decryption tests
run: |-
uv run pytest --html=test-results/sdk-${FOCUS_SDK}-${PLATFORM_TAG}.html --self-contained-html --sdks-encrypt "${ENCRYPT_SDK}" -ra -v --focus "$FOCUS_SDK" test_legacy.py
uv run pytest -n auto --dist loadscope --html=test-results/sdk-${FOCUS_SDK}-${PLATFORM_TAG}.html --self-contained-html --sdks-encrypt "${ENCRYPT_SDK}" -ra -v --focus "$FOCUS_SDK" test_legacy.py
working-directory: otdftests/xtest
env:
PLATFORM_DIR: "../../${{ steps.run-platform.outputs.platform-working-dir }}"
Expand All @@ -431,7 +519,7 @@ jobs:
- name: Run all standard xtests
if: ${{ env.FOCUS_SDK == 'all' }}
run: |-
uv run pytest --html=test-results/sdk-${FOCUS_SDK}-${PLATFORM_TAG}.html --self-contained-html --sdks-encrypt "${ENCRYPT_SDK}" -ra -v test_tdfs.py test_policytypes.py
uv run pytest -n auto --dist loadscope --html=test-results/sdk-${FOCUS_SDK}-${PLATFORM_TAG}.html --self-contained-html --sdks-encrypt "${ENCRYPT_SDK}" -ra -v test_tdfs.py test_policytypes.py
working-directory: otdftests/xtest
env:
PLATFORM_DIR: "../../${{ steps.run-platform.outputs.platform-working-dir }}"
Expand All @@ -441,7 +529,7 @@ jobs:
- name: Run xtests focusing on a specific SDK
if: ${{ env.FOCUS_SDK != 'all' }}
run: |-
uv run pytest --html=test-results/sdk-${FOCUS_SDK}-${PLATFORM_TAG}.html --self-contained-html --sdks-encrypt "${ENCRYPT_SDK}" -ra -v --focus "$FOCUS_SDK" test_tdfs.py test_policytypes.py
uv run pytest -n auto --dist loadscope --html=test-results/sdk-${FOCUS_SDK}-${PLATFORM_TAG}.html --self-contained-html --sdks-encrypt "${ENCRYPT_SDK}" -ra -v --focus "$FOCUS_SDK" test_tdfs.py test_policytypes.py
working-directory: otdftests/xtest
env:
PLATFORM_DIR: "../../${{ steps.run-platform.outputs.platform-working-dir }}"
Expand Down Expand Up @@ -531,7 +619,7 @@ jobs:
- name: Run attribute based configuration tests
if: ${{ steps.multikas.outputs.supported == 'true' }}
run: |-
uv run pytest --html=test-results/attributes-${FOCUS_SDK}-${PLATFORM_TAG}.html --self-contained-html --sdks-encrypt "${ENCRYPT_SDK}" -ra -v --focus "$FOCUS_SDK" test_abac.py
uv run pytest -n auto --dist loadscope --html=test-results/attributes-${FOCUS_SDK}-${PLATFORM_TAG}.html --self-contained-html --sdks-encrypt "${ENCRYPT_SDK}" -ra -v --focus "$FOCUS_SDK" test_abac.py
working-directory: otdftests/xtest
env:
PLATFORM_DIR: "../../${{ steps.run-platform.outputs.platform-working-dir }}"
Expand Down Expand Up @@ -588,6 +676,48 @@ jobs:
throw err; // Re-throw unexpected errors
}

test-artifacts:
# Validate that published SDK artifacts can be downloaded
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false

- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
with:
node-version: "22"

- uses: actions/setup-java@5896cecc08fd8a1fbdfaf517e29b571164b031f7
with:
java-version: "17"
distribution: "temurin"

- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: "1.24"

- name: Test Java artifact build
run: |
chmod +x xtest/sdk/java/download-artifact.sh
xtest/sdk/java/download-artifact.sh 0.12.0 /tmp/java-test
ls -la /tmp/java-test/
java -jar /tmp/java-test/cmdline.jar --version

- name: Test JS artifact download
run: |
chmod +x xtest/sdk/js/download-artifact.sh
xtest/sdk/js/download-artifact.sh 0.4.0 /tmp/js-test
ls -la /tmp/js-test/

- name: Test Go artifact download
run: |
chmod +x xtest/sdk/go/download-artifact.sh
xtest/sdk/go/download-artifact.sh 0.24.0 /tmp/go-test
ls -la /tmp/go-test/

xtest:
# Capstone job: always runs, evaluates matrix job aggregate result and fails if any xct variant failed
runs-on: ubuntu-latest
Expand Down
39 changes: 39 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Repository Guidelines

## Project Structure & Module Organization

- `xtest/`: Cross-client compatibility test harness (Python + `pytest`), with fixtures in `xtest/fixtures/` and golden data in `xtest/golden/`.
- `xtest/sdk/`: Helper scripts and Makefiles for checking out/building SDKs under test (e.g., `xtest/sdk/scripts/checkout-all.sh`, `cd xtest/sdk && make`).
- `vulnerability/`: Playwright-based security regression tests (`vulnerability/tests/`).
- `.github/workflows/`: CI workflows (lint/type-check, xtest matrix runs, vulnerability runs).

## Build, Test, and Development Commands

- Enter the dev environment: `devbox shell` (installs Python/JDK/Node per `devbox.json`).
- Install xtest deps: `cd xtest && uv sync` (or `uv sync --extra dev` for dev tools)
- Run xtest: `cd xtest && uv run pytest`
- Focus a subset: `uv run pytest --sdks "go js" --focus go` (see `xtest/conftest.py` options)
- HTML report: `uv run pytest --html tmp/test-report.html --self-contained-html`
- Build SDK CLIs (after checkout): `cd xtest/sdk && make`
- Run vulnerability tests: `cd vulnerability && npm ci && npm test` (requires a running platform; see `README.md` and `vulnerability/README.md`).

## Coding Style & Naming Conventions

- Python code in `xtest/` uses 4-space indentation, `snake_case`, and `pytest`-style fixtures.
- CI enforces these checks in `xtest/`:
- `ruff check` and `ruff format --check`
- `pyright`
- Local equivalent: `cd xtest && uv sync --extra dev && uv run ruff check . && uv run ruff format --check . && uv run pyright`

## Testing Guidelines

- `pytest` tests live in `xtest/test_*.py`; add new fixtures under `xtest/fixtures/`.
- Tests assume a platform backend is reachable (Docker + Keycloak). Use `xtest/test.env` as a template:
- `cd xtest && set -a && source test.env && set +a`

## Commit & Pull Request Guidelines

- Use semantic commit/PR titles (enforced by CI): `feat(xtest): ...`, `fix(vulnerability): ...`, `docs: ...` (types: `fix|feat|chore|docs`; scopes include `xtest`, `vulnerability`, `go`, `java`, `web`, `ci`).
- DCO sign-off is required: `git commit -s -m "feat(xtest): ..."` (see `CONTRIBUTING.md`).
- PRs should include a clear description, linked issue (if any), and relevant logs/screenshots for test failures; reviewers are defined in `CODEOWNERS`.

1 change: 1 addition & 0 deletions CLAUDE.md
11 changes: 8 additions & 3 deletions xtest/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,14 @@ def pt_file(tmp_dir: Path, size: str) -> Path:


@pytest.fixture(scope="package")
def tmp_dir() -> Path:
"""Create and return temporary directory for test files."""
dname = Path("tmp/")
def tmp_dir(request: pytest.FixtureRequest) -> Path:
"""Create worker-specific temporary directory for test files.

When running with pytest-xdist, each worker gets its own subdirectory
to prevent file collisions between parallel test processes.
"""
worker_id = getattr(request.config, "workerinput", {}).get("workerid", "master")
dname = Path(f"tmp/{worker_id}/")
dname.mkdir(parents=True, exist_ok=True)
return dname

Expand Down
1 change: 1 addition & 0 deletions xtest/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies = [
"Pygments>=2.19.2",
"pytest>=9.0.2",
"pytest-html>=4.1.1",
"pytest-xdist>=3.6.1",
"pytest-metadata>=3.1.1",
"referencing>=0.37.0",
"requests>=2.32.5",
Expand Down
Loading