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 commit_first_creates_commit() { let dir = init_repo(); std::fs::write(dir.path().join("hello.txt"), "hello world\n").unwrap(); let output = arc_cmd() .args(["commit", "initial commit"]) .current_dir(dir.path()) .output() .expect("failed to run arc"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(stdout.contains("committed")); } #[test] fn commit_creates_zst_file() { let dir = init_repo(); std::fs::write(dir.path().join("file.txt"), "content\n").unwrap(); let output = arc_cmd() .args(["commit", "add file"]) .current_dir(dir.path()) .output() .expect("failed to run arc"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); let commit_id = stdout.trim().strip_prefix("✓ committed ").unwrap(); let commits_dir = dir.path().join(".arc").join("commits"); let found = std::fs::read_dir(&commits_dir) .unwrap() .filter_map(|e| e.ok()) .any(|e| e.file_name().to_string_lossy().starts_with(commit_id)); assert!(found, "commit .zst file should exist"); } #[test] fn commit_updates_head_to_attached() { let dir = init_repo(); std::fs::write(dir.path().join("a.txt"), "a\n").unwrap(); arc_cmd() .args(["commit", "first"]) .current_dir(dir.path()) .output() .expect("failed to run arc"); let head = std::fs::read_to_string(dir.path().join(".arc").join("HEAD")).unwrap(); assert!(head.contains("attached")); assert!(head.contains("main")); } #[test] fn commit_updates_bookmark_ref() { let dir = init_repo(); std::fs::write(dir.path().join("a.txt"), "a\n").unwrap(); let output = arc_cmd() .args(["commit", "first"]) .current_dir(dir.path()) .output() .expect("failed to run arc"); let stdout = String::from_utf8_lossy(&output.stdout); let commit_id = stdout.trim().strip_prefix("✓ committed ").unwrap(); let bookmark = std::fs::read_to_string(dir.path().join(".arc").join("bookmarks").join("main")).unwrap(); assert!(bookmark.contains(commit_id)); } #[test] fn commit_fails_with_no_changes() { let dir = init_repo(); let output = arc_cmd() .args(["commit", "empty"]) .current_dir(dir.path()) .output() .expect("failed to run arc"); assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("nothing to commit")); } #[test] fn commit_nothing_after_same_content() { let dir = init_repo(); std::fs::write(dir.path().join("a.txt"), "hello\n").unwrap(); arc_cmd() .args(["commit", "first"]) .current_dir(dir.path()) .output() .expect("failed to run arc"); let output = arc_cmd() .args(["commit", "second"]) .current_dir(dir.path()) .output() .expect("failed to run arc"); assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("nothing to commit")); } #[test] fn second_commit_has_parent() { let dir = init_repo(); std::fs::write(dir.path().join("a.txt"), "first\n").unwrap(); let first = arc_cmd() .args(["commit", "first"]) .current_dir(dir.path()) .output() .expect("failed"); let first_id = String::from_utf8_lossy(&first.stdout) .trim() .strip_prefix("✓ committed ") .unwrap() .to_string(); std::fs::write(dir.path().join("a.txt"), "second\n").unwrap(); let second = arc_cmd() .args(["commit", "second"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(second.status.success()); let head = std::fs::read_to_string(dir.path().join(".arc").join("HEAD")).unwrap(); let second_id = String::from_utf8_lossy(&second.stdout) .trim() .strip_prefix("✓ committed ") .unwrap() .to_string(); assert!(head.contains(&second_id)); assert_ne!(first_id, second_id); } #[test] fn commit_ignores_arc_directory() { let dir = init_repo(); std::fs::write(dir.path().join("file.txt"), "data\n").unwrap(); let output = arc_cmd() .args(["commit", "test"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); } #[test] fn commit_ignores_arcignore_patterns() { let dir = init_repo(); std::fs::write(dir.path().join(".arcignore"), "*.log\n").unwrap(); std::fs::write(dir.path().join("data.log"), "log data\n").unwrap(); std::fs::write(dir.path().join("keep.txt"), "keep\n").unwrap(); arc_cmd() .args(["commit", "first"]) .current_dir(dir.path()) .output() .expect("failed"); std::fs::remove_file(dir.path().join("data.log")).unwrap(); let output = arc_cmd() .args(["commit", "should fail"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(!output.status.success()); } #[test] fn commit_detects_deletions() { 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", "add files"]) .current_dir(dir.path()) .output() .expect("failed"); std::fs::remove_file(dir.path().join("b.txt")).unwrap(); let output = arc_cmd() .args(["commit", "delete b"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); } #[test] fn commit_detects_modifications() { let dir = init_repo(); std::fs::write(dir.path().join("a.txt"), "original\n").unwrap(); arc_cmd() .args(["commit", "add file"]) .current_dir(dir.path()) .output() .expect("failed"); std::fs::write(dir.path().join("a.txt"), "modified\n").unwrap(); let output = arc_cmd() .args(["commit", "modify file"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); } #[test] fn commit_outside_repo_fails() { let dir = TempDir::new().unwrap(); let output = arc_cmd() .args(["commit", "fail"]) .current_dir(dir.path()) .output() .expect("failed to run arc"); assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("not an arc repository")); } #[test] fn commit_with_subdirectories() { let dir = init_repo(); std::fs::create_dir_all(dir.path().join("src").join("nested")).unwrap(); std::fs::write(dir.path().join("src/nested/deep.txt"), "deep\n").unwrap(); std::fs::write(dir.path().join("root.txt"), "root\n").unwrap(); let output = arc_cmd() .args(["commit", "nested dirs"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); } #[test] fn three_sequential_commits_succeed() { let dir = init_repo(); std::fs::write(dir.path().join("a.txt"), "v1\n").unwrap(); let c1 = arc_cmd() .args(["commit", "c1"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(c1.status.success()); std::fs::write(dir.path().join("b.txt"), "v1\n").unwrap(); let c2 = arc_cmd() .args(["commit", "c2"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(c2.status.success()); std::fs::write(dir.path().join("a.txt"), "v2\n").unwrap(); let c3 = arc_cmd() .args(["commit", "c3"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(c3.status.success()); }