Testing and CI#

This page documents how to test cellucid-python locally and what CI currently enforces.


What tests exist#

Tests live under:

  • cellucid-python/tests/

The suite covers input-format classification and serving every registry format (test_input_formats.py, test_serve_input_formats.py), the Cellucid-ready AnnData and MuData writers, exact scientific value and identity contracts, HTTP routes and server lifecycle, the viewer channel (test_viewer_channel_contract.py) and Jupyter messaging, session files, the history and the apply (test_sessions.py, against the golden history in tests/fixtures/history_golden/ that the web app and cellucid-r read too; test_session_layout.py, the chunk layout the web app’s writer makes: parts shared, cyclic, expanding past what a page holds, unreferenced or renamed, steps and values in many pieces, chunks stored as they are, parameters naming lazy arrays; test_history_words.py, the methods text and pseudocode rules the golden branches do not reach, string for string with cellucid-r; test_history_model.py, the generated model against history_model.schema.json), the local MCP server (test_mcp.py: the pinned mcp_tools.json, the shared local_agents.json fixture, the agent messages of the viewer channel, a viewer’s publication and loopback endpoint and its refusals, and cellucid mcp over stdio against a fake page: initialize, lists, calls, errors, cancellation, elicitation, list changes, a viewer stopping), vector fields, cache and path confinement, documentation, cross-platform repository paths, and release artifacts. The MuData tests need mudata, which the dev extra brings with it (they are skipped without it). On Python 3.11 pip installs mudata 0.3 (mudata 0.4 needs Python 3.12); tests/conftest.py sets its pull_on_update=False, so every MuData the suite builds or reads keeps each modality’s columns in the modality, as mudata 0.4 does. test_minimum_python_grammar.py parses every Python file with the grammar of the oldest Python requires-python admits, including the f-strings only Python 3.12 accepts. Changes to a public contract require a focused regression test at the owning boundary.

Some tests compare with the web app or run its readers: the golden history and the history model schema byte for byte (test_sessions.py, test_history_model.py), the session README’s and codecs’ constants (test_sessions.py, test_session_layout.py), and the web app’s AnnData and priority readers on what prepare() writes (test_prepare_data_and_mudata_contract.py, test_prepare_priority_order_contract.py). They find the web app at CELLUCID_WEB_ROOT (a checkout or worktree of the cellucid repository), else in the cellucid checkout beside this repository (tests/_web_root.py). A web checkout that is named or present must hold the file a test reads: one it lacks fails the test. Only when there is no web checkout at all, as in this repository’s own CI, are they skipped, saying so.

CELLUCID_WEB_ROOT=/path/to/cellucid python -m pytest

Running tests locally#

From the cellucid-python/ folder:

python -m pytest

Useful variants:

python -m pytest -k sessions
python -m pytest -k vector_fields

Coverage (if installed):

python -m pytest --cov=cellucid

Linting, formatting, and types#

Ruff#

Format the Python files you intentionally changed, then lint the surfaces CI lints (src and tests; the release workflow adds scripts/validate_release.py and scripts/normalize_sdist.py):

python -m ruff format path/to/changed_file.py
python -m ruff check src tests

To check an intentionally formatted file without changing it:

python -m ruff format --check path/to/changed_file.py

mypy#

python -m mypy src/cellucid

Note: mypy is configured pragmatically (ignore_missing_imports = true).


CI workflows (GitHub Actions)#

Workflows live under:

  • cellucid-python/.github/workflows/

Current workflows:

  • docs-check.yml: builds Sphinx docs with warnings as errors (sphinx-build -W --keep-going) on PRs and pushes to main

  • test.yml: installs .[dev] (which brings mudata) and runs the complete pytest suite, Ruff, mypy, and distribution checks on Python 3.11–3.14 on Ubuntu, and on 3.11, 3.12 and 3.14 on macOS and on Windows

  • pypi-publish.yml: runs when a GitHub Release is published (and on nothing else); proves the release and tag provenance, validates source and docs, normalizes and verifies the source-distribution digest, installs both artifacts on all three operating systems, and publishes through trusted identity (Release process)

  • readthedocs.yml: triggers the Read the Docs latest build via API on a push to main



Troubleshooting#

Symptom: “Tests pass locally but docs-check fails”#

Docs-check installs only the docs extra and builds Sphinx.

Common causes:

  • broken MyST syntax (unclosed fences),

  • bad {doc} links,

  • missing files referenced by toctrees.

Fix: run the exact CI command, which treats warnings as errors:

sphinx-build -W --keep-going -b html docs docs/_build/html

Symptom: “Ruff complains but formatting looks fine”#

Run both checks on the files you changed:

python -m ruff format path/to/changed_file.py
python -m ruff check path/to/changed_file.py

Any rule suppression must be narrow and justified in the review description.