8000 test: golden-file compatibility corpus for format v7 (#391) by adityamukho · Pull Request #455 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

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
23 changes: 23 additions & 0 deletions .github/workflows/policy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ name: Policy
# - msrv: the crate builds and its unit tests pass on `rust-version`
# - semver-checks: no accidental public-API break against the latest release tag
# - cargo-deny: dependency licenses, duplicate versions and sources (deny.toml)
# - golden-corpus: golden files in tests/golden/ are only ever added (#391)

on:
push:
Expand Down Expand Up @@ -88,3 +89,25 @@ jobs:
with:
# Advisories are covered by cargo-audit.yml and osv-scanner.yml.
command: check bans licenses sources

golden-corpus:
name: Golden files are add-only
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Refuse modified, deleted or renamed golden files
env:
BASE_REF: ${{ github.base_ref }}
run: |
changed=$(git diff --name-only --diff-filter=MDRT "origin/${BASE_REF}...HEAD" -- \
'tests/golden/*.graph' 'tests/golden/*.wal')
if [ -n "$changed" ]; then
echo "::error::Golden files are never regenerated, only added (tests/golden/README.md):"
echo "$changed"
exit 1
fi
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ minigraf-ffi/python/minigraf/minigraf_ffi/
minigraf-ffi/python/**/__pycache__/
minigraf-ffi/python/.venv/
minigraf-ffi/python/dist/

# golden-file generator build output (tests/golden/gen/*)
tests/golden/gen/*/target/
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Tests

- **Golden-file compatibility corpus (#391).** `tests/golden/` holds v7 files written by minigraf 2.0.3, each with a JSON manifest of its CRC32, `tx_count` and expected query results. They cover one checkpoint, several checkpoints, indexes rebuilt on open, a WAL left by a crash, a WAL whose entries were already checkpointed, and same-transaction multi-values (#371). `tests/golden_corpus_test.rs` opens a copy of each file and checks its manifest after open, after a checkpoint and reopen, and after one more write. Golden files are never regenerated, only added; a new `golden-corpus` job in `policy.yml` fails a pull request that changes one. The generator is a standalone crate pinned to `minigraf = "=2.0.3"` (`tests/golden/gen/v7/`).

## v2.0.3 — 2026-10-06

Patch release on the v2.x line with two data-integrity fixes, under the [support policy](PHILOSOPHY.md#support-policy). File format is unchanged (v7). No API changes. Upgrading is recommended for every v2.x user.
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ Auto-migrates v1/v2/v3/v4/v5/v6 → v7 on open/checkpoint.

## Test Coverage

**1219 tests** (1211 passing, 8 ignored; unit + integration + doc).
**1221 tests** (1213 passing, 8 ignored; unit + integration + doc).
See `docs/TEST_COVERAGE.md` for the full per-file breakdown.

**Testing conventions** — see the Testing Conventions section below before writing any tests.
Expand Down
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ Bugs that can lose, corrupt or hide data are handled so that users can always se

At release time, the maintainer updates the pinned issue and closes the issues whose fixes are in the release.

## Golden Files

`tests/golden/` holds database files written by released versions, each with a manifest of the results it must give (#391). See [`tests/golden/README.md`](tests/golden/README.md).

- Golden files are **never regenerated, only added**. CI fails a pull request that modifies, deletes or renames one.
- Every release that changes on-disk behavior (a new format version, a new page or WAL shape, a migration) adds golden files for it, with the recipe in a generator crate under `tests/golden/gen/`.

## Philosophy Check

Before submitting, ask yourself:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
members = ["."]
exclude = ["fuzz"]
exclude = ["fuzz", "tests/golden/gen/v7"]
resolver = "2"

[package]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ let r2 = pq.execute(&[("tx", BindValue::TxCount(2)), ("entity", BindValue::Entit

```bash
cargo run # interactive Datalog REPL
cargo test # run 1219 tests
cargo test # run 1221 tests
cargo run < demos/demo_recursive.txt # recursive rules demo
```

Expand Down
6 changes: 3 additions & 3 deletions docs/TEST_COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@

**Verified**: 2026-10-06 with `cargo test`

**Result**: 1,211 passing tests and 8 ignored tests (1,219 total)
**Result**: 1,213 passing tests and 8 ignored tests (1,221 total), including the golden-file corpus added after v2.0.3

The v2.0.3 suite covers the public database API, the Datalog engine, storage and recovery paths, and the v2.0.0 locking and structured-error-code changes, plus v2.0.1's regression test for rebuilding indexes on open after several checkpoints (#370). Since v2.0.1, `tests/point_query_history_test.rs` and new storage unit tests check that attribute-narrowed bound-entity lookups return exactly what a full scan returns, and that `net_asserted_facts` matches its previous implementation on randomized histories (#323). Attribute-scan tests check that the AEVT range covers exactly the queried attribute, including non-ASCII names such as `:丿` and prefix siblings such as `:ab` for `:a` (#381). Incremental B+tree rebuild tests check that checkpoints which copy untouched index leaves produce exactly the entries and file checksum a full rebuild would, including over repeated checkpoints, that single-fact checkpoints do not fragment the index, and that a cyclic leaf chain or damaged slot directory is rejected rather than copied (#315). Directory-sync unit tests check that creating the `.graph` file or the WAL, and deleting the WAL, fsync the parent directory after the file operation, and that reopening an existing file does not (#389). Disjunction tests check that `or-join` returns an empty result, not INT-031, when no rows reach it: on an empty database, after a pattern that matches nothing, after a `not` that removes every row, and in a rule body (#405). A stratification unit test builds the same two-rule negative cycle 64 times and checks that the INT-054 message names the same predicate pair every time (#410). Magic-sets tests check that a recursive rule with a literal start that recurses through another rule returns the same results as without the literal (#297). API tests check that `execute()` and `WriteTransaction::execute()` reject `$slot` bind tokens in every position with API-010 (#407). The REPL `run()` test now runs in a child process with null stdin, so it no longer hangs when the test runner's stdin is an open non-TTY stream (#415). A fault-injection test crashes `save()` at each of its 47 page writes and syncs and checks that every reopen finds exactly the old or the new checkpointed facts (#374). For v2.0.3, storage unit tests and two `edge_cases_test.rs` tests check that more than 65,535 uncheckpointed facts each resolve to their own fact through entity lookups and attribute scans, in memory and file-backed, before and after a checkpoint, and that the widened `FactRef` encodes to the same bytes as before (#445). A WAL test reopens a database next to a WAL whose entries are all checkpointed, writes again, and checks that `:as-of` history is unchanged before and after another reopen; a unit test checks that restoring the transaction counter never lowers it (#447).
The v2.0.3 suite covers the public database API, the Datalog engine, storage and recovery paths, and the v2.0.0 locking and structured-error-code changes, plus v2.0.1's regression test for rebuilding indexes on open after several checkpoints (#370). Since v2.0.1, `tests/point_query_history_test.rs` and new storage unit tests check that attribute-narrowed bound-entity lookups return exactly what a full scan returns, and that `net_asserted_facts` matches its previous implementation on randomized histories (#323). Attribute-scan tests check that the AEVT range covers exactly the queried attribute, including non-ASCII names such as `:丿` and prefix siblings such as `:ab` for `:a` (#381). Incremental B+tree rebuild tests check that checkpoints which copy untouched index leaves produce exactly the entries and file checksum a full rebuild would, including over repeated checkpoints, that single-fact checkpoints do not fragment the index, and that a cyclic leaf chain or damaged slot directory is rejected rather than copied (#315). Directory-sync unit tests check that creating the `.graph` file or the WAL, and deleting the WAL, fsync the parent directory after the file operation, and that reopening an existing file does not (#389). Disjunction tests check that `or-join` returns an empty result, not INT-031, when no rows reach it: on an empty database, after a pattern that matches nothing, after a `not` that removes every row, and in a rule body (#405). A stratification unit test builds the same two-rule negative cycle 64 times and checks that the INT-054 message names the same predicate pair every time (#410). Magic-sets tests check that a recursive rule with a literal start that recurses through another rule returns the same results as without the literal (#297). API tests check that `execute()` and `WriteTransaction::execute()` reject `$slot` bind tokens in every position with API-010 (#407). The REPL `run()` test now runs in a child process with null stdin, so it no longer hangs when the test runner's stdin is an open non-TTY stream (#415). A fault-injection test crashes `save()` at each of its 47 page writes and syncs and checks that every reopen finds exactly the old or the new checkpointed facts (#374). For v2.0.3, storage unit tests and two `edge_cases_test.rs` tests check that more than 65,535 uncheckpointed facts each resolve to their own fact through entity lookups and attribute scans, in memory and file-backed, before and after a checkpoint, and that the widened `FactRef` encodes to the same bytes as before (#445). A WAL test reopens a database next to a WAL whose entries are all checkpointed, writes again, and checks that `:as-of` history is unchanged before and after another reopen; a unit test checks that restoring the transaction counter never lowers it (#447). Since v2.0.3, `tests/golden_corpus_test.rs` opens a copy of each committed golden file in `tests/golden/` (v7 files written by minigraf 2.0.3: one checkpoint, several checkpoints, indexes rebuilt on open, a crash-left WAL, an already-checkpointed WAL, same-transaction multi-values) and checks its manifest's CRC32, `tx_count` and query results after open, after a checkpoint and reopen, and after one more write; a second test checks that every golden file has a manifest (#391).

### Covered Areas

- **Core database behavior**: in-memory and file-backed operation, transactions, checkpoints, retractions, bi-temporal queries, recursive rules, prepared statements, aggregation, expressions, disjunction, window functions, UDFs, and magic-sets evaluation.
- **Storage correctness**: packed pages, B+tree indexes and range scans, cache behavior, file-header validation, migration, WAL replay, checksums, corruption handling, and fault injection.
- **Reliability and concurrency**: concurrent reads and writes, rollback behavior, same-process handle exclusion, cross-process locking, PID namespaces, NFS and container lock behavior, and SIGKILL recovery.
- **Public diagnostics**: structured parser, query, storage, WAL, API, and internal error codes; the error-code registry is checked against [`ERROR_REFERENCE.md`](ERROR_REFERENCE.md).
- **Compatibility and quality**: cross-platform format compatibility, property-based tests, long-haul smoke coverage, XTDB/Datomic semantic compatibility, grammar conformance, and rustdoc examples.
- **Compatibility and quality**: golden-file corpus of released file formats, cross-platform format compatibility, property-based tests, long-haul smoke coverage, XTDB/Datomic semantic compatibility, grammar conformance, and rustdoc examples.

### Ignored Tests

Expand Down
35 changes: 35 additions & 0 deletions docs/superpowers/plans/2026-10-06-golden-file-corpus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Golden-file corpus: implementation plan (#391)

Spec: `docs/superpowers/specs/2026-10-06-golden-file-corpus-design.md`.

## PR A: `main` (branch `test/391-golden-corpus`)

1. **Generator crate** `tests/golden/gen/v7/` with `minigraf = "=2.0.3"` and
`crc32fast`, its own `[workspace]`, and a committed `Cargo.lock`. Add the
crate to root `workspace.exclude`. It holds one function per recipe:
`basic`, `multi_checkpoint`, `index_rebuilt`, `pending_wal` and `multivalue`.
It refuses to overwrite existing files and prints the CRC32 and `tx_count`
of each file.
2. **Generate** into `tests/golden/`. Check the version bytes (`xxd -l 8`) and
that `v7_pending_wal.graph.wal` exists.
3. **Manifests**: write the five `.json` files by hand from the recipes, as in
spec §5.
4. **Harness** `tests/golden_corpus_test.rs` with `READER = 2`, implementing
the steps in spec §6. Check that it is green, then that it catches a broken
manifest: change one expected row and one CRC, see both failures reported,
then revert.
5. **Policy job** `golden-corpus` in `.github/workflows/policy.yml`. This job
lets PRs add golden files but never modify or delete them.
6. **Docs**: `tests/golden/README.md`, CONTRIBUTING.md (the add-only rule),
`docs/TEST_COVERAGE.md`, CHANGELOG Unreleased, and the CLAUDE.md test count.
7. Run fmt, clippy, and `cargo test`. Open the PR to `main` and own CI until it
passes.

## PR B: `v3` (after `main` is merged into `v3`)

1. Set `READER = 3`. Add the migration checks: the header is v8 after open, and
the v1 WAL is replayed and then removed.
2. **Generator crate** `tests/golden/gen/v8/` pinned to a `v3` git rev. It
writes `basic`, `multi_checkpoint`, `pending_wal`, `multivalue`,
`migrated_from_v7` and `large_values`.
3. Write the v8 manifests and update the v3 docs. Open the PR to `v3`.
Loading
Loading
0