Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .cargo/mutants.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# SPDX-License-Identifier: MPL-2.0
#
# Configuration for cargo-mutants — mutation testing.
#
# Usage:
# cargo mutants # run on the whole workspace
# cargo mutants --in-diff <ref> # only mutate code changed vs <ref>
# cargo mutants -p verisim-drift # focus on a single crate
# cargo mutants --shard 0/4 # parallel sharding
#
# What mutation testing does: it inserts small bug-like changes into your
# code (e.g. `>` → `>=`, `+` → `-`, `true` → `false`) and runs the test
# suite. A mutant that survives means your tests don't distinguish the
# buggy version from the original — you have a coverage gap that line/branch
# metrics can't see.
#
# Target mutation score: 75-85% on core invariants (drift, normaliser,
# octad). HTTP glue and trace setup are exempt because mutating them
# produces uninteresting survivors.

# ---------------------------------------------------------------------------
# Scope
# ---------------------------------------------------------------------------

# Skip generated code, vendored protos, and crates whose value lies in
# their I/O surface rather than their logic.
exclude_globs = [
# Generated protobuf code — mutating it tests nothing real
"rust-core/verisim-api/src/proto/**",
# Repl/binary entry points — mutations here exercise rustyline/atty
# rather than VeriSimDB logic
"rust-core/verisim-repl/src/main.rs",
"rust-core/verisim-api/src/main.rs",
# Fuzz harnesses — mutations would just make them not fuzz
"fuzz/**",
"rust-core/fuzz/**",
# Benchmarks — mutations break bench harness, not project logic
"benches/**",
# Build scripts
"**/build.rs",
]

# Match cargo-mutants' default mutator set. If a specific mutator produces
# too much noise (e.g. constant-replacement on cost-model coefficients that
# are themselves heuristic), pin operators here:
# operators = ["binop", "logop", "bool_lit", "return"]

# ---------------------------------------------------------------------------
# Test execution
# ---------------------------------------------------------------------------

# Use cargo nextest if available — significantly faster than cargo test for
# large mutation runs. Falls back to cargo test automatically.
# test_tool = "nextest"

# Per-mutant timeout multiplier — generous to avoid false-positive timeouts
# on the slower spatial/temporal tests.
timeout_multiplier = 3.0

# Run the *baseline* (no mutation) once before applying mutants; if it fails
# the run aborts early with a clear "your tests are broken before any mutation"
# message rather than blaming the mutator.
# (cargo-mutants does this by default; documented here for clarity.)

# ---------------------------------------------------------------------------
# Per-crate strategy
# ---------------------------------------------------------------------------
#
# Mutation testing is most valuable where invariants are encoded in pure
# functions: drift calculation, hash-chain integrity, constraint
# evaluation, k-nearest sorting. Less valuable for HTTP routing, error
# message strings, and IPC glue.
#
# Priority crates (mutation score target ≥80%):
# - verisim-drift (drift score arithmetic, threshold policies)
# - verisim-normalizer (regeneration strategies, conflict resolution)
# - verisim-spatial (haversine distance, R-tree search)
# - verisim-provenance (hash-chain verification)
# - verisim-semantic (constraint validation, ZKP primitives)
# - verisim-octad (transaction state machine, WAL replay)
#
# Best-effort crates (no enforced floor):
# - verisim-api (HTTP handlers — mutations land in plumbing)
# - verisim-repl (CLI surface — see exclusions above)
# - verisim-graph (in-memory dispatcher, thin layer)
#
# For PR CI, run `cargo mutants --in-diff origin/main` to only mutate
# changed lines. This keeps per-PR runtime under ~5 minutes for typical
# changesets.
33 changes: 33 additions & 0 deletions .github/workflows/rust-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,39 @@ jobs:
- name: Enforce minimum coverage
run: cargo llvm-cov --workspace --summary-only --fail-under-lines 60

mutants:
# Mutation testing — runs only on workflow_dispatch + scheduled because
# full-workspace mutants can take >20 min. For PRs the recommended
# invocation is `cargo mutants --in-diff origin/main` which we can wire
# into a separate pr-mutants.yml once the suite stabilises.
name: cargo-mutants (on-demand)
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule'
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: cargo-mutants
- name: Run mutation tests on core invariant crates
run: |
cargo mutants \
-p verisim-drift \
-p verisim-normalizer \
-p verisim-spatial \
-p verisim-provenance \
-p verisim-semantic \
-p verisim-octad \
--no-shuffle \
--output mutants-out/
continue-on-error: true
- uses: actions/upload-artifact@v4
if: always()
with:
name: mutants-report
path: mutants-out/

fuzz-compile:
name: fuzz targets compile
runs-on: ubuntu-latest
Expand Down
37 changes: 37 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ axum-server = { version = "0.8", default-features = false, features = ["tls-rust
# Testing
proptest = "1.11"
criterion = "0.8"
insta = { version = "1.40", features = ["yaml", "json"] }

# Async
futures = "0.3"
Expand Down
59 changes: 54 additions & 5 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,64 @@ the upstream chain.

`mix hex.audit` and `mix deps.unlock --check-unused` on every Elixir PR.

## Adopted Tier 2 standards

### Snapshot testing — `insta` (Rust)

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.rs` snapshots the
full `ExplainOutput` for three representative `LogicalPlan` fixtures
(single-modality document, two-modality graph+vector, semantic-proof
trailing). YAML format chosen so diffs read naturally.
- Workflow when a snapshot fails:
1. `cargo insta review` — inspect the diff interactively
2. Accept legitimate changes or fix the regression
3. Commit the updated `tests/snapshots/*.snap` file
- 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.

### Mutation testing — `cargo-mutants` (Rust, opt-in)

Configuration in `.cargo/mutants.toml`:
- `exclude_globs` skips generated protobuf, bench harnesses, fuzz
harnesses, `main.rs` binaries, and build scripts (mutating them
produces uninteresting survivors).
- `timeout_multiplier = 3.0` gives slower spatial/temporal tests room.

Recommended invocations:
- Local development: `cargo mutants -p verisim-drift` to focus on one
crate.
- PR triage: `cargo mutants --in-diff origin/main` only 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

## Next-tier standards (not yet adopted)

These are documented for future work; no current CI gate enforces them.

- **Mutation testing**: `cargo-mutants --in-diff` for changed files in
`verisim-drift`, `verisim-normalizer`, `verisim-octad`. Target 75-85%
mutation score on core invariants.
- **Snapshot testing**: `insta` (Rust) for VQL parser AST, explain plans,
drift reports; `mneme` (Elixir) for built-in parser output.
- **Snapshot testing (Elixir)**: `mneme` for built-in VQL parser output.
Skipped initially because `mneme`'s interactive accept/reject doesn't
fit batch CI; we'd need to wire `MNEME_REPLY=accept` workflow.
- **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.
Expand Down
1 change: 1 addition & 0 deletions rust-core/verisim-planner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ tracing.workspace = true
[dev-dependencies]
proptest.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "time"] }
insta.workspace = true
Loading
Loading