Testing
pgRDF is tested in layers. The fast ones run inside a pgrx-managed server; the rest run SQL against a real postgres:18-trixie server with the extension mounted in, the same way it is deployed.
Layers
| Layer | Location | Local command | In CI |
|---|---|---|---|
| Rust unit and pgrx integration tests | #[test] and #[pg_test] in src/, plus tests/pgrx/ | just test (in the builder image) or just test-native | test job |
| SQL regression (golden output) | tests/regression/ | just test-regression | regression job |
pg_dump round trips | tests/regression/scripts/pg-dump-roundtrip.sh, pg-dump-dict-roundtrip.sh | just test-pg-dump-roundtrip | dictionary round trip |
| Artifact parity | tests/regression/scripts/verify-installed-artifacts.sh | just test-artifact-parity | regression job |
| W3C-shape SPARQL harness with differential oracle | tests/w3c-sparql/, tests/oracle/ | just test-w3c | regression job |
| W3C SHACL Core gate and SHACL-SPARQL fixtures | tests/w3c-shacl/ | just test-shacl-manifest (--pgrdf, --sparql) | regression job |
| LUBM-shape correctness | tests/perf/lubm-shape/, tests/perf/lubm-shacl-sparql/ | just test-lubm | regression job |
| SHACL capability document | tests/shacl-capability/ | tests/shacl-capability/run.sh | manual |
| Contract suite | tests/lib-tdd/ | tests/lib-tdd/run.sh | manual |
| Real-world ontology parsing | tests/perf/smoke-ontologies.sh | tests/perf/smoke-ontologies.sh --check | manual |
| LUBM performance | tests/perf/ | just test-lubm-10, just benchmark | nightly |
Aggregate recipes:
just test-all # pgrx tests + SQL regression
just test-conformance # regression, W3C SPARQL and SHACL, LUBM-shape, pg_dump round trip
just test-everything # pgrx tests + test-conformance
just smoke-cold # tear down the compose stack, rebuild, start, then run test-conformanceRun just smoke-cold after touching anything in compose/, fixtures/ or the test SQL itself: such changes can pass against a warm server and break on the next clean start.
Rust and pgrx tests
cargo pgrx test pg18 compiles the extension with the test harness, starts a pgrx-managed PostgreSQL 18, installs the extension and runs both plain #[test] units and #[pg_test] functions. The #[pg_test] functions run inside the server, each in a transaction that is rolled back. pgrx requires them to live in the same crate as the extension, so they sit next to the code they test, in mod tests blocks. just test runs the suite inside the Linux builder image, which works on any host; just test-native runs it on the host toolchain.
SQL regression
Each tests/regression/sql/NN-<topic>.sql is piped to psql against the compose server, and its output is compared with tests/regression/expected/NN-<topic>.out. run.sh runs psql with tuples-only, unaligned output, so a golden file holds just the result rows. The number prefix sets the run order, and each file's header comment states what it tests.
just test-regression # every file
bash tests/regression/run.sh 71-shacl-real # one file by name
ACCEPT=1 just test-regression # rewrite expected/ from actual outputWrite expected output by hand from the fixture and the spec of the feature under test. ACCEPT=1 is for output-format churn only (for example a PostgreSQL minor release that changes how a value prints), never for accepting new query results.
The scripts in tests/regression/scripts/ need more than psql:
pg-dump-roundtrip.shseeds graph IRI bindings, dumps the database withpg_dump, drops the extension, restores, and checks the bindings survived.pg-dump-dict-roundtrip.shdoes the same for dictionary contents.verify-installed-artifacts.shbuilds the current tree into a temporary directory, byte-compares it withcompose/extensions/, checks that the running container mounts this repository's files and that their hashes match, and afterCREATE EXTENSIONchecks thatextversionandpgrdf.version()match the control file. This catches a server that reports the right version while running stale files.
W3C-shape SPARQL harness
Each directory in tests/w3c-sparql/ is one test: data (data.ttl or setup.sql), a query (query.rq) and expected rows (expected.jsonl). The tests cover BGPs, modifiers, OPTIONAL, UNION, MINUS, FILTER functions, aggregates and HAVING, BIND, ASK, GRAPH, UPDATE, CONSTRUCT, DESCRIBE, property paths, type-aware ORDER BY, reasoning-dependent queries and TriG/N-Quads loading. Expected outputs are written by hand against the SPARQL 1.1 specification.
The differential oracle (tests/oracle/, a standalone Rust binary pgrdf-oracle) also evaluates eligible fixtures with spareval, Oxigraph's SPARQL evaluator, over an in-memory copy of the data, and compares the answers after canonicalisation and blank-node isomorphism checks. An intended difference, such as the HAVING alias extension, is recorded as a known divergence rather than a failure.
W3C SHACL harness
tests/w3c-shacl/fixtures/core/ holds a vendored subset of the W3C data-shapes SHACL Core tests: the unmodified W3C source (<name>.w3c.ttl), the data-plus-shapes file the harness loads (<name>.ttl) and a hand-derived <name>.expected.json. The fixtures are checked in, so the harness never fetches anything.
just test-shacl-manifest: the Core gate, 25 of 25 passing. It comparesconformsrather than violation counts, because the read-back relabels blank nodes and can shift a count without changing the verdict.--pgrdf: SHACL-SPARQL fixtures throughmode => 'pgrdf', graded against the W3C expected verdicts.--sparql: checks thatmode => 'sparql'keeps its documented contract.
SHACL capability document
tests/shacl-capability/run.sh answers "which SHACL components does validate actually enforce on this build?" and writes the answer to CAPABILITY.json. Each probe has a shapes file, a violating data file and a control data file, loaded into separate graphs. A component counts as enforced only if the violating data fails and the control passes.
Contract suite
tests/lib-tdd/ holds shell cases for the engine's client-facing contracts: the SQLSTATE of each kind of refusal, digest behaviour on absent and empty graphs, digest collision and separation pairs, per-call statistics, graph_inventory(), surface() coverage against the manifest, the lock refusal's cure, export_graph round trips, graph_manifest recomputation, and the identity triple. Each case exits in one of three states: GREEN (behaviour met), RED (fails exactly as predicted for an unfinished feature) or BROKEN (fails for any other reason). The runner fails only on BROKEN. Cases create only urn:tdd:* graphs and drop them afterwards.
tests/lib-tdd/run.sh # all cases against the compose server
tests/lib-tdd/run.sh 01-e0-lock-sqlstate # one case
PGRDF_TDD_PSQL="psql …" tests/lib-tdd/run.shLUBM
- LUBM-shape (
tests/perf/lubm-shape/): three hand-authored queries (class membership, professor-of, takes-course) over a small curated dataset. This is a correctness check, not a benchmark. A SHACL-SPARQL variant lives intests/perf/lubm-shacl-sparql/. - LUBM performance (
tests/perf/lubm/): a containerised UBA generator (just lubm-build,just lubm-gen 10) produces LUBM-N data into a Docker volume.just test-lubm-10(ortest-lubm-1) runs load, reasoning and queries and fails if correctness fields drift from the baseline or timings leave their tolerance.just benchmarkappends each run to a local history and renders an HTML report.
Real-world ontology parsing
fixtures/ontologies.sh fetches a set of public ontologies (FOAF, PROV, SKOS, RDFS, OWL and others) into a git-ignored directory. tests/perf/smoke-ontologies.sh --check loads each one and compares triple counts with tests/perf/smoke-ontologies.expected.tsv, as a tripwire for Turtle parser regressions.
CI
ci.yml runs on pull requests and on pushes to main. Changes that touch only documentation (Markdown, docs/, guide/, the licence) skip it. All jobs target PostgreSQL 18.
| Job | Runs |
|---|---|
fmt | cargo fmt --all -- --check |
clippy | cargo clippy --no-default-features --features pg18 -- -D warnings |
test | cargo pgrx test pg18 |
regression | builds the .so in the builder image, boots the compose server (its CREATE EXTENSION is the first version gate), then runs artifact parity, the SQL regression suite, the dictionary pg_dump round trip, the W3C-shape SPARQL harness with the oracle, the SHACL Core gate, the --sparql and --pgrdf SHACL-SPARQL runs, and the LUBM-shape checks |
Draft pull requests run only fmt and clippy. test and regression run when a pull request is marked ready for review and on every push to main. CI build caches are used here only; release builds are compiled from scratch.
perf-nightly.yml runs the LUBM-10 gate every night. The release-time gates (version coherence, boot on both architectures, attestation) are described on Release.
Discipline
- Every bug fix comes with a regression test that reproduces the failure.
- Every new SQL function gets a
#[pg_test]and asurface_manifest.tsvrow. - Expected outputs are computed by hand, not captured from the code under test.
Not covered
- The full W3C SPARQL 1.1 test manifest. The SPARQL harness is hand-authored W3C-shape tests, cross-checked by the oracle.
- The full W3C SHACL suite beyond the vendored Core subset and the SHACL-SPARQL fixtures.
- Replication and streaming scenarios.