use std::path::PathBuf;
use std::process::Command;
fn setup_remote(base: &PathBuf) -> PathBuf {
let remote = base.join("remote.git");
std::fs::create_dir_all(&remote).expect("create remote dir");
let out = Command::new("git")
.args(["init", "--bare"])
.arg(&remote)
.output()
.expect("git init --bare");
assert!(
out.status.success(),
"git init --bare failed: {:?}",
out.stderr
);
let work = base.join("work");
std::fs::create_dir_all(&work).expect("create work dir");
let run = |args: &[&str], dir: &PathBuf| {
let out = Command::new(args[0])
.args(&args[1..])
.current_dir(dir)
.output()
.unwrap_or_else(|e| panic!("command {:?} failed: {}", args, e));
assert!(
out.status.success(),
"command {:?} failed in {:?}: {}",
args,
dir,
String::from_utf8_lossy(&out.stderr)
);
};
run(&["git", "init"], &work);
run(&["git", "config", "user.email", "hugues@linux.com"], &work);
run(&["git", "config", "user.name", "Test"], &work);
run(&["git", "config", "commit.gpgsign", "false"], &work);
run(&["git", "config", "core.hooksPath", "/dev/null"], &work);
let remote_str = remote.to_string_lossy().into_owned();
run(&["git", "remote", "add", "origin", &remote_str], &work);
for i in 0..50u32 {
let filename = format!("file{:03}.txt", i);
std::fs::write(work.join(&filename), format!("content {}", i)).expect("write file");
run(&["git", "add", &filename], &work);
run(
&["git", "commit", "-m", &format!("chore: add file {:03}", i)],
&work,
);
}
run(&["git", "push", "origin", "HEAD:main"], &work);
remote
}
fn setup_local(base: &PathBuf, remote: &PathBuf) -> PathBuf {
let local = base.join("local");
let remote_str = remote.to_string_lossy().into_owned();
let out = Command::new("git")
.args(["clone", &remote_str, "local"])
.current_dir(base)
.output()
.expect("git clone");
assert!(
out.status.success(),
"git clone failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let run_local = |args: &[&str]| {
let out = Command::new(args[0])
.args(&args[1..])
.current_dir(&local)
.output()
.unwrap_or_else(|e| panic!("command {:?} failed: {}", args, e));
assert!(
out.status.success(),
"command {:?} failed: {}",
args,
String::from_utf8_lossy(&out.stderr)
);
};
run_local(&["git", "config", "user.email", "hugues@linux.com"]);
run_local(&["git", "config", "user.name", "Test"]);
let out = Command::new("git")
.args(["log", "--oneline"])
.current_dir(&local)
.output()
.expect("git log");
let log = String::from_utf8_lossy(&out.stdout);
let commits: Vec<&str> = log.lines().collect();
let target = commits
.get(19)
.map(|l| l.split_whitespace().next().unwrap_or("HEAD"));
if let Some(sha) = target {
let _ = Command::new("git")
.args(["reset", "--hard", sha])
.current_dir(&local)
.output();
}
local
}
#[test]
fn test_exec_git_pull_volume_guard() {
let base = std::env::temp_dir().join(format!(
"aptu-exec-volume-{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
));
std::fs::create_dir_all(&base).expect("create base dir");
let remote = setup_remote(&base);
let local = setup_local(&base, &remote);
let out = Command::new("git")
.args(["pull", "--no-stat", "origin", "main"])
.current_dir(&local)
.output()
.expect("git pull");
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);
let combined_len = stdout.len() + stderr.len();
assert!(
combined_len < 2000,
"git pull output should be < 2000 chars, got {} (stdout={} + stderr={})\nstdout: {}\nstderr: {}",
combined_len,
stdout.len(),
stderr.len(),
&stdout,
&stderr,
);
let _ = std::fs::remove_dir_all(&base);
}