Testing and CI

How to run tests locally and how GitHub Actions is wired.

Quick start (local)

  1. Clone the repo and install Python 3.12, 3.13, and 3.14 (see requires-python and tox envs).

  2. Install dev dependencies with uv:

    uv sync --extra test
    
  3. Lint (ruff, bandit, format, …):

    uv run tox -e lint
    
  4. Dependency audit:

    uv run tox -e audit
    
  5. One Python version:

    uv run tox -e py314
    

    Bare uv run pytest uses pyproject.toml addopts and prints term-missing coverage.

  1. All supported versions + combined coverage (optional):

    uv run tox -e coverage
    
  2. Parallel matrix (before commit):

    uv run tox run-parallel -p auto -o --skip-env lint
    

    Runs py312, py313, and py314 concurrently, then report (depends) to combine coverage. Use -o for live output.

Subset of tests: uv run tox -e py314 -- tests/v1/test_auth.py -q

Documentation

Same gate as CI — run it whenever you change docs:

uv run tox -e docs

Requires uv sync --extra test so tox is on your PATH. Build fails on Sphinx warnings (-W). See Contributing for doc layout, when to edit user-guide pages, and a direct sphinx-build workflow for local preview.

Tox environments

Env

Purpose

lint

pre-commit on all files (includes bandit on src/)

audit

pip-audit on installed runtime dependencies

docs

Sphinx HTML build (warnings as errors)

default

pytest on current interpreter

py312 / py313 / py314

pytest + coverage data in .coverage.{envname}

report

coverage combine, terminal report, coverage.xml

coverage

Depends on report; convenience meta-env

build / clean / publish

Packaging (release workflow)

Python versions are defined once in [testenv:py3{12,13,14}]; tox picks the interpreter from the env name (no per-env basepython blocks).

Coverage behavior

  • Local pytest — addopts = "--cov --cov-report=term-missing" in pyproject.toml; [tool.coverage.run] scopes measurement to the aioafero package (source_pkgs) and omits tests/*. Do not also pass --cov=aioafero (duplicates source_pkgs and warns module-not-measured).

  • Tox py envs — replace addopts with --cov --cov-report= so each version collects silently; report prints the combined result once.

  • Why replace, not append? pytest-cov treats --cov-report as multi-allowed; appending --cov-report= does not cancel term-missing from config.

GitHub Actions

Workflows:

  • .github/workflows/cicd.yaml — lint, audit, docs, test matrix (3.12–3.14), combined coverage, Codecov (on push/PR/weekly schedule to main).

  • .github/workflows/codeql-analysis.yml — CodeQL static analysis (push/PR/weekly).

  • .github/workflows/reusable-ci.yaml — shared jobs; inputs for Python versions and run-codecov.

  • .github/workflows/release.yaml — runs CI then tox -e build and PyPI publish, and triggers a Read the Docs build (default branch only; requires READTHEDOCS_TOKEN).

CI test step mirrors tox py envs: COVERAGE_FILE=.coverage.py312, pytest with -o addopts="--cov --cov-report=", JUnit XML per version. Coverage artifacts use include-hidden-files: true because .coverage.* files are dotfiles excluded by upload-artifact@v4 by default.

Maintenance

  • Add a Python version — extend [testenv:py3{12,13,14}] factor in tox.ini, update python-versions default in reusable-ci.yaml, install the interpreter locally, bump classifiers in pyproject.toml.

  • Dependabot — .github/dependabot.yml (GitHub Actions, pip, pre-commit); enable Dependabot alerts and security updates in repo settings (see SECURITY.md).

Security scanning

See SECURITY.md in the repository root.

  • Bandit — Python security lint via pre-commit (tox -e lint); config in pyproject.toml [tool.bandit].

  • pip-audit — OSV/CVE scan of runtime deps (tox -e audit); runs in CI on every push/PR and weekly.

  • CodeQL — GitHub code scanning workflow on push/PR/weekly.

  • Dependabot — weekly version update PRs plus grouped security-updates in dependabot.yml.