use release_tool::command::SystemCommandRunner;
use release_tool::git::GitRepository;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::Arc;
use tempfile::TempDir;
#[test]
fn dirty_tracked_worktree_is_rejected() {
let fixture = GitFixture::new();
fs::write(fixture.work.join("source"), "changed\n").unwrap();
let error = fixture.repository().snapshot("main").unwrap_err();
assert!(error.to_string().contains("clean worktree"));
}
#[test]
fn detached_head_is_rejected() {
let fixture = GitFixture::new();
run(&fixture.work, &["git", "checkout", "--detach", "HEAD"]);
let error = fixture.repository().snapshot("main").unwrap_err();
assert!(error.to_string().contains("detached HEAD"));
}
#[test]
fn wrong_branch_is_rejected() {
let fixture = GitFixture::new();
run(&fixture.work, &["git", "switch", "-c", "feature"]);
let error = fixture.repository().snapshot("main").unwrap_err();
assert!(error.to_string().contains("current branch is `feature`"));
}
#[test]
fn head_that_differs_from_the_remote_branch_is_rejected() {
let fixture = GitFixture::new();
fs::write(fixture.work.join("source"), "next\n").unwrap();
run(&fixture.work, &["git", "add", "--", "source"]);
run(&fixture.work, &["git", "commit", "-m", "Local only"]);
let error = fixture.repository().snapshot("main").unwrap_err();
assert!(error.to_string().contains("HEAD must equal origin/main"));
}
#[test]
fn matching_remote_branch_does_not_require_a_local_upstream() {
let fixture = GitFixture::new();
run(&fixture.work, &["git", "branch", "--unset-upstream"]);
let snapshot = fixture.repository().snapshot("main").unwrap();
assert_eq!(snapshot.branch, "main");
assert_eq!(snapshot.head, snapshot.remote_branch_head);
}
struct GitFixture {
_root: TempDir,
work: PathBuf,
}
impl GitFixture {
fn new() -> Self {
let root = tempfile::tempdir().unwrap();
let work = root.path().join("work");
let remote = root.path().join("remote.git");
run(
root.path(),
&["git", "init", "--bare", remote.to_str().unwrap()],
);
run(
root.path(),
&[
"git",
"init",
"--initial-branch=main",
work.to_str().unwrap(),
],
);
run(&work, &["git", "config", "user.name", "Release Test"]);
run(
&work,
&["git", "config", "user.email", "release@example.com"],
);
fs::write(work.join("source"), "seed\n").unwrap();
run(&work, &["git", "add", "--", "source"]);
run(&work, &["git", "commit", "-m", "Seed"]);
run(
&work,
&["git", "remote", "add", "origin", remote.to_str().unwrap()],
);
run(&work, &["git", "push", "-u", "origin", "main"]);
Self { _root: root, work }
}
fn repository(&self) -> GitRepository {
GitRepository::new(&self.work, Arc::new(SystemCommandRunner))
}
}
fn run(directory: &Path, command: &[&str]) {
let result = Command::new(command[0])
.args(&command[1..])
.current_dir(directory)
.output()
.unwrap();
assert!(
result.status.success(),
"command {command:?} failed: {}",
String::from_utf8_lossy(&result.stderr)
);
}