use std::{path::Path, process::Command};
use basis::{Fingerprint, Snapshot, fingerprint};
fn known(workspace: &Path) -> Fingerprint {
match fingerprint::snapshot(workspace) {
Snapshot::Known(fingerprint) => fingerprint,
Snapshot::Unknown { reason } => panic!("expected a fingerprint, got: {reason}"),
}
}
fn git(dir: &Path, args: &[&str]) {
let status = Command::new("git")
.arg("-C")
.arg(dir)
.args(["-c", "user.email=test@example.invalid"])
.args(["-c", "user.name=basis tests"])
.args(["-c", "commit.gpgsign=false"])
.args(args)
.status()
.expect("git runs");
assert!(status.success(), "git {args:?} failed");
}
fn repository() -> tempfile::TempDir {
let dir = tempfile::tempdir().expect("tempdir");
git(dir.path(), &["init", "--quiet"]);
std::fs::write(dir.path().join("tracked.txt"), "one").expect("write");
git(dir.path(), &["add", "tracked.txt"]);
git(dir.path(), &["commit", "--quiet", "-m", "first"]);
dir
}
#[test]
fn a_repository_fingerprints_at_all() {
let repo = repository();
assert_eq!(known(repo.path()), known(repo.path()));
}
#[test]
fn an_ignored_file_does_not_count_as_a_change() {
let repo = repository();
std::fs::write(repo.path().join(".gitignore"), "build/\n").expect("write");
git(repo.path(), &["add", ".gitignore"]);
git(repo.path(), &["commit", "--quiet", "-m", "ignore build"]);
let before = known(repo.path());
std::fs::create_dir(repo.path().join("build")).expect("mkdir");
std::fs::write(repo.path().join("build/artifact.bin"), "noise").expect("write");
assert_eq!(
known(repo.path()),
before,
"an ignored file is not workspace content"
);
}
#[test]
fn an_untracked_file_does_count_as_a_change() {
let repo = repository();
let before = known(repo.path());
std::fs::write(repo.path().join("new.rs"), "fn main() {}").expect("write");
assert_ne!(known(repo.path()), before);
}
#[test]
fn a_commit_alone_changes_the_fingerprint() {
let repo = repository();
std::fs::write(repo.path().join("tracked.txt"), "two").expect("write");
git(repo.path(), &["add", "tracked.txt"]);
let before = known(repo.path());
git(repo.path(), &["commit", "--quiet", "-m", "second"]);
assert_ne!(known(repo.path()), before);
}
#[test]
fn a_tracked_file_deleted_changes_the_fingerprint() {
let repo = repository();
let before = known(repo.path());
std::fs::remove_file(repo.path().join("tracked.txt")).expect("remove");
assert_ne!(known(repo.path()), before);
}
#[test]
fn a_repository_and_a_plain_directory_are_told_apart() {
let repo = repository();
let before = known(repo.path());
std::fs::remove_dir_all(repo.path().join(".git")).expect("remove .git");
assert_ne!(
known(repo.path()),
before,
"losing the repository is a change in how the workspace is read"
);
}