Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 5 additions & 5 deletions .claude/agents/test-architect.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@ Work through each layer top-down:

- Write scenarios in business language -- no URLs, status codes, or selectors
- Always include a product tag: `@connect`, `@workbench`, or `@package_manager`
- Place in the correct category directory under `tests/`
- Reuse existing Given steps from `tests/conftest.py` for common guards
- Place in the correct category directory under `src/vip_tests/`
- Reuse existing Given steps from `src/vip_tests/conftest.py` for common guards

### 2. Step Definitions (Layer 2)

- Create a matching `.py` file next to the `.feature` file
- Use `@scenario("file.feature", "Scenario name")` to link scenarios
- Use `target_fixture` to pass state between steps
- Keep steps under ~10 lines; push logic to the client layer
- Reuse existing steps; check `tests/conftest.py` and sibling test files
- Reuse existing steps; check `src/vip_tests/conftest.py` and sibling test files

### 3. Driver Port (Layer 3)

Expand Down Expand Up @@ -64,9 +64,9 @@ When reviewing test code, verify:

- Tests must be non-destructive. Tag created content with `_vip_test` and clean up.
- Use `pytest.skip("reason")` in Given steps when preconditions aren't met -- don't use assertions, which produce confusing failures instead of clean skips.
- Fixtures are defined in `tests/conftest.py` (session-scoped) and available everywhere.
- Fixtures are defined in `src/vip_tests/conftest.py` (session-scoped) and available everywhere.
- Available clients: `connect_client`, `workbench_client`, `pm_client` (all session-scoped, `None` if unconfigured).
- Selftests in `selftests/` verify framework behavior; product tests in `tests/` verify deployments.
- Selftests in `selftests/` verify framework behavior; product tests in `src/vip_tests/` verify deployments.

## Anti-Patterns to Flag

Expand Down
23 changes: 15 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,21 @@ jobs:
name: Ruff lint
with:
version: "0.15.0"
args: check src/ tests/ selftests/ examples/
args: check src/ selftests/ examples/

- uses: astral-sh/ruff-action@v3
name: Ruff format
with:
version: "0.15.0"
args: format --check src/ tests/ selftests/ examples/
args: format --check src/ selftests/ examples/

typecheck:
name: Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: astral-sh/setup-uv@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

Expand All @@ -46,15 +46,22 @@ jobs:
steps:
- uses: actions/checkout@v6

- uses: astral-sh/setup-uv@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

- name: Install dependencies
run: uv sync --all-extras

- name: Audit dependencies
run: uv run pip-audit --skip-editable
run: |
# CVE-2026-4539: ReDoS in pygments' AdlLexer (archetype.py), CVSS 3.3.
# Requires local access; no network exposure. VIP never invokes the ADL lexer.
# Pygments cannot be removed — it is a hard dependency of pytest, rich, ipython,
# and the full Jupyter stack. No patched version exists upstream yet (reported at
# https://github.com/pygments/pygments/issues/3058 but unaddressed as of 2026-03-24).
# Re-evaluate when a fix is released.
uv run pip-audit --skip-editable --ignore-vuln CVE-2026-4539

selftest:
name: Selftests (${{ matrix.python-version }})
Expand All @@ -65,7 +72,7 @@ jobs:
steps:
- uses: actions/checkout@v6

- uses: astral-sh/setup-uv@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

Expand All @@ -79,7 +86,7 @@ jobs:
run: uv run pytest selftests/ -v --junitxml=selftest-results.xml --cov=src/vip --cov-report=term-missing

- name: Collect VIP tests (dry run)
run: uv run pytest tests/ --collect-only --quiet
run: uv run pytest src/vip_tests/ --collect-only --quiet

- name: Upload test results
if: always()
Expand All @@ -97,7 +104,7 @@ jobs:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: astral-sh/setup-uv@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- uses: extractions/setup-just@v3
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/connect-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ jobs:
}

# Set up Python and install VIP
- uses: astral-sh/setup-uv@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

Expand Down Expand Up @@ -131,11 +131,11 @@ jobs:
- name: Run Connect smoke tests
run: |
uv run pytest \
tests/prerequisites/test_components.py \
tests/connect/test_auth.py \
tests/connect/test_users.py \
tests/connect/test_runtime_versions.py \
tests/security/test_error_handling.py \
src/vip_tests/prerequisites/test_components.py \
src/vip_tests/connect/test_auth.py \
src/vip_tests/connect/test_users.py \
src/vip_tests/connect/test_runtime_versions.py \
src/vip_tests/security/test_error_handling.py \
-v \
-k "not connect_login_ui" \
--vip-config=vip.toml \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ jobs:
with:
version: v0.48.1
- name: Install uv
uses: astral-sh/setup-uv@v5
uses: astral-sh/setup-uv@v7
- name: Install showboat
run: uv tool install showboat
13 changes: 7 additions & 6 deletions .github/workflows/example-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ jobs:
echo "Resolved Package Manager version: ${VERSION}"
echo "resolved=${VERSION}" >> "$GITHUB_OUTPUT"

- uses: astral-sh/setup-uv@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

Expand Down Expand Up @@ -129,11 +129,12 @@ jobs:
VIP_ENABLE_EXPECTED_FAILURE_DEMO: "1"
run: |
uv run pytest \
tests/prerequisites/test_components.py \
tests/prerequisites/test_expected_failure.py \
tests/connect/test_auth.py \
tests/package_manager/test_repos.py \
-v -k "reachable or api or mirror or repo_exists or expected_failure" \
src/vip_tests/prerequisites/test_components.py \
src/vip_tests/prerequisites/test_expected_failure.py \
src/vip_tests/connect/test_auth.py \
src/vip_tests/connect/test_system_checks.py \
src/vip_tests/package_manager/test_repos.py \
-v -k "reachable or api or mirror or repo_exists or expected_failure or system_checks" \
--vip-config=vip.toml \
|| true

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/packagemanager-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ jobs:
}

# Set up Python and install VIP
- uses: astral-sh/setup-uv@v5
- uses: astral-sh/setup-uv@v7
if: steps.license.outputs.available == 'true'
with:
enable-cache: true
Expand Down Expand Up @@ -172,9 +172,9 @@ jobs:
if: steps.license.outputs.available == 'true'
run: |
uv run pytest \
"tests/prerequisites/test_components.py::test_product_server_is_reachable[Package Manager]" \
tests/prerequisites/test_versions.py \
tests/package_manager/test_repos.py \
"src/vip_tests/prerequisites/test_components.py::test_product_server_is_reachable[Package Manager]" \
src/vip_tests/prerequisites/test_versions.py \
src/vip_tests/package_manager/test_repos.py \
-v \
--vip-config=vip.toml \
--junitxml=smoke-results.xml
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ jobs:
steps:
- uses: actions/checkout@v6

- uses: astral-sh/setup-uv@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

- name: Build wheel and sdist
run: uv build

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7
with:
name: dist
path: dist/
Expand All @@ -30,11 +30,11 @@ jobs:
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/vip
url: https://pypi.org/p/posit-vip
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
name: dist
path: dist/
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/website-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
steps:
- uses: actions/checkout@v6

- uses: astral-sh/setup-uv@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

Expand Down Expand Up @@ -63,7 +63,7 @@ jobs:
umbrella-dir: pr-preview-site
comment: false

- uses: marocchino/sticky-pull-request-comment@v2
- uses: marocchino/sticky-pull-request-comment@v3
env:
WEBSITE_URL: https://posit-dev.github.io/vip/pr-preview-site/pr-${{ github.event.pull_request.number }}/
REPORT_URL: https://posit-dev.github.io/vip/pr-preview/pr-${{ github.event.pull_request.number }}/
Expand Down Expand Up @@ -93,7 +93,7 @@ jobs:
comment: false
action: remove

- uses: marocchino/sticky-pull-request-comment@v2
- uses: marocchino/sticky-pull-request-comment@v3
with:
header: preview-links
delete: true
4 changes: 2 additions & 2 deletions .github/workflows/website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
branches: [main]
paths:
- "website/**"
- "tests/**"
- "src/vip_tests/**"
- "report/**"
- "src/vip/**"
- "scripts/generate-test-catalog.py"
Expand Down Expand Up @@ -33,7 +33,7 @@ jobs:
with:
persist-credentials: false

- uses: astral-sh/setup-uv@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/workbench-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ jobs:
}

# Set up Python and install VIP
- uses: astral-sh/setup-uv@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

Expand Down Expand Up @@ -162,8 +162,8 @@ jobs:
- name: Run Workbench smoke tests
run: |
uv run pytest \
tests/prerequisites/test_components.py \
tests/workbench/test_auth.py \
src/vip_tests/prerequisites/test_components.py \
src/vip_tests/workbench/test_auth.py \
-v -k "workbench" \
--vip-config=vip.toml \
--junitxml=smoke-results.xml
Expand Down
30 changes: 15 additions & 15 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ Use `uv run` to execute all commands (pytest, ruff, quarto). Do not use bare `py
Ruff is the linter and formatter. CI enforces both. Always run checks before committing:

``` bash
uv run ruff check src/ tests/ selftests/ examples/
uv run ruff format --check src/ tests/ selftests/ examples/
uv run ruff check src/ src/vip_tests/ selftests/ examples/
uv run ruff format --check src/ src/vip_tests/ selftests/ examples/
```

Or with just:
Expand All @@ -30,7 +30,7 @@ Or with just:
just check
```

Ruff rules: `E`, `F`, `I`, `UP`. Line length is 100. All Python directories (`src/`, `tests/`, `selftests/`, `examples/`) must pass. CI pins ruff to version 0.15.0 -- do not change the version without updating `.github/workflows/ci.yml`.
Ruff rules: `E`, `F`, `I`, `UP`. Line length is 100. All Python directories (`src/`, `src/vip_tests/`, `selftests/`, `examples/`) must pass. CI pins ruff to version 0.15.0 -- do not change the version without updating `.github/workflows/ci.yml`.

Auto-fix before committing:

Expand All @@ -52,18 +52,18 @@ uv run pytest selftests/ -v

Run selftests after any change to `src/vip/`. If you add new config fields, plugin hooks, or reporting features, add corresponding selftests. Plugin integration tests use the `pytester` fixture (subprocess isolation).

### Product tests (`tests/`)
### Product tests (`src/vip_tests/`)

BDD tests that run against real Posit Team deployments. These are organized by category:

```
tests/prerequisites/ # Server reachability, auth
tests/package_manager/ # CRAN/PyPI mirrors, repos
tests/connect/ # Auth, deploy, data sources, packages, email
tests/workbench/ # Auth, IDE launch, sessions, packages
tests/cross_product/ # SSL, monitoring, system resources
tests/performance/ # Load times, concurrency
tests/security/ # HTTPS, auth policy, secrets
src/vip_tests/prerequisites/ # Server reachability, auth
src/vip_tests/package_manager/ # CRAN/PyPI mirrors, repos
src/vip_tests/connect/ # Auth, deploy, data sources, packages, email
src/vip_tests/workbench/ # Auth, IDE launch, sessions, packages
src/vip_tests/cross_product/ # SSL, monitoring, system resources
src/vip_tests/performance/ # Load times, concurrency
src/vip_tests/security/ # HTTPS, auth policy, secrets
```

Product tests cannot run in CI (no products available). They are collected with `--collect-only` as a dry run in CI.
Expand All @@ -75,7 +75,7 @@ Every test is a pair of files:
1. **`.feature` file** -- Gherkin scenarios with a product marker tag
2. **`.py` file** -- Step definitions using `pytest_bdd`

Example feature file (`tests/connect/test_auth.feature`):
Example feature file (`src/vip_tests/connect/test_auth.feature`):

``` gherkin
@connect
Expand All @@ -86,7 +86,7 @@ Feature: Connect authentication
Then I see the Connect dashboard
```

Example step file (`tests/connect/test_auth.py`):
Example step file (`src/vip_tests/connect/test_auth.py`):

``` python
from pytest_bdd import scenario, given, when, then
Expand Down Expand Up @@ -145,13 +145,13 @@ Key principles:
| `src/vip/verify/site.py` | PTD Site CR parsing, vip.toml generation |
| `src/vip/verify/credentials.py` | Keycloak + interactive credential provisioning |
| `src/vip/verify/job.py` | K8s Job creation, log streaming, cleanup |
| `tests/conftest.py` | Root fixtures: clients, auth, runtimes, data sources |
| `src/vip_tests/conftest.py` | Root fixtures: clients, auth, runtimes, data sources |
| `report/index.qmd` | Quarto summary page |
| `report/details.qmd` | Quarto detailed results page |

## Fixtures available in product tests

These are defined in `tests/conftest.py` and available to all tests:
These are defined in `src/vip_tests/conftest.py` and available to all tests:

- `vip_config` -- the full `VIPConfig` object
- `connect_client` / `workbench_client` / `pm_client` -- httpx API clients (or `None` if not configured)
Expand Down
Loading
Loading