//! Walks every JSON fixture under `crates/plan-runner/fixtures/` and //! verifies it against the `expected_bindings` the exporter lifted from //! the matching `tools/exporter/examples/*.scenario.json`. A fixture without an oracle //! is reported as a failure (every checked-in fixture is expected to //! carry one). use std::collections::BTreeMap; use std::fs; use std::path::PathBuf; use plan_runner::{parse_plan, run_json, verify}; fn fixtures_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixtures") } fn collect_fixtures() -> BTreeMap { let mut out = BTreeMap::new(); for entry in fs::read_dir(fixtures_dir()).expect("read fixtures/") { let path = entry.expect("dir entry").path(); if path.extension().and_then(|e| e.to_str()) != Some("json") { continue; } let name = path .file_stem() .and_then(|s| s.to_str()) .expect("ascii fixture name") .to_string(); let contents = fs::read_to_string(&path).expect("read fixture"); out.insert(name, contents); } out } #[test] fn every_fixture_runs_and_matches_its_oracle() { let fixtures = collect_fixtures(); assert!( !fixtures.is_empty(), "no fixtures found in {}", fixtures_dir().display() ); let mut failures: Vec = Vec::new(); for (name, json) in &fixtures { let plan = match parse_plan(json) { Ok(p) => p, Err(err) => { failures.push(format!("{name}: parse error: {err}")); continue; } }; if plan.expected_bindings.is_none() { failures.push(format!("{name}: fixture has no expected_bindings")); continue; } let relation = match run_json(json) { Ok(r) => r, Err(err) => { failures.push(format!("{name}: run error: {err}")); continue; } }; match verify(&plan, &relation) { Ok(true) => {} Ok(false) => failures.push(format!("{name}: verify returned no-oracle unexpectedly")), Err(err) => failures.push(format!("{name}: {err}")), } } assert!( failures.is_empty(), "{} fixture(s) failed:\n {}", failures.len(), failures.join("\n ") ); }