tests/workspace_holger.rs
//! End-to-end: load a workspace descriptor pointing at the real
//! sibling `holger` and `znippy` checkouts, build the cross-repo dep
//! graph, derive the build order, persist into a fresh
//! IcebergWarehouse, reopen the warehouse, query it back, and assert
//! the snapshot round-trips.
//!
//! Skipped automatically if the sibling clones aren't present (so CI
//! and other machines don't fail loudly).
use std::path::{Path, PathBuf};
use nornir::warehouse::dep_graph;
use nornir::warehouse::iceberg::IcebergWarehouse;
use nornir::workspace::WorkspaceDescriptor;
fn sibling(name: &str) -> Option<PathBuf> {
let here = Path::new(env!("CARGO_MANIFEST_DIR"));
let candidate = here.parent()?.join(name);
if candidate.join("Cargo.toml").is_file() {
Some(candidate)
} else {
None
}
}
#[test]
fn holger_znippy_workspace_graph_and_persistence() {
let Some(holger) = sibling("holger") else {
eprintln!("[skip] ../holger not checked out next to nornir/");
return;
};
let Some(znippy) = sibling("znippy") else {
eprintln!("[skip] ../znippy not checked out next to nornir/");
return;
};
let tmp = tempfile::tempdir().expect("tempdir");
let toml = format!(
r#"[workspace]
name = "holger-suite"
[repos.holger]
path = "{}"
[repos.znippy]
path = "{}"
"#,
holger.display(),
znippy.display()
);
let toml_path = tmp.path().join("nornir-workspace.toml");
std::fs::write(&toml_path, toml).expect("write workspace toml");
// ─── 1. parse + build graph ─────────────────────────────────────
let desc = WorkspaceDescriptor::load(&toml_path).expect("load descriptor");
let graph = dep_graph::build(&desc).expect("build graph");
assert!(
graph.facts.contains_key("holger") && graph.facts.contains_key("znippy"),
"both repos must be inspected"
);
let holger_to_znippy = graph
.dependencies_of("holger")
.into_iter()
.find(|e| e.to == "znippy")
.expect("expected edge holger -> znippy");
for crate_name in ["znippy-common", "znippy-compress"] {
assert!(
holger_to_znippy.via.contains(crate_name),
"edge holger->znippy missing crate `{crate_name}` in via set: {:?}",
holger_to_znippy.via
);
}
assert!(
graph
.dependencies_of("znippy")
.iter()
.all(|e| e.to != "holger"),
"found illegal reverse edge znippy -> holger"
);
// ─── 2. build order: deps first ────────────────────────────────
let order = graph.build_order().expect("toposort");
let pos_z = order
.iter()
.position(|n| n == "znippy")
.expect("znippy in order");
let pos_h = order
.iter()
.position(|n| n == "holger")
.expect("holger in order");
assert!(
pos_z < pos_h,
"build order must put znippy before holger; got {order:?}"
);
// ─── 3. persist into warehouse ─────────────────────────────────
let wh_root = tmp.path().join("warehouse-root");
let wh = IcebergWarehouse::open(&wh_root).expect("open IcebergWarehouse");
let snap_id = wh
.block_on(dep_graph::record_dep_graph(&wh, "holger-suite", &graph))
.expect("record snapshot");
// ─── 4. reopen + query ────────────────────────────────────────
drop(wh);
let wh2 = IcebergWarehouse::open(&wh_root).expect("reopen warehouse");
let snaps = wh2
.block_on(dep_graph::query_dep_graph_snapshots(
&wh2,
"holger-suite",
None,
))
.expect("query snapshots");
assert_eq!(snaps.len(), 1, "expected exactly one snapshot");
let snap = &snaps[0];
assert_eq!(snap.snapshot_id, snap_id);
assert_eq!(snap.workspace_name, "holger-suite");
let persisted_edge = snap
.edges
.iter()
.find(|e| e.from == "holger" && e.to == "znippy")
.expect("persisted edge holger->znippy");
assert!(persisted_edge.via.contains("znippy-common"));
assert!(persisted_edge.via.contains("znippy-compress"));
}