Last updated: 2026-05-24. Status: Tier 1 standards adopted; Tier 2/3 documented as next steps.
We treat testing as a pyramid weighted toward unit/property tests in the Rust core and integration tests at the Elixir↔Rust HTTP boundary.
| Layer | Tooling | Where it lives | Gate |
|---|---|---|---|
| Unit (Rust) | cargo test + #[cfg(test)] mod tests |
rust-core/*/src/**/*.rs |
rust-ci/test |
| Integration (Rust) | cargo test --test <name> |
rust-core/*/tests/*.rs |
rust-ci/test |
| Property (Rust) | proptest |
rust-core/*/tests/property_tests.rs |
rust-ci/test |
| Fuzz (Rust) | cargo-fuzz + libFuzzer |
fuzz/ and rust-core/fuzz/ |
rust-ci/fuzz-compile + ClusterFuzzLite |
| Bench (Rust) | criterion |
benches/modality_benchmarks.rs |
rust-ci/bench-compile |
| Doctest (Rust) | cargo test --doc |
/// in any lib.rs |
rust-ci/test |
| Unit (Elixir) | ExUnit |
elixir-orchestration/test/**/*_test.exs |
elixir-ci/build-test |
| Property (Elixir) | stream_data via use ExUnitProperties |
test/verisim/property/*_props_test.exs |
elixir-ci/build-test |
| Bench (Elixir) | benchee |
bench/*_bench.exs, JSON output |
elixir-ci/bench-compile |
| Coverage (Rust) | cargo-llvm-cov → Codecov |
CI ephemeral | rust-ci/coverage (≥60% gate) |
| Coverage (Elixir) | excoveralls → Codecov |
CI ephemeral | elixir-ci/coverage (≥60% gate) |
| Lint | clippy --all-targets -D warnings, mix compile --warnings-as-errors, mix format --check-formatted, cargo fmt --all -- --check |
every commit | rust-ci + elixir-ci |
| Supply-chain | cargo audit, cargo deny, mix hex.audit, Dependabot |
every PR | rust-ci/audit + rust-ci/deny + elixir-ci/audit |
The coverage floor is set at 60% to start, with the intention of ratcheting upward as the test suite matures. The industry baseline is 80% line coverage (per Codecov's published guidance); we'll move there once the bigger gap modules (vcl_executor) have richer coverage.
Every push and PR must pass all of:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --no-deps -- -D warnings
cargo test --workspace --no-fail-fast
cargo doc --workspace --no-deps # with RUSTDOCFLAGS="-D warnings"
cargo bench --no-run # compile-only
cargo audit
cargo deny check # advisories, bans, licenses, sources
cargo llvm-cov --workspace --fail-under-lines 60Plus a matrix cargo check --manifest-path fuzz/Cargo.toml and
rust-core/fuzz/Cargo.toml so fuzz harnesses stay green.
Where to use it:
- Pure functions with mathematical invariants (haversine symmetry, hash-chain integrity, set algebra).
- Round-trips:
decode(encode(x)) == x,index(x); get(id) == x. - Monotonicity & ordering:
nearest(k)returns ascending distances.
Patterns adopted:
- Custom
Strategyover canonical input domains (-90..=90for latitude, etc.) prop_filter_mapto discard out-of-domain inputs cleanlytokio::runtime::Runtimeper property to keep async tests deterministic
See rust-core/verisim-spatial/tests/property_tests.rs (8 properties)
and rust-core/verisim-provenance/tests/property_tests.rs (6 properties)
for the established pattern.
All 8 modalities (graph, vector, tensor, semantic, document, temporal,
spatial, provenance) have dedicated benchmark groups in
benches/modality_benchmarks.rs. Each group covers at least:
- A throughput measurement (insertions or upserts)
- A query latency at a representative dataset size (~1000 records)
cargo bench --no-run is the CI gate; full execution is left to
developers running locally or to a periodic bench job.
mix format --check-formatted
mix compile --warnings-as-errors
mix test
mix coveralls.json # uploaded to Codecov, ≥60% line-coverage floor
mix hex.audit
mix deps.unlock --check-unusedPlus a syntax check that every bench/*.exs script parses with
Code.string_to_quoted!/1 so misformed benchmark scripts don't sneak
through.
Where to use it:
- Parser invariants: every UTF-8 input yields
{:ok, _} | {:error, _}(no panics) - Source-id round-trips through
FROM HEXAD <uuid>clauses - Constraint validation symmetries (required: missing iff rejected; range: in-bounds iff accepted)
- Pure-function determinism (validate(x) called twice yields the same result)
Patterns adopted:
- One
*_props_test.exsfile per module undertest/verisim/property/ - Module-level
@moduledocenumerates each invariant tested - Generators favour
one_of/mapoverany/0so failures shrink to meaningful minima prop_assume!guards to skip irrelevant inputs without false-failures
Established examples:
test/verisim/property/schema_registry_props_test.exs(7 properties)test/verisim/property/vcl_bridge_props_test.exs(6 properties)
Per-module scripts under elixir-orchestration/bench/:
drift_monitor_bench.exs— report/status/sweep round-trip latencyquery_router_bench.exs— per-modality dispatch overheadvql_executor_bench.exs— explain-plan generation, statement dispatchvql_bridge_bench.exs— built-in parser throughput (every query shape)schema_registry_bench.exs— register/get/validate/type_hierarchy
Each script writes JSON to bench/results/<name>.json. The intended
workflow is to run benchmarks on a stable branch, commit the JSON as a
baseline, and diff successive runs in CI when perf regressions matter
for a given PR. (No mandatory perf gate yet — manual review of the
diff is the current discipline.)
Common config:
Benchee.run(jobs,
warmup: 2,
time: 5,
memory_time: 1,
reduction_time: 1,
print: [fast_warning: false],
formatters: [
Benchee.Formatters.Console,
{Benchee.Formatters.JSON, file: "bench/results/<module>.json"}
]
)- Rust: every public function should have at least one
///example that compiles undercargo test --doc. Not yet a hard gate; will be promoted oncecargo docwarnings are zero. - Elixir: add
doctest MyModuleto test files for any module whose@doc/@moduledoccontainsiex>examples.
Both fuzz/fuzz_targets/fuzz_octad_id.rs and
rust-core/fuzz/fuzz_targets/fuzz_vql_parser.rs accept arbitrary UTF-8
input. Seed corpora live under fuzz/corpus/<target>/ and
rust-core/fuzz/corpus/<target>/ once we accumulate interesting
inputs. The build script in .clusterfuzzlite/build.sh has commented-out
hooks to wire those into ClusterFuzzLite.
cargo audit + cargo deny check advisories bans licenses sources on
every PR. deny.toml keeps a small ignore list for transitive
unmaintained crates that live inside the burn ML stack and can only be
addressed by upstream releases; each entry is documented inline with
the upstream chain.
mix hex.audit and mix deps.unlock --check-unused on every Elixir PR.
Where to use it:
- Public structured outputs whose shape is part of a contract — explain plans, AST dumps, drift report JSON, normaliser plans.
Patterns adopted:
rust-core/verisim-planner/tests/snapshot_tests.rssnapshots the fullExplainOutputfor three representativeLogicalPlanfixtures (single-modality document, two-modality graph+vector, semantic-proof trailing). YAML format chosen so diffs read naturally.- Workflow when a snapshot fails:
cargo insta review— inspect the diff interactively- Accept legitimate changes or fix the regression
- Commit the updated
tests/snapshots/*.snapfile
- In CI snapshots are read-only (no
INSTA_UPDATE): any drift fails the build.
Snapshots live under <crate>/tests/snapshots/*.snap and are
human-readable YAML. Review them with git log -p like any other
source file.
Configuration in .cargo/mutants.toml:
exclude_globsskips generated protobuf, bench harnesses, fuzz harnesses,main.rsbinaries, and build scripts (mutating them produces uninteresting survivors).timeout_multiplier = 3.0gives slower spatial/temporal tests room.
Recommended invocations:
- Local development:
cargo mutants -p verisim-driftto focus on one crate. - PR triage:
cargo mutants --in-diff origin/mainonly mutates lines changed against main — keeps runtime under ~5 min for typical changesets. - Nightly/weekly:
cargo mutants -p <core invariant crates>full run for a mutation-score baseline.
CI integration: rust-ci.yml has a mutants job gated on
workflow_dispatch and schedule events (full runs take >20 min;
not suitable for every PR). Report uploaded as
mutants-report artifact.
Mutation score targets:
- Priority crates (drift, normalizer, spatial, provenance, semantic, octad): ≥80% on core invariants
- Best-effort crates (api, repl, graph): no enforced floor
These are documented for future work; no current CI gate enforces them.
- Snapshot testing (Elixir):
mnemefor built-in VCL parser output. Skipped initially becausemneme's interactive accept/reject doesn't fit batch CI; we'd need to wireMNEME_REPLY=acceptworkflow. - Differential testing: SQLancer-style queries through redb vs Oxigraph backends and naive linear vs HNSW vector search; assert result-set equivalence to catch optimiser bugs.
- Concurrency testing:
loom+shuttleforverisim-octadWAL transactions;concuerrorfor Elixir supervision tree. - Bench-as-PR-gate:
codspeed-criterion-compatorbencher.devfor noise-resistant perf regression detection. - AFL++ alongside libFuzzer: structure-aware fuzzing of the VCL
grammar via
arbitraryderive on AST types, corpus sync across cargo-fuzz / AFL++ / honggfuzz.