OpenParable
Skip to content
← Back to skills

houndarr-testing

Testing & Quality

Houndarr's pytest patterns. Loads when reading or editing tests/ files. Covers the fixture dependency graph, the FK seeding pattern for cooldowns/search_log tests, the local _login() and CSRF helpers in route tests, and the auth-state reset in test_settings.

View on GitHub ↗
QUICK START

How to use this skill

Bring this guide into your coding agent with a prompt tailored to the tool you use.

  1. Open your project in Codex.
  2. Copy and paste the prompt below.
  3. Review files and risks before approving any action.
Prompt to paste
Inspect the Agent Skill at https://github.com/av1155/houndarr/blob/HEAD/.agents/skills/houndarr-testing/SKILL.md and all supporting files. Report provenance, permissions, scripts, dependencies, and security risks. Propose an exact installation into .agents/skills/houndarr-testing, but wait for my approval before writing files or running scripts.

Copying this prompt does not install or run the skill. Review third-party files before use.

Houndarr testing patterns

Framework

  • pytest + pytest-asyncio: asyncio_mode = "auto", asyncio_default_fixture_loop_scope = "function", addopts = "-q --tb=short" (set in pyproject.toml).
  • Async tests: use @pytest.mark.asyncio() (with parens), return -> None.
  • HTTP mocking: respx for httpx calls; use @respx.mock decorator.
  • App testing: TestClient (sync) or AsyncClient via ASGITransport.

Fixture dependency graph

tmp_data_dir          (temp directory, no deps)
  ├── db              (init SQLite, depends on tmp_data_dir)
  └── test_settings   (AppSettings + auth state reset, depends on tmp_data_dir)
        ├── app       (TestClient, depends on test_settings)
        └── async_client (AsyncClient, depends on test_settings)

db and test_settings are siblings; both depend on tmp_data_dir independently. Tests that need a database AND the app must request both db and app (or use fixtures that depend on db).

The test_settings fixture resets _auth._serializer, _auth._setup_complete, and _auth._login_attempts so auth state does not bleed between tests.

FK constraint pattern

Tests touching cooldowns or search_log must seed the instances table first via the seeded_instances fixture (defined locally in tests/test_engine/test_search_loop.py, tests/test_engine/test_golden_search_log.py, tests/test_engine/test_supervisor.py, and tests/test_services/test_cooldown.py):

@pytest_asyncio.fixture()
async def seeded_instances(db: None) -> AsyncGenerator[None, None]:
    async with get_db() as conn:
        await conn.executemany(
            "INSERT INTO instances (id, name, type, url, encrypted_api_key)"
            " VALUES (?, ?, ?, ?, ?)",
            [(1, "Sonarr Test", "sonarr", "http://sonarr:8989", _ENC_KEY),
             (2, "Radarr Test", "radarr", "http://radarr:7878", _ENC_KEY)],
        )
        await conn.commit()
    yield

Engine tests set encrypted_api_key to a valid Fernet-encrypted value (_ENC_KEY). The simpler 4-column form (without encrypted_api_key) is used in test_cooldown.py where only FK constraints matter.

Login helper for route tests

A _login() helper is defined locally in each route test file that needs it (test_logs.py, test_settings.py, test_status.py):

def _login(client: TestClient) -> None:
    client.post("/setup", data={"username": "admin", "password": "ValidPass1!", ...})
    client.post("/login", data={"username": "admin", "password": "ValidPass1!"})

CSRF helper for route tests

Mutating authenticated routes require a valid CSRF token. Use the helpers from tests/conftest.py:

from tests.conftest import csrf_headers, get_csrf_token

resp = client.post("/settings/instances", data=form, headers=csrf_headers(client))
resp = client.delete("/settings/instances/1", headers=csrf_headers(client))

Current CSRF exemptions: POST /logout, /login, /setup.

Markers

  • @pytest.mark.integration: 12 async engine-cycle cases in tests/test_e2e/ plus 15 Playwright flows in tests/e2e_browser/ (browser tree excluded from default collection via norecursedirs; test_e2e/ is collected and filterable).
  • @pytest.mark.pinning: characterisation tests pinning current behaviour before a refactor batch. Unit-scope; runs in the default suite. Add one whenever a refactor needs a behavioural lock.

Running

just test, just test-quick, just test-integration, and just pin run with pytest -n auto by default (pytest-xdist). Override with PYTEST_WORKERS=0 for serial triage, or PYTEST_WORKERS=4 to constrain. just test-browser chromium runs the Playwright e2e flows serially (shared stack on fixed ports).