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

nordisk/gunnar

crates/gunnar-cli/tests/blame_search.rs

#![cfg(all(feature = "server", feature = "control"))]

//! **`blame` and `search`, end to end**, through stock git over real ssh.
//!
//! The `gunnar-server` unit tests prove the *algorithms* against a commit graph
//! whose oids the test chose. This proves the other half — the verb's store
//! resolution: a repository pushed by stock git, a revision resolved the way
//! `Refs.Log` resolves it, and the answer read back from the real store. The
//! oids blame is checked against are the ones `git rev-parse` reported at push
//! time, so this is the verb being checked against git, not against itself.

mod common;

use common::Harness;
use gunnar_cli::server::blame::blame;
use gunnar_cli::server::search::{search, Limits};
use gunnar_core::HashKind;
use gunnar_proto::RepoPath;

fn tools_present() -> bool {
    ["git", "ssh"].iter().all(|tool| {
        std::process::Command::new(tool)
            .arg("--version")
            .output()
            .is_ok()
    })
}

/// Commit A writes three lines; commit B rewrites the middle one. Returns
/// `(oid_a, oid_b)` as `git rev-parse` reported them.
fn two_commits(harness: &Harness, work: &std::path::Path, url: &str) -> (String, String) {
    harness
        .git(
            harness.dir.path(),
            &["init", "--quiet", "-b", "main", work.to_str().unwrap()],
        )
        .expect_ok("git init");

    std::fs::write(work.join("src.txt"), "line one\nline two\nline three\n").expect("write");
    harness.git(work, &["add", "src.txt"]).expect_ok("add");
    harness
        .git(work, &["commit", "--quiet", "-m", "A"])
        .expect_ok("commit A");
    let a = harness.git(work, &["rev-parse", "HEAD"]);
    a.expect_ok("rev-parse A");

    std::fs::write(
        work.join("src.txt"),
        "line one\nLINE TWO CHANGED\nline three\n",
    )
    .expect("write");
    harness
        .git(work, &["commit", "--quiet", "-am", "B"])
        .expect_ok("commit B");
    let b = harness.git(work, &["rev-parse", "HEAD"]);
    b.expect_ok("rev-parse B");

    harness
        .git(work, &["push", "--quiet", url, "main"])
        .expect_ok("push");

    (a.stdout.trim().to_owned(), b.stdout.trim().to_owned())
}

/// **Blame names the commit that wrote each line**, by the oid git reported —
/// line 2 is B's, lines 1 and 3 are A's.
#[test]
fn blame_names_the_commit_that_wrote_each_line() {
    assert!(
        tools_present(),
        "this test requires `git` and `ssh` on PATH"
    );
    let mut harness = Harness::start("team", "blame", HashKind::Sha1);
    let work = harness.dir.path().join("work");
    let url = harness.url("team", "blame");
    let (oid_a, oid_b) = two_commits(&harness, &work, &url);

    let plane = gunnar_cli::server::CoreControlPlane::new(std::sync::Arc::clone(&harness.stores));
    let repo = RepoPath::new("team", "blame");
    let rows = blame(&harness.stores, &plane, &repo, "refs/heads/main", "src.txt").expect("blame");

    assert_eq!(rows.len(), 3, "the file has three lines");
    assert!(
        oid_a.starts_with(&rows[0].commit.to_hex().to_string()[..12]),
        "line 1 must be A ({oid_a}), got {}",
        rows[0].commit
    );
    assert!(
        oid_b.starts_with(&rows[1].commit.to_hex().to_string()[..12]),
        "line 2 was rewritten by B ({oid_b}), got {}",
        rows[1].commit
    );
    assert!(
        oid_a.starts_with(&rows[2].commit.to_hex().to_string()[..12]),
        "line 3 must be A ({oid_a}), got {}",
        rows[2].commit
    );
    assert_eq!(rows[1].line, b"LINE TWO CHANGED");

    harness.stop();
}

/// **Search finds a present string at its exact line, and an absent one returns
/// nothing** — not an error, not a false hit.
#[test]
fn search_finds_the_present_string_and_not_the_absent_one() {
    assert!(
        tools_present(),
        "this test requires `git` and `ssh` on PATH"
    );
    let mut harness = Harness::start("team", "search", HashKind::Sha1);
    let work = harness.dir.path().join("work");
    let url = harness.url("team", "search");
    two_commits(&harness, &work, &url);

    let plane = gunnar_cli::server::CoreControlPlane::new(std::sync::Arc::clone(&harness.stores));
    let repo = RepoPath::new("team", "search");

    let hits = search(
        &harness.stores,
        &plane,
        &repo,
        "refs/heads/main",
        "CHANGED",
        Limits::default(),
    )
    .expect("search");
    assert_eq!(hits.len(), 1, "the string is on exactly one line");
    assert_eq!(hits[0].path, "src.txt");
    assert_eq!(hits[0].line_number, 2);

    let none = search(
        &harness.stores,
        &plane,
        &repo,
        "refs/heads/main",
        "this string is nowhere in the tree",
        Limits::default(),
    )
    .expect("a search for an absent string is not an error");
    assert!(
        none.is_empty(),
        "an absent string yields zero hits, got {none:?}"
    );

    harness.stop();
}