Testing
How the Python harness is tested. Repo: felix-run/felix.
Layout
Section titled “Layout”| Suite | Path | Role |
|---|---|---|
| Unit | tests/unit/ |
Schema, patterns, governance, stores (often memory:// DB + object_store=memory), auth modes |
| Integration | tests/integration/ |
FastAPI HTTP surfaces (/health, /chat, /v1, …) against a test app |
| Conformance | tests/conformance/ |
One contract run against every backend of a store — session, memory, manifests, approvals, eval, documents, usage, fibers, retention, RLS, migrations |
| Smoke | tests/test_smoke.py |
Coarse import / build / optional live-model checks |
| Eval | CLI + felix.eval |
Offline datasets; --mock scores rubric without a live model |
The conformance suite is the one to know about, because it can pass without proving anything. A
store’s contract is written once and run against each backend — the in-memory arm always runs, but
the Postgres arm runs only when FELIX_CONFORMANCE_DATABASE_URL points at a reachable database
and skips otherwise. That skip is a real hole in coverage rather than a pass, which matters because
the logic under test exists twice: once for memory:// and once in SQL. CI sets
FELIX_CONFORMANCE_REQUIRE_POSTGRES=1 so a missing database fails the job instead of silently
skipping half of it.
FELIX_CONFORMANCE_DATABASE_URL=postgresql+psycopg://user:pass@localhost:5432/db \ ./scripts/test.sh tests/conformance -qEvery store that has gained a Postgres arm so far has produced a real divergence between the two
copies, which is the argument for adding the rest. The eval store’s was the worst: put_dataset
overwrote an existing item on the in-memory twin and raised UniqueViolation on Postgres, so the
second run of any eval failed on every real deployment while the suite stayed green. Adding a
backend to BACKENDS in a contract file makes it inherit every assertion in that file.
No Vitest / miniflare. Run via:
make check # ruff + ty + pytest with the coverage floor + format checkmake check-ci # + every other gate CI runs without infrastructure./scripts/test.sh # the full suite./scripts/test.sh tests/unit/test_react_loop.py -q # one file./scripts/test.sh -k compact # one themety needs the optional extras (make install-full). On a lean install make check skips it, says so
as its last line, and still exits 0; make check-ci and STRICT=1 make check fail instead.
Patterns when adding tests
Section titled “Patterns when adding tests”- Settings —
tests/support/factories.py:make_settings(**overrides)builds the hermeticmemory://baseline (no inbound auth, in-memory object store); name only the fields the test cares about. Shared helpers live intests/support/, and a test never imports another test module or a conftest. - Stub tools — pass an explicit
ToolProvider/ in-memory tools; don’t depend on full Compose wiring. - Manifests — build with
apiVersion: felix/v1dicts orparse_manifest; don’t require the on-disk bundle. - Tenant isolation — assert another tenant cannot read rows you wrote.
- Async — mark with
@pytest.mark.asynciowhere the harness APIs are async.
Mock eval
Section titled “Mock eval”felix eval -d my-dataset -m quick --fixture ./dataset.json --mock--mock compares against rubric mock_answer / expect fields — CI-friendly without provider keys. Omit --mock to drive the candidate manifest through a real model.
CLI helpers used in CI / ops
Section titled “CLI helpers used in CI / ops”| Command | Purpose |
|---|---|
felix migrate |
Alembic upgrade |
felix doctor |
Read-only config / connectivity checks |
felix eval … |
Offline eval runs |
felix mint-jwt |
Self-issued JWT for jwt mode |
felix bundle-manifests |
Validate bundled YAML |
felix validate-manifest <file> |
Validate one manifest as PUT /manifests would, before it is stored |
felix chat |
Chat with a manifest on a running server, one line at a time (--url, --manifest, --api-key/FELIX_API_KEY, --stream); make cli runs it |
Local stack for integration
Section titled “Local stack for integration”make bootstrap && make up # the whole stack in Docker; it migrates before the API startsmake db migrate dev # or: Postgres + Valkey in Docker, the API from the checkoutLean hosts: make up-lite. Details: getting started, deploy.