Testing¶
Each package owns <import_root>/__tests__/, with unit/ and integration/ beside the code they test. Core and
CLI tests use fake executors or services and need no database. Database tests belong to rowx_postgres.
| Tree | What it holds |
|---|---|
packages/rowx-core/src/rowx_core/__tests__/unit/ |
Row plan, errors, migration source logic. No database. |
packages/rowx-core/src/rowx_core/__tests__/support/row_types.py |
The shared row-type table the core row plan executes. |
src/rowx/__tests__/unit/ |
CLI, checker discovery and config, with fakes. No database. |
packages/rowx-postgres/src/rowx_postgres/__tests__/unit/ |
Compilers, codecs and checker pieces that do not need a server. |
packages/rowx-postgres/src/rowx_postgres/__tests__/integration/ |
Queries, describe, migrations, the checker against Postgres. |
packages/rowx-postgres/src/rowx_postgres/__tests__/conformance/ |
The row-type grammar against the runtime and the checker, and the pyright/mypy reveal test. |
packages/rowx-postgres/src/rowx_postgres/__tests__/support/schema.sql |
The per-test schema. |
packages/rowx-postgres/src/rowx_postgres/__tests__/support/engine.py |
The embedded engine's recorded divergences (Embedded lane). |
packages/rowx-embedded/src/rowx_embedded/__tests__/unit/ |
The embedded glue (Database, URL grammar, scratch, snapshot cache) against a fake engine. |
packages/rowx-embedded/src/rowx_embedded/__tests__/integration/ |
The glue and the engine binding (test__engine.py, slow) against the real engine. They run with the rest of just test-py; the glue tests are also marked embedded, so the embedded lane runs them. The host's Rust tests run with just test-rust-embedded (cargo test -p rowx-embedded-host). |
A test file is test_<module>.py for the module it covers.
Database¶
scripts/pg.sh starts postgres:18. Tests read ROWX_TEST_DSN. When it is unset, tests marked db skip. When
ROWX_REQUIRE_DB=1, as CI sets on Linux, they fail instead. Do not sleep to synchronise. Timing tests poll
pg_stat_activity and are marked timing.
Integration tests build a schema t_<hex> from support/schema.sql and set search_path through the DSN. They do
not use public. The nullability evidence cases live in
integration/describe/test_nullability_regression.py, one case per row of the
evidence table, in that order. Known-limit cases live in
integration/describe/test_known_limits.py. Checker fixtures live under integration/check/cases/. Snippet tests
execute fences tagged python test in integration/docs/test_snippets.py. The snippet glob includes the
use-rowx skill. Ordinary python
fences are not executed.
Rust tests are cargo test over the default workspace members (just test-rust). Database tests in
crates/rowx-postgres/tests/ use the same DSN. crates/rowx-core has no database.
Commands¶
| Command | What it runs |
|---|---|
just check |
uv run prek run --all-files: rumdl, ruff, ty check on src, packages/rowx-core/src and packages/rowx-postgres/src, cargo fmt, cargo clippy. |
just test-py |
uv run pytest. |
just test-rust |
cargo test over the default members (no Wasmer). |
just test-rust-embedded |
cargo test -p rowx-embedded-host. |
just docs-build |
zensical build --clean --strict. |
just check-embedded |
Clippy for the embedded companion, a non-default workspace member: uv run prek run cargo-clippy-embedded --hook-stage manual. |
just test-embedded |
The embedded lane: ROWX_TEST_DSN=embedded:// uv run pytest -m "(db and not server) or embedded". |
ty is the type-check gate. pyright and mypy stay in the dev group because
conformance/test_typecheckers.py runs both against the public query annotations. They are not part of just check.
Linux CI runs just check and the database tests against postgres:18. The macOS job runs just check and does
not start a database.
What the suites pin¶
- Row types.
support/row_types.pyin core andconformance/row_types.pyinrowx_postgresare the grammar. A form the runtime accepts and the checker rejects is recorded as such. The two tables are not one file. Core rows do not need a database. Conformance rows that describe SQL do. - Types.
crates/rowx-postgres/src/types.rsis the only OID table. A new leaf joins the generated type table, the codecs, and the conformance rows. Wire codecs are unit-tested as pure functions. - Nullability. The evidence table is an integration test, in row order. The plan walker has Rust tests on captured plans. A NOT NULL verdict that a NULL contradicts fails. Over-promotion is reported and is not a failure by itself. The limits are Known limits.
- Checker. Discovery, the source map, resolution and comparison have unit tests.
integration/check/runs the CLI against Postgres, including scratch databases, drift, and migration commands.query_uncheckedcases assert that SQL is not described and that a badTis still reported. - Migrations. Filename grammar and the plan are tested without a database. Apply, revert, the advisory lock and
the history table are
rowx_postgresintegration tests.migrate addis tested with no backend installed. - Distributions.
src/rowx/__tests__/unit/test_validate_distributions.pychecks the three lockstep packages:rowx-corehas no runtime dependencies,rowx-postgresdepends only onrowx-core, androwxdoes not depend onrowx-postgresunless the extra is selected. Itsembeddedextra must add exactlyrowx-postgresand the platform gatedrowx-embedded==<version>. The embedded wheels have their own check,scripts/validate-embedded-wheels.py, and the version agreement is inscripts/validate-release-versions.py.
Test plugin¶
rowx.testing (src/rowx/testing.py) is opt-in. Nothing registers it as a pytest plugin entry point. A project
sets pytest_plugins = ["rowx.testing"] and depends on rowx[pytest], which adds pytest and does not add a
database backend. The plugin reads [tool.rowx.migrate] and the database URL. It builds a template database and
gives each test a clone. Without a URL the tests skip, or fail when the project requires a database. The plugin
tests under src/rowx/__tests__/unit/test_testing.py use fakes and do not connect.
Embedded lane¶
| Marker | Meaning |
|---|---|
db |
Needs a live Postgres (ROWX_TEST_DSN). |
server(needs=...) |
Needs a real server: sessions, cancel, tls, create-database, pg-stat-activity, timing, auth or tcp. timing implies server. |
server(divergence=...) |
Hits an engine divergence: inet-cidr, xml, encoding, locale or backend-pid. |
embedded |
Runs the embedded engine and needs no server; the embedded lane selects it. |
With ROWX_TEST_DSN=embedded:// the suite starts one instance per process (per xdist worker) and points the tests at
it; server tests skip, a test that skips for any other reason fails (so a lost precondition cannot shrink the lane),
and a test that leaves a session open fails the next tests with one line naming it. The lane is
pytest -m "(db and not server) or embedded". Docker stays the full-capability lane, so ROWX_REQUIRE_DB=1 runs with a
server run everything.
The divergences are recorded in packages/rowx-postgres/src/rowx_postgres/__tests__/support/engine.py. Removing a mark
is the fix for its cause:
| Divergence | What differs |
|---|---|
inet-cidr |
Binary inet and cidr values carry WASI address-family bytes. |
xml |
The engine has no XML support (0A000). |
encoding |
No conversion libraries: SET client_encoding = 'SJIS' fails. |
locale |
No locale data: lc_monetary stays C. |
backend-pid |
Every session reports the same pg_backend_pid(). |
Not recorded as a mark, because the tests that touch it are engine tests: closing a directory instance stops the
backend as a process exit does, with no shutdown checkpoint, so the next open runs crash recovery. The host runs a
CHECKPOINT just before a normal close (never on an abandon), so a sequence does not skip the 32 values it pre-logs.
UNLOGGED relations are still reset by every reopen, which a server does only after a crash.