#![allow(clippy::unwrap_used)]
use std::fs;
use std::path::Path;
use std::process::Output;
fn mkit_bin() -> &'static str {
env!("CARGO_BIN_EXE_mkit")
}
fn run_in(cwd: &Path, xdg: &Path, args: &[&str]) -> Output {
std::process::Command::new(mkit_bin())
.args(args)
.current_dir(cwd)
.env("XDG_CONFIG_HOME", xdg)
.output()
.expect("spawn mkit")
}
fn init_repo() -> (tempfile::TempDir, tempfile::TempDir) {
let td = tempfile::tempdir().unwrap();
let xdg = tempfile::tempdir().unwrap();
let (root, x) = (td.path(), xdg.path());
assert!(run_in(root, x, &["init"]).status.success());
assert!(run_in(root, x, &["keygen"]).status.success());
(td, xdg)
}
fn write_oversized_sparse_file(path: &Path) {
let f = fs::File::create(path).expect("create sparse file");
f.set_len(mkit_core::worktree::MAX_FILE_BYTES + 1)
.expect("set sparse length");
}
fn write_small_files(root: &Path, n: usize) {
for i in 0..n {
fs::write(root.join(format!("f{i}.txt")), format!("content {i}\n")).unwrap();
}
}
fn above_fanout_threshold_file_count() -> usize {
let threads = std::thread::available_parallelism().map_or(4, std::num::NonZeroUsize::get);
8 * threads + 40
}
fn error_line_count(out: &Output) -> usize {
String::from_utf8_lossy(&out.stderr)
.lines()
.filter(|l| l.starts_with("error:"))
.count()
}
#[test]
fn single_error_line_below_hash_fanout_threshold() {
let (td, xdg) = init_repo();
let (root, x) = (td.path(), xdg.path());
write_small_files(root, 2);
write_oversized_sparse_file(&root.join("oversized.bin"));
let out = run_in(root, x, &["add", "."]);
assert!(
!out.status.success(),
"add should fail on the oversized file"
);
assert_eq!(
error_line_count(&out),
1,
"expected exactly one error line, got: {}",
String::from_utf8_lossy(&out.stderr)
);
}
#[test]
fn single_error_line_above_hash_fanout_threshold() {
let (td, xdg) = init_repo();
let (root, x) = (td.path(), xdg.path());
write_small_files(root, above_fanout_threshold_file_count());
write_oversized_sparse_file(&root.join("oversized.bin"));
let out = run_in(root, x, &["add", "."]);
assert!(
!out.status.success(),
"add should fail on the oversized file"
);
assert_eq!(
error_line_count(&out),
1,
"expected exactly one error line, got: {}",
String::from_utf8_lossy(&out.stderr)
);
}
#[test]
fn happy_path_above_hash_fanout_threshold_stages_and_commits() {
let (td, xdg) = init_repo();
let (root, x) = (td.path(), xdg.path());
let n = above_fanout_threshold_file_count();
write_small_files(root, n);
let add_out = run_in(root, x, &["add", "."]);
assert!(add_out.status.success(), "add failed: {add_out:?}");
let commit_out = run_in(root, x, &["commit", "-m", "bulk"]);
assert!(commit_out.status.success(), "commit failed: {commit_out:?}");
let status_out = run_in(root, x, &["status"]);
assert!(status_out.status.success());
assert!(
String::from_utf8_lossy(&status_out.stderr).contains("nothing to commit"),
"expected clean status after committing all {n} files, got: {}",
String::from_utf8_lossy(&status_out.stderr)
);
}