Skip to content

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

LayerLocationLocal commandIn 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-nativetest job
SQL regression (golden output)tests/regression/just test-regressionregression job
pg_dump round tripstests/regression/scripts/pg-dump-roundtrip.sh, pg-dump-dict-roundtrip.shjust test-pg-dump-roundtripdictionary round trip
Artifact paritytests/regression/scripts/verify-installed-artifacts.shjust test-artifact-parityregression job
W3C-shape SPARQL harness with differential oracletests/w3c-sparql/, tests/oracle/just test-w3cregression job
W3C SHACL Core gate and SHACL-SPARQL fixturestests/w3c-shacl/just test-shacl-manifest (--pgrdf, --sparql)regression job
LUBM-shape correctnesstests/perf/lubm-shape/, tests/perf/lubm-shacl-sparql/just test-lubmregression job
SHACL capability documenttests/shacl-capability/tests/shacl-capability/run.shmanual
Contract suitetests/lib-tdd/tests/lib-tdd/run.shmanual
Real-world ontology parsingtests/perf/smoke-ontologies.shtests/perf/smoke-ontologies.sh --checkmanual
LUBM performancetests/perf/just test-lubm-10, just benchmarknightly

Aggregate recipes:

sh
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-conformance

Run 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.

sh
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 output

Write 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.sh seeds graph IRI bindings, dumps the database with pg_dump, drops the extension, restores, and checks the bindings survived. pg-dump-dict-roundtrip.sh does the same for dictionary contents.
  • verify-installed-artifacts.sh builds the current tree into a temporary directory, byte-compares it with compose/extensions/, checks that the running container mounts this repository's files and that their hashes match, and after CREATE EXTENSION checks that extversion and pgrdf.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 compares conforms rather than violation counts, because the read-back relabels blank nodes and can shift a count without changing the verdict.
  • --pgrdf: SHACL-SPARQL fixtures through mode => 'pgrdf', graded against the W3C expected verdicts.
  • --sparql: checks that mode => '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.

sh
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.sh

LUBM

  • 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 in tests/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 (or test-lubm-1) runs load, reasoning and queries and fails if correctness fields drift from the baseline or timings leave their tolerance. just benchmark appends 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.

JobRuns
fmtcargo fmt --all -- --check
clippycargo clippy --no-default-features --features pg18 -- -D warnings
testcargo pgrx test pg18
regressionbuilds 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 a surface_manifest.tsv row.
  • 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.

pgRDF is released under the MIT license. Documentation built with VitePress, served via GitHub Pages.