use std::path::Path;
use std::process::Command;
use tempfile::TempDir;
fn git(path: &Path, args: &[&str]) {
let output = Command::new("git")
.args(args)
.current_dir(path)
.output()
.unwrap();
assert!(
output.status.success(),
"git {} in {} failed: {}",
args.join(" "),
path.display(),
String::from_utf8_lossy(&output.stderr)
);
}
fn git_config(path: &Path) {
git(path, &["config", "user.email", "test@test.com"]);
git(path, &["config", "user.name", "Test"]);
}
fn create_test_repo() -> TempDir {
let tmp = TempDir::new().unwrap();
git(tmp.path(), &["init", "-b", "main"]);
git_config(tmp.path());
std::fs::write(tmp.path().join("file1.rs"), "fn original() {}\n").unwrap();
std::fs::write(tmp.path().join("file2.rs"), "fn two() {}\n").unwrap();
git(tmp.path(), &["add", "."]);
git(tmp.path(), &["commit", "-m", "Initial commit"]);
tmp
}
fn wsb_git(repo_path: &Path, args: &[&str]) -> std::process::Output {
let mut cmd_args = vec!["git"];
cmd_args.extend_from_slice(args);
Command::new(env!("CARGO_BIN_EXE_wsb"))
.args(&cmd_args)
.current_dir(repo_path)
.output()
.unwrap()
}
#[test]
fn test_diff_no_changes_produces_no_files() {
let repo = create_test_repo();
let outdir = repo.path().join("diffs");
let output = wsb_git(repo.path(), &["diff", "-o", outdir.to_str().unwrap()]);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success(), "Should succeed with no changes");
assert!(
stdout.contains("No changes"),
"Should report no changes. Got: {}",
stdout
);
assert!(
!outdir.exists() || std::fs::read_dir(&outdir).unwrap().count() == 0,
"No diff files should be created"
);
}
#[test]
fn test_diff_unstaged_changes_produce_diff_files() {
let repo = create_test_repo();
let outdir = repo.path().join("diffs");
std::fs::write(repo.path().join("file1.rs"), "fn modified() {}\n").unwrap();
let output = wsb_git(repo.path(), &["diff", "-o", outdir.to_str().unwrap()]);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success(), "Should succeed");
assert!(
stdout.contains("1 diff file(s)"),
"Should report 1 diff file. Got: {}",
stdout
);
let diff_file = outdir.join("file1.rs.diff");
assert!(diff_file.exists(), "file1.rs.diff should exist");
let content = std::fs::read_to_string(&diff_file).unwrap();
assert!(
content.contains("original") && content.contains("modified"),
"Diff should show old and new content. Got: {}",
content
);
}
#[test]
fn test_diff_staged_flag_includes_staged_changes() {
let repo = create_test_repo();
let outdir = repo.path().join("diffs");
std::fs::write(repo.path().join("file2.rs"), "fn staged_change() {}\n").unwrap();
git(repo.path(), &["add", "file2.rs"]);
let output = wsb_git(repo.path(), &["diff", "-o", outdir.to_str().unwrap()]);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("No changes"),
"Without --staged, should find no unstaged changes. Got: {}",
stdout
);
let outdir2 = repo.path().join("diffs2");
let output = wsb_git(
repo.path(),
&["diff", "--staged", "-o", outdir2.to_str().unwrap()],
);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(
stdout.contains("1 diff file(s)"),
"With --staged, should find staged change. Got: {}",
stdout
);
let diff_file = outdir2.join("file2.rs.diff");
assert!(diff_file.exists(), "file2.rs.diff should exist");
let content = std::fs::read_to_string(&diff_file).unwrap();
assert!(
content.contains("staged_change"),
"Diff should contain staged change. Got: {}",
content
);
}
#[test]
fn test_diff_staged_appends_to_existing_diff() {
let repo = create_test_repo();
let outdir = repo.path().join("diffs");
std::fs::write(repo.path().join("file1.rs"), "fn unstaged_ver() {}\n").unwrap();
git(repo.path(), &["add", "file1.rs"]);
std::fs::write(repo.path().join("file1.rs"), "fn unstaged_ver2() {}\n").unwrap();
let output = wsb_git(
repo.path(),
&["diff", "--staged", "-o", outdir.to_str().unwrap()],
);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(
stdout.contains("file1.rs"),
"Should mention file1.rs. Got: {}",
stdout
);
let diff_file = outdir.join("file1.rs.diff");
assert!(diff_file.exists());
let content = std::fs::read_to_string(&diff_file).unwrap();
assert!(
content.contains("unstaged_ver2") && content.contains("unstaged_ver"),
"Diff should contain both unstaged and staged hunks. Got: {}",
content
);
}
#[test]
fn test_diff_with_base_ref() {
let repo = create_test_repo();
let outdir = repo.path().join("diffs");
std::fs::write(repo.path().join("file1.rs"), "fn second_commit() {}\n").unwrap();
std::fs::write(repo.path().join("new_file.rs"), "fn brand_new() {}\n").unwrap();
git(repo.path(), &["add", "."]);
git(repo.path(), &["commit", "-m", "Second commit"]);
let output = wsb_git(
repo.path(),
&["diff", "--base", "HEAD~1", "-o", outdir.to_str().unwrap()],
);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(
stdout.contains("2 diff file(s)"),
"Should find 2 changed files. Got: {}",
stdout
);
assert!(
outdir.join("file1.rs.diff").exists(),
"file1.rs.diff should exist"
);
assert!(
outdir.join("new_file.rs.diff").exists(),
"new_file.rs.diff should exist"
);
let content = std::fs::read_to_string(outdir.join("new_file.rs.diff")).unwrap();
assert!(
content.contains("brand_new"),
"new_file.rs diff should show new content. Got: {}",
content
);
}
#[test]
fn test_diff_preserves_directory_structure() {
let repo = create_test_repo();
let outdir = repo.path().join("diffs");
std::fs::create_dir_all(repo.path().join("src/deep/nested")).unwrap();
std::fs::write(
repo.path().join("src/deep/nested/module.rs"),
"fn nested() {}\n",
)
.unwrap();
git(repo.path(), &["add", "."]);
git(repo.path(), &["commit", "-m", "Add nested file"]);
std::fs::write(
repo.path().join("src/deep/nested/module.rs"),
"fn nested_modified() {}\n",
)
.unwrap();
let output = wsb_git(repo.path(), &["diff", "-o", outdir.to_str().unwrap()]);
assert!(output.status.success());
let diff_path = outdir.join("src/deep/nested/module.rs.diff");
assert!(
diff_path.exists(),
"Diff file should preserve directory structure: {}",
diff_path.display()
);
}
#[test]
fn test_diff_with_invalid_base_ref_fails() {
let repo = create_test_repo();
let outdir = repo.path().join("diffs");
let output = wsb_git(
repo.path(),
&[
"diff",
"--base",
"nonexistent-ref-abc123",
"-o",
outdir.to_str().unwrap(),
],
);
assert!(
!output.status.success(),
"Should fail with invalid base ref"
);
}