Testing guidelines¶
This doc explains how we test the frontend parsers and the online API.
Local unit tests¶
Use pytest + pytest-asyncio (
asyncio_mode = "auto").Mock HTTP (no network) with pytest-httpx (
httpx_mockfixture); in catalog tests, fake or mock the injected API client’sget_bytes()method.Coverage:
poe covreports project coverage; pre-push enforces 80% onpybragerone(CLI entrypoints omitted) and 100% patch vsmerge-base(origin/main)viascripts/check_patch_coverage.sh(diff-cover— same diff basis as Codecov PR patch). CI uploads the XML report to Codecov.Optional captured UI dumps: drop
*.jsfromone.brager.plintotests/assets/index,tests/assets/params,tests/assets/menus, ortests/assets/i18n. Files matchingtests/assets/**/*.jsare gitignored (do not commit vendor JS).tests/test_catalog_captured_assets.pyparses every file; empty directories skip. This does not hit the network.Scheduled public catalog watch (no login, not a PR gate):
.github/workflows/upstream-assets.ymlcomparesGET /v1/system/versionplus the homepageindex-*.jsfilename, then tree-sitter-parses the live index only when that fingerprint changes. When it parses, it also asserts a non-empty language config, units descriptor table, andunitsi18n namespace, rejects leftover JS\\x/\\uescapes, and — when the index mapsdeviceMenu0 — that a gated parse with APIDISPLAY_*strings yields tokens and thatpermissionModulevalues are not leftover_0x…['NAME']text. Local equivalent:uv run --group test python scripts/check_upstream_assets.py --always-parseorpytest --run-live tests/test_catalog_live_upstream.py.
Example layout:
tests/
test_api.py
test_api_rest.py
test_api_get_bytes_retry.py
test_catalog_permissions.py
test_catalog_asset_index.py
test_parser_resilience.py
test_i18n_parser.py
test_param_map_parser.py
test_gateway_prime_reconnect.py
test_gateway_dispatch.py
conftest.py
conftest.py (live toggle + session)¶
Tests that need real network access are marked @pytest.mark.needs_internet and are skipped by default; opt in with pytest --run-live or RUN_LIVE_TESTS=1.
"""Pytest configuration and shared fixtures.
This module contains pytest configuration settings and shared fixtures
used across the test suite.
"""
import os
import pytest
def pytest_addoption(parser: pytest.Parser) -> None:
"""Register the --run-live option for tests that require internet access."""
parser.addoption(
"--run-live",
action="store_true",
default=False,
help="run tests marked needs_internet (require network access)",
)
def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None:
"""Skip needs_internet tests unless --run-live or RUN_LIVE_TESTS=1 is given."""
if config.getoption("--run-live") or os.environ.get("RUN_LIVE_TESTS") == "1":
return
skip_live = pytest.mark.skip(reason="requires internet; pass --run-live or set RUN_LIVE_TESTS=1")
for item in items:
if "needs_internet" in item.keywords:
item.add_marker(skip_live)
@pytest.fixture(scope="session")
def anyio_backend() -> str:
"""Configure asyncio as the async backend for tests.
Returns:
str: The async backend name to use for testing.
"""
return "asyncio"
Example Test File¶
See the existing test files in the tests/ directory for examples of proper test structure and patterns.