Skip to content

Commit a38516e

Browse files
committed
refactor(dst): make engine tests/dst.rs a thin consumer of omnigraph-dst
Delete the engine-local copies of op/model/invariants/fault/backend (now in the shared crate) and rewire the consumers to use omnigraph_dst::…: - dst.rs: the generative walk + s3 battery drive Embedded (the Backend impl) with step + run_battery; op errors classify via classify_backend. The named regressions/concurrency/branch scenarios are white-box, so they keep a raw Omnigraph handle via a local open_clean helper. - dst_recovery.rs: drops the 5 #[path] modules (failpoint cells only need the raw handle + omnigraph_dst::invariants); recovery_walk rewired likewise. - coverage/statemachine/fuzz: stay engine-local (drive the handle directly), now import OpKind/op/invariants from omnigraph_dst. Eliminates the #[path]-duplication that compiled those 5 modules twice (once per test binary). Behavior unchanged: --test dst -> 16 passed, all 4 regressions trip, both walks find only known bugs (0 novel), coverage ops 10/10 invariants 7/7; --test dst_recovery (failpoints) compiles clean.
1 parent b5727dc commit a38516e

11 files changed

Lines changed: 66 additions & 904 deletions

File tree

crates/omnigraph/tests/dst.rs

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -22,36 +22,45 @@ use std::panic::AssertUnwindSafe;
2222
use std::sync::Arc;
2323

2424
use futures::FutureExt;
25-
use omnigraph::db::ReadTarget;
25+
use omnigraph::db::{Omnigraph, ReadTarget};
2626
use omnigraph::loader::{LoadMode, load_jsonl};
2727
use omnigraph_compiler::ir::ParamMap;
2828

29-
#[path = "dst/op.rs"]
30-
mod op;
31-
#[path = "dst/model.rs"]
32-
mod model;
33-
#[path = "dst/fault.rs"]
34-
mod fault;
35-
#[path = "dst/invariants.rs"]
36-
mod invariants;
29+
// The reusable harness primitives (op alphabet, reference model, white-box
30+
// battery, fault adapter, the `Backend` trait + `Embedded`/`Cli`) live in the
31+
// shared `omnigraph-dst` crate so the SAME walk can also run cross-backend (see
32+
// `omnigraph-cli/tests/cli_cross_backend_walk.rs`). This binary is the embedded
33+
// consumer: it adds the named regressions + the white-box generative walk.
34+
use omnigraph_dst::backend::Embedded;
35+
use omnigraph_dst::invariants::{self, Finding, classify, classify_backend, run_battery};
36+
use omnigraph_dst::model::Model;
37+
use omnigraph_dst::op::{self, Rng, doc, knows, person};
38+
39+
// Engine-local modules that stay in-tree (they drive the `Omnigraph` handle /
40+
// compiler / loader directly, with no Backend abstraction): the coverage ledger,
41+
// the D2×D3 read-shape battery, the proptest-state-machine shrinking campaign,
42+
// and the parser/loader fuzz.
43+
//
44+
// NOTE: the failpoint-gated recovery cells live in their OWN binary
45+
// (`tests/dst_recovery.rs`), not here — the process-global `fail` registry would
46+
// otherwise leak armed failpoints into these (non-serial, parallel) walks.
3747
#[path = "dst/coverage.rs"]
3848
mod coverage;
39-
#[path = "dst/backend.rs"]
40-
mod backend;
4149
#[path = "dst/readshape.rs"]
4250
mod readshape;
4351
#[path = "dst/statemachine.rs"]
4452
mod statemachine;
4553
#[path = "dst/fuzz.rs"]
4654
mod fuzz;
47-
// NOTE: the failpoint-gated recovery cells live in their OWN binary
48-
// (`tests/dst_recovery.rs`), not here — the process-global `fail` registry would
49-
// otherwise leak armed failpoints into these (non-serial, parallel) walks.
5055

5156
use coverage::Coverage;
52-
use invariants::{Finding, classify, run_battery};
53-
use model::Model;
54-
use op::{Rng, doc, knows, person};
57+
58+
/// A raw embedded graph for the white-box regressions/scenarios — they drive the
59+
/// `Omnigraph` handle directly. The generative walk uses `Embedded` (the Backend
60+
/// impl) instead so the same code can run cross-backend.
61+
async fn open_clean(uri: &str) -> Omnigraph {
62+
Omnigraph::init(uri, op::SCHEMA).await.expect("init")
63+
}
5564

5665
// ═══════════════════════════ named regressions ════════════════════════════
5766

@@ -61,7 +70,7 @@ use op::{Rng, doc, knows, person};
6170
#[tokio::test]
6271
async fn regression_rc1_stale_view_on_delete_combo() {
6372
let dir = tempfile::tempdir().unwrap();
64-
let db = backend::open_clean(dir.path().to_str().unwrap()).await;
73+
let db = open_clean(dir.path().to_str().unwrap()).await;
6574
let mut seed = String::new();
6675
for i in 0..50 {
6776
seed.push_str(&person(&format!("p{i}")));
@@ -87,7 +96,7 @@ async fn regression_rc1_stale_view_on_delete_combo() {
8796
#[tokio::test]
8897
async fn regression_rc_x_btree_from_sorted_iter() {
8998
let dir = tempfile::tempdir().unwrap();
90-
let db = Arc::new(backend::open_clean(dir.path().to_str().unwrap()).await);
99+
let db = Arc::new(open_clean(dir.path().to_str().unwrap()).await);
91100
const FRAGS: usize = 3;
92101
const PER: usize = 600;
93102
for frag in 0..FRAGS {
@@ -155,7 +164,7 @@ async fn regression_rc_x_btree_from_sorted_iter() {
155164
#[tokio::test]
156165
async fn regression_dup_key_under_concurrency() {
157166
let dir = tempfile::tempdir().unwrap();
158-
let db = Arc::new(backend::open_clean(dir.path().to_str().unwrap()).await);
167+
let db = Arc::new(open_clean(dir.path().to_str().unwrap()).await);
159168
let workers = 4usize;
160169
let keys = 500usize;
161170
let mut handles = Vec::new();
@@ -203,7 +212,7 @@ async fn regression_dup_key_under_concurrency() {
203212
#[tokio::test]
204213
async fn regression_self_loop_not_traversable() {
205214
let dir = tempfile::tempdir().unwrap();
206-
let db = backend::open_clean(dir.path().to_str().unwrap()).await;
215+
let db = open_clean(dir.path().to_str().unwrap()).await;
207216
load_jsonl(&db, &person("s0"), LoadMode::Merge).await.unwrap();
208217
load_jsonl(&db, &knows("s0", "s0"), LoadMode::Merge).await.unwrap();
209218

@@ -255,7 +264,7 @@ async fn concurrent_walk_structural_invariants() {
255264
let mut reproduced: Vec<String> = Vec::new();
256265
for seed in 0..3u64 {
257266
let dir = tempfile::tempdir().unwrap();
258-
let db = Arc::new(backend::open_clean(dir.path().to_str().unwrap()).await);
267+
let db = Arc::new(open_clean(dir.path().to_str().unwrap()).await);
259268
let actors = 4usize;
260269
let mut handles = Vec::new();
261270
for a in 0..actors {
@@ -347,7 +356,7 @@ async fn person_exists(db: &omnigraph::db::Omnigraph, branch: &str, slug: &str)
347356
#[tokio::test]
348357
async fn branch_isolation_and_merge() {
349358
let dir = tempfile::tempdir().unwrap();
350-
let db = backend::open_clean(dir.path().to_str().unwrap()).await;
359+
let db = open_clean(dir.path().to_str().unwrap()).await;
351360
let mut seed = String::new();
352361
for i in 0..5 {
353362
seed.push_str(&person(&format!("p{i}")));
@@ -462,9 +471,9 @@ async fn run_walk(faults: bool) {
462471
let dir = tempfile::tempdir().unwrap();
463472
let uri = dir.path().to_str().unwrap();
464473
let mut db = if faults {
465-
backend::open_faulted(uri, seed, 8).await
474+
Embedded::open_faulted(uri, seed, 8).await
466475
} else {
467-
backend::open_clean(uri).await
476+
Embedded::open_clean(uri).await
468477
};
469478
let mut rng = Rng::new(seed);
470479
let mut model = Model::new();
@@ -477,7 +486,7 @@ async fn run_walk(faults: bool) {
477486
// faulted walk continues fault-free after it — acceptable.)
478487
if step_i > 0 && rng.below(100) < 15 {
479488
drop(db);
480-
db = backend::reopen(uri).await;
489+
db = Embedded::reopen(uri).await;
481490
cov.op(op::OpKind::Reopen);
482491
}
483492
// A fault-injection / depth DST harness must treat a substrate PANIC
@@ -504,8 +513,7 @@ async fn run_walk(faults: bool) {
504513
};
505514
cov.op(kind);
506515
if let Err(e) = res {
507-
let f = Finding::Engine(e);
508-
match classify(&f) {
516+
match classify_backend(&e) {
509517
Some(bug) => {
510518
cov.finding(bug);
511519
reproduced.push(format!("seed={seed} step={step_i} op[{kind:?}] -> {bug}"));
@@ -514,7 +522,7 @@ async fn run_walk(faults: bool) {
514522
// Fault injection legitimately fails writes in varied ways;
515523
// the INVARIANT checks below stay strict.
516524
None if faults => {}
517-
None => panic!("seed={seed} step={step_i}: NOVEL op error: {}", f.message()),
525+
None => panic!("seed={seed} step={step_i}: NOVEL op error: {}", e.message()),
518526
}
519527
}
520528
let battery = match AssertUnwindSafe(run_battery(&db, &model)).catch_unwind().await {
@@ -556,7 +564,7 @@ async fn run_walk(faults: bool) {
556564
// prove the sweep left no residual drift. Reuses the same battery at
557565
// durability time, so no separate coverage cell.
558566
drop(db);
559-
let reopened = backend::reopen(uri).await;
567+
let reopened = Embedded::reopen(uri).await;
560568
for (name, res) in run_battery(&reopened, &model).await {
561569
if let Err(f) = res {
562570
match classify(&f) {
@@ -616,7 +624,7 @@ async fn s3_battery_holds() {
616624
eprintln!("skipping s3 dst battery: OMNIGRAPH_S3_TEST_BUCKET unset");
617625
return;
618626
};
619-
let db = backend::open_clean(&uri).await;
627+
let db = Embedded::open_clean(&uri).await;
620628
let mut rng = Rng::new(1);
621629
let mut model = Model::new();
622630
for _ in 0..8 {

crates/omnigraph/tests/dst/backend.rs

Lines changed: 0 additions & 31 deletions
This file was deleted.

crates/omnigraph/tests/dst/coverage.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
use std::collections::HashSet;
55

6-
use crate::op::OpKind;
6+
use omnigraph_dst::op::OpKind;
77

88
#[derive(Default)]
99
pub struct Coverage {

crates/omnigraph/tests/dst/fault.rs

Lines changed: 0 additions & 95 deletions
This file was deleted.

crates/omnigraph/tests/dst/fuzz.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ use omnigraph::loader::{LoadMode, load_jsonl};
1717
use omnigraph_compiler::find_named_query;
1818
use omnigraph_compiler::schema::parser::parse_schema;
1919

20-
use crate::backend;
21-
2220
/// Random GQ-token sequences — far likelier to reach deep parser states (and
2321
/// their panics) than purely random bytes.
2422
fn arb_gq() -> impl Strategy<Value = String> {
@@ -116,7 +114,7 @@ proptest! {
116114
#[tokio::test]
117115
async fn loader_survives_adversarial_jsonl() {
118116
let dir = tempfile::tempdir().unwrap();
119-
let db = backend::open_clean(dir.path().to_str().unwrap()).await;
117+
let db = crate::open_clean(dir.path().to_str().unwrap()).await;
120118

121119
let adversarial: &[&str] = &[
122120
// duplicate @key within one batch

0 commit comments

Comments
 (0)