use std::process::Command; use tempfile::TempDir; fn arc_cmd() -> Command { let mut cmd = Command::new(env!("CARGO_BIN_EXE_arc")); cmd.env("NO_COLOR", "1"); cmd } fn init_repo() -> TempDir { let dir = TempDir::new().unwrap(); arc_cmd() .arg("init") .current_dir(dir.path()) .output() .expect("failed to init"); dir } #[test] fn ignore_matches_arc_directory() { let dir = init_repo(); std::fs::write(dir.path().join("real.txt"), "data\n").unwrap(); let output = arc_cmd() .arg("status") .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(!stdout.contains(".arc")); assert!(stdout.contains("real.txt")); } #[test] fn ignore_directory_pattern() { let dir = init_repo(); std::fs::write(dir.path().join(".arcignore"), "build/\n").unwrap(); std::fs::create_dir_all(dir.path().join("build")).unwrap(); std::fs::write(dir.path().join("build/output.bin"), "binary\n").unwrap(); std::fs::write(dir.path().join("src.txt"), "source\n").unwrap(); let output = arc_cmd() .arg("status") .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(!stdout.contains("output.bin")); assert!(stdout.contains("src.txt")); } #[test] fn ignore_wildcard_pattern() { let dir = init_repo(); std::fs::write(dir.path().join(".arcignore"), "*.o\n").unwrap(); std::fs::write(dir.path().join("main.o"), "object\n").unwrap(); std::fs::write(dir.path().join("main.c"), "source\n").unwrap(); let output = arc_cmd() .arg("status") .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(!stdout.contains("main.o")); assert!(stdout.contains("main.c")); } #[test] fn negation_pattern_overrides_ignore() { let dir = init_repo(); std::fs::write(dir.path().join(".arcignore"), "*.log\n!important.log\n").unwrap(); std::fs::write(dir.path().join("debug.log"), "debug\n").unwrap(); std::fs::write(dir.path().join("important.log"), "keep\n").unwrap(); let output = arc_cmd() .arg("status") .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(!stdout.contains("debug.log")); assert!(stdout.contains("important.log")); } #[test] fn state_reconstruction_multi_commit() { let dir = init_repo(); std::fs::write(dir.path().join("a.txt"), "v1\n").unwrap(); arc_cmd() .args(["commit", "c1"]) .current_dir(dir.path()) .output() .expect("failed"); std::fs::write(dir.path().join("b.txt"), "b\n").unwrap(); arc_cmd() .args(["commit", "c2"]) .current_dir(dir.path()) .output() .expect("failed"); std::fs::write(dir.path().join("a.txt"), "v2\n").unwrap(); arc_cmd() .args(["commit", "c3"]) .current_dir(dir.path()) .output() .expect("failed"); let output = arc_cmd() .arg("status") .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(stdout.contains("working tree clean")); } #[test] fn state_reconstruction_with_delete() { let dir = init_repo(); std::fs::write(dir.path().join("a.txt"), "a\n").unwrap(); std::fs::write(dir.path().join("b.txt"), "b\n").unwrap(); arc_cmd() .args(["commit", "c1"]) .current_dir(dir.path()) .output() .expect("failed"); std::fs::remove_file(dir.path().join("b.txt")).unwrap(); arc_cmd() .args(["commit", "c2"]) .current_dir(dir.path()) .output() .expect("failed"); let output = arc_cmd() .arg("status") .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(stdout.contains("working tree clean")); } #[test] fn binary_file_commit_and_status() { let dir = init_repo(); std::fs::write(dir.path().join("binary.bin"), [0u8, 1, 2, 255, 254, 253]).unwrap(); let output = arc_cmd() .args(["commit", "add binary"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let status = arc_cmd() .arg("status") .current_dir(dir.path()) .output() .expect("failed"); assert!(status.status.success()); let stdout = String::from_utf8_lossy(&status.stdout); assert!(stdout.contains("working tree clean")); } #[test] fn zstd_compressed_storage() { let dir = init_repo(); std::fs::write(dir.path().join("file.txt"), "content\n").unwrap(); let output = arc_cmd() .args(["commit", "test zstd"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let commits_dir = dir.path().join(".arc").join("commits"); let entries: Vec<_> = std::fs::read_dir(&commits_dir) .unwrap() .filter_map(|e| e.ok()) .collect(); assert_eq!(entries.len(), 1); let entry = &entries[0]; let name = entry.file_name().to_string_lossy().to_string(); assert!( name.ends_with(".zst"), "commit file should have .zst extension" ); let bytes = std::fs::read(entry.path()).unwrap(); assert_eq!( &bytes[0..4], &[0x28, 0xB5, 0x2F, 0xFD], "should have zstd magic bytes" ); }