Testing

The test suite under tests/ mirrors the package layout. The default run is fast and dependency-free. Tests that need a network, Docker, or a heavy optional extra are opt-in or skip cleanly.

uv run pytest                 # the default suite
uv run pytest tests/store     # one subtree
uv run pytest -k materialize  # by keyword

Configuration is in pyproject.toml under [tool.pytest.ini_options]: --import-mode=importlib, testpaths = ["tests"], and the integration marker.

Unit and functional tests

The default run requires no external service. Adapters that wrap an optional extra are tested when that extra is installed and skip with a clear reason when it is not, so a partial environment can still complete successfully. Because the dev group installs every extra that has a cp314 wheel, a full uv sync environment exercises nearly all of them.

Round-trip and lazy-import tests follow distinct patterns:

Recorded HTTP

Tests that exercise a real third-party HTTP API record their traffic with pytest-recording (VCR cassettes). The tests replay these cassettes offline and deterministically. The Hugging Face Hub tests in tests/integrations/hf/test_hub.py use this. To refresh a cassette, delete it and re-run with recording enabled and real credentials; commit the new cassette with the change.

Integration tests and the local PDS

Tests marked integration are deselected unless you pass --run-integration:

uv run pytest --run-integration -m integration   # only the integration tests
uv run pytest --run-integration                   # the whole suite, integration included

The flag and the marker are registered in tests/conftest.py. The main integration fixture starts a real Bluesky PDS with Docker Compose (tests/pds/docker-compose.yml), waits for it to come up, provisions an account, and tears it down afterward. It picks a free port automatically to avoid collisions with local services and skips cleanly when Docker is not available. Tests that use this fixture exercise the read/write path end to end against an actual server rather than a mock.

CI runs the integration job separately from the fast checks; see .github/workflows/ci.yml.

TUI tests

The Textual explorer has its own fixtures in tests/tui/conftest.py. Its tests drive the app through Textual's Pilot interface by mounting the app, sending key presses, switching tabs and views, and asserting on the rendered widget tree. They catch interaction regressions (a view that fails to switch, a query that inserts the wrong text) without a terminal.

Writing tests