Skip to content

Testing architecture

The suite is organized by test level and product module, not by delivery stage.

Layout

tests/
  conftest.py              # root fixtures (e.g. tui_fx), interpreter guards
  fixtures/                # shared YAML repos, fake providers
  helpers/                 # CliRunner wrappers and other utilities
  unit/                    # fast, isolated, no full CLI/TUI workflows
  integration/             # service + CLI + fixture-repo workflows
  smoke/                   # thin production-readonly / CLI surface checks

Each of unit/, integration/, and smoke/ has a conftest.py that applies pytest.mark.unit, pytest.mark.integration, or pytest.mark.smoke to every test in that tree via pytest_collection_modifyitems (directory-scoped auto-marking).

Subdirectories under those roots mirror product areas (agent/, reconciliation/, tui/, question_lifecycle/, etc.).

Markers

Registered in pyproject.toml:

Marker Meaning
unit Pure logic / models / registries
integration Fixture repos, services, CLI, TUI pilots
smoke Read-only prod baseline or CLI surface

Filter examples:

uv run pytest -m unit -n0 --cov-fail-under=0
uv run pytest -m 'integration or smoke' -q

CLI helpers

Prefer tests.helpers.cli over ad-hoc CliRunner usage:

  • invoke_rig_human(*args) — human actor (default)
  • invoke_rig_bot(*args) — prepends --am-bot

Fixtures

Stage-era isolated repos (fx15, fx17, fx19, fx20, fx21, iso, …) live in tests/fixtures/ (notably repo_fixtures.py) so split modules share one definition.

Root tui_fx remains in tests/conftest.py for TUI mutation isolation.

Never mutate production data/ from tests. Smoke tests that read production YAML must be read-only.

Coverage gate

Full suite (CI / local validation) enforces 80% via pyproject.toml addopts (--cov-fail-under=80):

uv run pytest -q

Subset / debug without the gate:

uv run pytest -m unit --cov-fail-under=0
uv run pytest -m integration --cov-fail-under=0
uv run pytest -m smoke --cov-fail-under=0
uv run pytest path/to/tests -n0 --cov-fail-under=0

CLI (music_rig.cli) and TUI packages are omitted from coverage; domain and service modules are the gate.

Lint gates

Ruff (Python) and ryl (YAML) are configured in pyproject.toml. Pre-commit runs them via local uv run system hooks (no duplicated rule config):

uv run pre-commit run --all-files
# or directly:
uv run ruff check --fix .
uv run ruff format .
uv run ryl check .

Docs build (CI + local quality gate):

uv run mkdocs build --strict

Reconciliation

See reconciliation.md for module ownership, authority flow, and where CLI suggestions are rendered. Manual load benchmarks:

uv run python scripts/bench_reconciliation.py