Verification & Testing

How the engine is verified — unit tests, differential parity, the W3C gates, benchmarking, and debugging the query pipeline.

The verification pyramid

      W3C differential gates        test/w3c/ (vs Comunica + spec results)
    Differential parity             test/parity/ (vs Comunica, 1:1)
  Unit / integration tests          src/**/*.test.ts
Typecheck + lint + fmt + parser     deno task check / lint / fmt:check / parser:check

Every layer runs in CI (.github/workflows/ci.yml): the ci job runs deno task ci (plus explicit test:exists-ref and publish:dry steps), w3c-parity runs deno task test:w3c + deno task test:exists-ref, latency-snapshot re-checks the committed bench inventory, docs-drift enforces the wiki’s detail_level guardrail (L<line> citations fail), docs-links fails on wiki link rot, and publish.yml gates deno publish.

Task reference

Task Command What it runs Gating
deno task test deno test --allow-all All unit + integration + parity *.test.ts files ci
deno task check deno check Typecheck the package ci
deno task fmt:check deno fmt --check Source and markdown formatting (width 80) ci
deno task lint deno lint Lint ci
deno task parser:check generate-parser.ts --check Generated parser.ts in sync with sparql.jison ci
deno task bench:check bench/budget.ts Perf regression budget (vs bench/baseline.json) ci
deno task test:exists-ref test/w3c/exists-ref.ts EXISTS/NOT EXISTS subquery surface vs Oxigraph ci + w3c-parity
deno task test:sparql12:gap test/w3c/sparql12-gap.ts RDF 1.2 eval-triple-terms gap suite ci
deno task test:sparql12 test/w3c/sparql12-main.ts W3C SPARQL 1.2 evaluation suite ci
deno task test:rdf11 test/w3c/rdf-differential.ts RDF 1.1 Turtle/TriG/N-Triples/N-Quads vs n3 ci
deno task test:rdf12 test/w3c/rdf-classify.ts RDF 1.2 syntax classifier ci
deno task publish:check test/publish-graph-check.ts Default export graph has no node:/npm: runtime imports ci
deno task ci — All of the above in dependency order ci job
deno task test:w3c test/w3c/w3c-main.ts SPARQL 1.1 evaluation-core differential vs Comunica w3c-parity job
deno task docs:link-check docs/link-check.ts Wiki link rot: every external markdown link (JSR/GitHub/W3C/…) must resolve (404/410 fails) docs-links job
deno task bench:latency:check bench/latency-check.ts Committed bench/latency-data.json bench inventory matches a fresh run latency-snapshot job
deno task test:ref test/w3c/ref-crosscheck.ts Allowlisted divergence audit vs Oxigraph + N3.js manual (on grammar change)
deno task bench deno bench --allow-all Three-engine benchmarks manual
deno task bench:latency bench/latency-chart.ts Render  
bench/latency-data.json → docs/assets/chart-latency.svg manual    
deno task publish:dry deno publish --dry-run JSR publish validation ci
job + publish job      

Unit & integration tests

deno test discovers *.test.ts alongside sources and under test/:

deno test src/              # engine internals only (fast loop)
deno test src/wazoo-sparql-engine.test.ts   # single file
deno test -n "exists"       # filter by test name substring

Covered areas: parser (src/parser/mod.test.ts, turtle-parser.test.ts), store semantics (src/store/memory-store.test.ts), quad-store adapters (src/quad-store.test.ts), the join engine (src/evaluator/join.test.ts), the planner (src/planner/*.test.ts), term algebra (src/term/term.test.ts), serializers (src/serialize/*.test.ts), updates (src/evaluator/update-evaluator.test.ts), and the engine integration suite (src/wazoo-sparql-engine.test.ts) — including the concurrent-execute() isolation tests for the EXISTS snapshot (issue #72).

Differential parity (the project’s core test)

The parity contract is behavioral equivalence with @comunica/query-sparql-rdfjs-lite, the engine this project ports 1:1. See test/parity/parity.test.ts and parity-update.test.ts:

W3C suites

deno task test:w3c runs the vendored W3C SPARQL 1.1 evaluation-core (aggregates, bind, bindings, cast, construct, exists, functions, grouping, negation, project-expression, property-path, subquery, and the update categories) differentially: every query runs through both Comunica and the wazoo engine, and observable results are compared. Categories per test:

The SPARQL 1.2 gates are similar but larger-surface: test:sparql12 (differential vs Comunica, including the four direction functions and RDF 1.2 triple terms) and test:sparql12:gap (the RDF 1.2 eval-triple-terms gap suite, where Comunica’s parser lags).

The RDF syntax gates validate the in-repo Turtle/TriG/N-Triples/N-Quads parser backing LOAD:

All fixtures are vendored under test/w3c/fixtures/ (no network needed); refresh instructions live in test/w3c/README.md.

Benchmarking

Each tool’s methodology is on the dedicated 07 — Benchmarking & Performance page (the measured numbers live in the root README.md); this section covers how to run each tool and what it gates.

deno task bench — three-engine comparison

bench/engine_bench.ts compares the wazoo engine against @comunica/query-sparql-rdfjs-lite and Oxigraph (WASM) over an identical generated graph (400 people, ~1,600 quads, plus named-graph datasets).

Two properties make the timings trustworthy:

  1. Verification first. Before any timing, every query asserts all three engines return identical results (verifySelectEquality, verifyAskEquality, verifyConstructEquality, verifyConstructIsoEquality — CONSTRUCT under the graph-result multiset contract: reference deduplicated, wazoo as-emitted (issue #87) — and every update asserts identical final store contents on fresh stores (verifyUpdateEquality). A benchmark of a broken engine fails loudly, not silently.
  2. Self-restoring updates. The timed update deletes and re-inserts the same quads, netting to zero per iteration, so the benchmark stores never drift.

Groups cover the feature surface: scan, join, asym-join (reorder on/off), reorder-chain, ask, construct, update, optional, minus, union, path, group-aggregate, filter-expr, order-limit, distinct, values-bind, graph, from, subquery, exists, cast, string-fn, having, reduced, update-ops. The reorder-chain group demonstrates the dynamic join planner (see 07 — Benchmarking & Performance): reordering makes the worst-case-ordered chain fast, at parity with Oxigraph.

deno task bench:check — regression budget

bench/budget.ts runs two queries (a 2-pattern BGP join and a reorder chain) 50× against a 100-subject store and fails if average latency exceeds maxAllowedMs: 50 from bench/baseline.json. This is the CI perf gate.

bench/concurrency-probe.ts — EXISTS concurrency stress

Standalone probe for issue #72 (a concurrent execute() must never observe another call’s EXISTS snapshot rebuild). Five queries cover the EXISTS surface — flat, NOT EXISTS, nested, ORDER BY, and GROUP BY + OPTIONAL — over a 300-subject store:

  1. Static-store concurrency — 40 rounds of the shuffled query mix run via Promise.all, asserting every round matches the sequential baseline byte-for-byte.
  2. Update interleaving — 20 rounds of a self-restoring DELETE/INSERT UPDATE running concurrently with EXISTS queries, asserting no call errors (exact results are undefined mid-mutation).

Any error or divergence exits 1:

deno run --allow-all bench/concurrency-probe.ts

The same guarantees are locked in as unit tests in src/wazoo-sparql-engine.test.ts.

deno task bench:size / bench:size:closures / bench:memory — footprint measurement

Latency is only half the story: the engine is also benchmarked on what it costs to ship and run (root README.md → Size & memory footprint):

Bar chart of engine footprints on disk
Fig — Library size on disk. One bar per engine; bar length is proportional to total installed size (values in binary MiB, share of the combined total in parentheses). Wazoo (green) is a sliver against Comunica’s transitive closure (orange); oxigraph (blue) sits between.
Treemap of peak heap per engine and workload
Fig — Peak heap during execution (10k-person graph). One panel per workload (full scan, nested EXISTS); within each, the three engines’ tiles are scaled by their peak `heapUsed` (values in MiB). Wazoo (green) is the smallest tile in both panels.

These are machine-specific snapshots, not guarantees — regenerate with the three tasks above and commit the updated SVGs.

Debugging guide

1. Inspect the parsed AST (the “query plan”)

The engine has no separate algebra IR — the sparqljs AST is the closest thing to a query plan. Dump it:

deno eval '
import { SparqlParser } from "./src/parser/sparql-parser.ts";
const ast = new SparqlParser().parse(
  "SELECT ?city (COUNT(*) AS ?cnt) WHERE { ?s <http://example.org/city> ?city } GROUP BY ?city HAVING (COUNT(*) > 1) ORDER BY ?city",
);
console.log(JSON.stringify(ast, null, 2));
'

You can see the algebra operators the evaluator will run: group + having + order + projection expressions map 1:1 onto applySelectPipeline() stages (see 02 — Architecture).

2. Inspect what the store sees

The engine evaluates over any rdfjs.Store. Dump the store the evaluator uses to check scoping:

deno eval '
import { DataFactory, MemoryStore } from "./src/mod.ts";
const { namedNode, literal, quad } = DataFactory;
const store = new MemoryStore();
store.addQuad(quad(namedNode("https://e/s"), namedNode("https://e/p"),
  literal("o"), namedNode("https://e/g")));
console.log(store.getQuads(null, null, null, null));
'

GraphScopedStore (src/quad-store.ts) is a view that fixes the graph term — to trace GRAPH ?g scoping, check which graph term the store view carries.

3. Trace a single pipeline stage

4. Run one case in isolation

deno test src/evaluator/update-evaluator.test.ts
deno test -n "nested" src/wazoo-sparql-engine.test.ts
deno task test:w3c   # then read the printed per-test report for gap details

The W3C runner prints one line per test with its category (pass/gap/error/divergence/conformance) — grep for gap to list the open parity defects.

5. Watch out