Testing

The test suite lives under tests/, mirroring the package layout. The default run is fast and dependency-free; everything that needs a network, Docker, or a heavy optional extra is opt-in or skips 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 covers the library without any 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 still produces a green, honest run. Because the dev group installs every extra that has a cp314 wheel, a full uv sync environment exercises nearly all of them.

Two patterns appear throughout:

Recorded HTTP

Tests that exercise a real third-party HTTP API record their traffic with pytest-recording (VCR cassettes), so they replay 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 headline 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, so it does not collide with anything already listening locally, and it skips cleanly when Docker is not available. This is what lets the read/write path be tested 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 and is tested by driving the app through Textual's Pilot interface: mounting the app, sending key presses, switching tabs and views, and asserting on the rendered widget tree. These tests catch interaction regressions (a view that fails to switch, a query that inserts the wrong text) without a terminal.

Writing tests