Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/nornir

crates/mimir-ingest/tests/ingest/dry.rs

//! The dry-run delta (wiring map I3): what a tree that is NOT a commit would
//! change, computed as the consumer would diff it — and nothing written.

use mimir_ingest::{Consumer, DryDelta, Dumper, Kind, RepoSpec};

use super::common::*;

const REPO: &str = "fixture";
const SEP: char = '\u{1f}';

fn edge(from: &str, to: &str) -> String {
    format!("{from}{SEP}{to}{SEP}CALLS")
}

/// (offset seq, ledger rows, node events, edge events, open node keys).
async fn written(c: &Consumer<FakeDumper>) -> (i64, usize, usize, usize, usize) {
    let log = c.log();
    (
        log.offset(REPO).await.unwrap().map_or(0, |o| o.seq),
        log.runs(REPO).await.unwrap().len(),
        events(log, Kind::Nodes).await.len(),
        events(log, Kind::Edges).await.len(),
        c.open_set().repo(Kind::Nodes, REPO).map_or(0, |r| r.len()),
    )
}

#[tokio::test(flavor = "current_thread")]
async fn a_dirty_tree_is_diffed_against_the_open_set_and_nothing_is_written() {
    let tmp = tempfile::tempdir().unwrap();
    let src = Scratch::new(tmp.path().join("src"));
    src.write("Cargo.toml", "[workspace]\nmembers = [\"crates/*\"]\n")
        .write("crates/a/src/lib.rs", "fn f (x: i32) | one\nfn g () | two\ncall g -> f\n")
        .write("crates/b/src/lib.rs", "fn h () | three\n");
    src.commit("c1");
    let log = open_log(&tmp.path().join("lake")).await;
    let mut c = Consumer::boot(log, FakeDumper::default()).await.unwrap();
    let rs = RepoSpec { name: REPO.into(), source: src.root.to_string_lossy().into_owned(), branch: "main".into(), worktree: tmp.path().join("clone"), initial_offset: None };
    c.run(&rs).await.unwrap();
    let before = written(&c).await;
    assert_eq!(before, (1, 1, 4, 1, 3), "c1: a.f a.g b.h (+ the ring's dep helper event), g→f");

    // ---- the tree as it is: nothing would change
    let mut dry = FakeDumper::default();
    let same = dry.dump(&src.root, "worktree").unwrap();
    let d = c.dry_delta(REPO, &same).unwrap();
    assert!(d.is_semantically_empty() && d.spans.is_empty() && d.failed_units.is_empty(), "{d:?}");

    // ---- an UNCOMMITTED edit: f's signature, g gone, k new, the call moves
    src.write("crates/a/src/lib.rs", "fn f (x: i64) | one\nfn k () | four\ncall k -> f\n");
    let dirty = dry.dump(&src.root, "worktree").unwrap();
    let d = c.dry_delta(REPO, &dirty).unwrap();
    assert_eq!(d.repo, REPO);
    assert_eq!((d.nodes.added.clone(), d.nodes.changed.clone(), d.nodes.removed.clone()), (vec!["a k".to_string()], vec!["a f".to_string()], vec!["a g".to_string()]));
    assert_eq!((d.edges.added.clone(), d.edges.changed.len(), d.edges.removed.clone()), (vec![edge("a k", "a f")], 0, vec![edge("a g", "a f")]));
    assert_eq!(d.touched_nodes(), vec!["a k", "a f", "a g"], "the seeds of a blast-radius walk");
    assert_eq!(DryDelta::edge_parts(&d.edges.added[0]), Some(("a k", "a f", "CALLS")));
    assert!(!d.spans.is_empty(), "the lines moved too");
    // crate b was dumped and is untouched: none of its keys appear
    assert!(!d.touched_nodes().iter().any(|n| n.starts_with("b ")));

    // ---- NOTHING was written: no event, no offset, no ledger row, same open set
    assert_eq!(written(&c).await, before);
    // … and the same answer from a reader that holds no consumer at all
    let pinned = c.log().pin().await.unwrap();
    assert_eq!(pinned.dry_delta(REPO, &dirty).await.unwrap(), d);

    // ---- a crate that does not compile in the dirty tree: named, its rows kept
    src.write("crates/b/src/lib.rs", "BROKEN\n");
    let broken = dry.dump(&src.root, "worktree").unwrap();
    let d2 = c.dry_delta(REPO, &broken).unwrap();
    assert_eq!(d2.failed_units, vec!["b:lib".to_string()]);
    assert!(!d2.touched_nodes().iter().any(|n| n.starts_with("b ")), "a unit that failed keeps its open state: {d2:?}");
    assert_eq!(d2.nodes, d.nodes);

    // ---- a crate deleted from the dirty tree: its keys would close
    src.remove("crates/b");
    let gone = dry.dump(&src.root, "worktree").unwrap();
    let d3 = c.dry_delta(REPO, &gone).unwrap();
    assert!(d3.nodes.removed.contains(&"b h".to_string()), "{d3:?}");

    // ---- once the edit IS a commit, the consumer writes exactly what the dry run said
    src.write("crates/b/src/lib.rs", "fn h () | three\n").write("crates/a/src/lib.rs", "fn f (x: i64) | one\nfn k () | four\ncall k -> f\n");
    src.commit("c2 the edit");
    let rep = c.run(&rs).await.unwrap();
    // semantic rows: nodes (+k, +f changed, −g) and edges (+k→f, −g→f)
    assert_eq!((rep[0].rows_plus as usize, rep[0].rows_minus as usize), (d.nodes.added.len() + d.nodes.changed.len() + d.edges.added.len(), d.nodes.removed.len() + d.edges.removed.len()));
    let after = c.dry_delta(REPO, &dry.dump(&src.root, "worktree").unwrap()).unwrap();
    assert!(after.is_semantically_empty() && after.spans.is_empty(), "the tree is the open state now: {after:?}");
}

/// A key that only moves owner (its crate's id changed, the row did not) is
/// not a change of the code.
#[tokio::test(flavor = "current_thread")]
async fn a_key_that_only_moved_owner_is_not_in_the_delta() {
    use mimir_ingest::{CommitDump, CrateDump};
    let tmp = tempfile::tempdir().unwrap();
    let src = Scratch::new(tmp.path().join("src"));
    src.write("Cargo.toml", "[workspace]\nmembers = [\"crates/*\"]\n").write("crates/a/src/lib.rs", "fn f () | one\nfn g () | two\n");
    src.commit("c1");
    let log = open_log(&tmp.path().join("lake")).await;
    let mut c = Consumer::boot(log, FakeDumper::default()).await.unwrap();
    let rs = RepoSpec { name: REPO.into(), source: src.root.to_string_lossy().into_owned(), branch: "main".into(), worktree: tmp.path().join("clone"), initial_offset: None };
    c.run(&rs).await.unwrap();

    // the same rows, dumped under a bumped version of the crate; g's body edited
    src.write("crates/a/src/lib.rs", "fn f () | one\nfn g () | TWO\n");
    let mut dump: CommitDump = FakeDumper::default().dump(&src.root, "worktree").unwrap();
    let bumped = "a@0.2.0:lib".to_string();
    dump.workspace_crates = [bumped.clone()].into();
    for cr in dump.crates.iter_mut().filter(|cr| !cr.universe) {
        *cr = CrateDump { krate: bumped.clone(), ..cr.clone() };
    }
    let d = c.dry_delta(REPO, &dump).unwrap();
    assert_eq!((d.nodes.added.len(), d.nodes.changed.clone(), d.nodes.removed.len()), (0, vec!["a g".to_string()], 0), "f only moved owner; g changed: {d:?}");
}