Raxx · internal docs

internal · gated

Testing Raptor (backend_v2)

Two-dialect test strategy

Raptor's test suite supports two database dialects:

Mode Dialect When How to invoke
SQLite (local dev) sqlite Local dev — fast, no Postgres required pytest backend_v2/tests
Postgres postgresql CI gate + local validation DATABASE_URL=... pytest backend_v2/tests

Both modes share the same test code. The db_engine and db_session fixtures in backend_v2/conftest.py detect the dialect from DATABASE_URL and adapt automatically. Individual tests do not need any dialect-specific code.

CI note: As of 2026-07-03 the SQLite-mode backend-tests CI job has been retired. The single backend-tests-postgres job (display name "Backend tests") is now the canonical CI signal. SQLite mode is still valid for local dev.

ADR reference: docs/architecture/adr/0070-pytest-postgresql-over-testcontainers.md


SQLite mode (default)

No environment variables needed. Run from the repo root:

python -m pytest backend_v2/tests -q

An ephemeral SQLite file is created under pytest's tmp_path per session. The Alembic baseline migration (0001_raptor_baseline.py) is applied automatically so the schema matches the Postgres-typed DDL. The DB is discarded after the session.


Postgres mode (CI gate)

Prerequisites

macOS (developer machine):

brew install postgresql@15
brew services start postgresql@15
# The installer adds pg_ctl to PATH automatically; verify:
pg_ctl --version

Create the test database:

createdb raptor_test

Ubuntu / Debian (CI or Linux dev machine):

sudo apt-get update && sudo apt-get install -y postgresql postgresql-client
sudo -u postgres createuser --superuser "$USER"
createdb raptor_test

Run against Postgres

DATABASE_URL=postgresql://localhost/raptor_test \
  python -m pytest backend_v2/tests -q

Or use the --postgres pytest flag (spawns an ephemeral pg_ctl process via pytest-postgresql; does not require a running Postgres service):

python -m pytest backend_v2/tests -q --postgres

The --postgres flag is useful for quick one-off validation on a machine that has pg_ctl on PATH but no persistent Postgres instance running. In CI the services: postgres: container approach is used instead (lower latency).

When to run Postgres mode locally


Fixtures

db_engine (session-scoped)

A SQLAlchemy Engine connected to the test database. The Alembic baseline migration is applied once at session startup. Use this when you need direct engine access (reflection, DDL inspection, bulk operations).

def test_table_exists(db_engine):
    from sqlalchemy import inspect
    assert "symbols" in inspect(db_engine).get_table_names()

db_session (function-scoped)

A SQLAlchemy Session that rolls back after each test. Use this for all DML — rows inserted in one test are never visible in the next.

def test_insert_visible_in_session(db_session):
    from sqlalchemy import text
    db_session.execute(text("INSERT INTO symbols (symbol, ...) VALUES (...)"))
    db_session.flush()
    result = db_session.execute(text("SELECT symbol FROM symbols")).fetchall()
    assert len(result) == 1

Legacy fixtures

Tests that predate RM-8 (test_engine.py, test_baseline_migration.py) use their own in-module fixtures and do not depend on db_engine / db_session. This is intentional — the RM-8 fixtures are opt-in for new tests.


CI jobs

Job id Display name Dialect Trigger
backend-tests-postgres Backend tests Postgres 15 Every push to develop where backend == true or deps == true

The backend-tests (SQLite) job was retired 2026-07-03 (#1556, #2125). The Postgres job is the sole backend CI signal and also runs the coverage gate (floor 30%, tracked to 40% in #2125).

The job uses a services: postgres:15 container and sets DATABASE_URL=postgresql://raptor:raptor_test@localhost:5432/raptor_test. alembic upgrade head is run as a pre-test step so the schema is applied before pytest collects tests. A 90-minute job cap and --timeout=120 per-test limit mitigate the known pytest-cov hang (#3928).


pyproject.toml / pytest.ini

Pytest is configured via pyproject.toml at the repo root. The --postgres flag is registered in backend_v2/conftest.py via pytest_addoption.


Common failures

Symptom Likely cause Fix
alembic upgrade head fails in conftest 0001_raptor_baseline.py has a syntax error for the active dialect Run alembic upgrade head manually with DATABASE_URL set and inspect the error
pytest_postgresql not installed requirements.txt not installed in current venv pip install -r backend_v2/requirements.txt
pg_ctl: command not found when using --postgres Postgres not installed locally brew install postgresql@15 (macOS) or apt-get install postgresql
CI Backend tests fails but local SQLite passes Dialect-specific SQL (e.g. ? placeholder, SQLite-only pragma) Replace with SQLAlchemy parameterised form; run locally with DATABASE_URL=... pytest