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 } fn commit_file(dir: &TempDir, name: &str, content: &str, msg: &str) -> String { std::fs::write(dir.path().join(name), content).unwrap(); let output = arc_cmd() .args(["commit", msg]) .current_dir(dir.path()) .output() .expect("failed to commit"); assert!(output.status.success()); String::from_utf8_lossy(&output.stdout) .trim() .strip_prefix("✓ committed ") .unwrap() .to_string() } #[test] fn mark_add_creates_bookmark() { let dir = init_repo(); commit_file(&dir, "a.txt", "hello\n", "first"); let output = arc_cmd() .args(["mark", "add", "feature"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(stdout.contains("bookmark feature set at")); let bookmark_path = dir.path().join(".arc").join("bookmarks").join("feature"); assert!(bookmark_path.exists()); } #[test] fn mark_add_at_specific_commit() { let dir = init_repo(); let id1 = commit_file(&dir, "a.txt", "v1\n", "first"); commit_file(&dir, "a.txt", "v2\n", "second"); let short = &id1[..12]; let output = arc_cmd() .args(["mark", "add", "old", short]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(stdout.contains("bookmark old set at")); } #[test] fn mark_add_fails_without_commits() { let dir = init_repo(); let output = arc_cmd() .args(["mark", "add", "feature"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("no commits yet")); } #[test] fn mark_rm_removes_bookmark() { let dir = init_repo(); commit_file(&dir, "a.txt", "hello\n", "first"); arc_cmd() .args(["mark", "add", "feature"]) .current_dir(dir.path()) .output() .expect("failed"); let output = arc_cmd() .args(["mark", "rm", "feature"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(stdout.contains("bookmark feature removed")); let bookmark_path = dir.path().join(".arc").join("bookmarks").join("feature"); assert!(!bookmark_path.exists()); } #[test] fn mark_rm_fails_for_active_bookmark() { let dir = init_repo(); commit_file(&dir, "a.txt", "hello\n", "first"); let output = arc_cmd() .args(["mark", "rm", "main"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("cannot remove active bookmark")); } #[test] fn mark_rm_fails_for_nonexistent() { let dir = init_repo(); commit_file(&dir, "a.txt", "hello\n", "first"); let output = arc_cmd() .args(["mark", "rm", "nonexistent"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("bookmark not found")); } #[test] fn mark_list_shows_bookmarks() { let dir = init_repo(); commit_file(&dir, "a.txt", "hello\n", "first"); arc_cmd() .args(["mark", "add", "feature"]) .current_dir(dir.path()) .output() .expect("failed"); arc_cmd() .args(["mark", "add", "dev"]) .current_dir(dir.path()) .output() .expect("failed"); let output = arc_cmd() .args(["mark", "list"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(stdout.contains("dev")); assert!(stdout.contains("feature")); assert!(stdout.contains("main")); } #[test] fn mark_list_marks_active_bookmark() { let dir = init_repo(); commit_file(&dir, "a.txt", "hello\n", "first"); arc_cmd() .args(["mark", "add", "feature"]) .current_dir(dir.path()) .output() .expect("failed"); let output = arc_cmd() .args(["mark", "list"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(stdout.contains("★ main")); assert!(stdout.contains(" feature")); } #[test] fn mark_list_sorted_alphabetically() { let dir = init_repo(); commit_file(&dir, "a.txt", "hello\n", "first"); arc_cmd() .args(["mark", "add", "zebra"]) .current_dir(dir.path()) .output() .expect("failed"); arc_cmd() .args(["mark", "add", "alpha"]) .current_dir(dir.path()) .output() .expect("failed"); let output = arc_cmd() .args(["mark", "list"]) .current_dir(dir.path()) .output() .expect("failed"); let stdout = String::from_utf8_lossy(&output.stdout); let alpha_pos = stdout.find("alpha").unwrap(); let main_pos = stdout.find("main").unwrap(); let zebra_pos = stdout.find("zebra").unwrap(); assert!(alpha_pos < main_pos); assert!(main_pos < zebra_pos); } #[test] fn mark_rename_renames_bookmark() { let dir = init_repo(); commit_file(&dir, "a.txt", "hello\n", "first"); arc_cmd() .args(["mark", "add", "feature"]) .current_dir(dir.path()) .output() .expect("failed"); let output = arc_cmd() .args(["mark", "rename", "feature", "bugfix"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let stdout = String::from_utf8_lossy(&output.stdout); assert!(stdout.contains("bookmark feature renamed to bugfix")); let old_path = dir.path().join(".arc").join("bookmarks").join("feature"); let new_path = dir.path().join(".arc").join("bookmarks").join("bugfix"); assert!(!old_path.exists()); assert!(new_path.exists()); } #[test] fn mark_rename_updates_head_if_active() { let dir = init_repo(); commit_file(&dir, "a.txt", "hello\n", "first"); let output = arc_cmd() .args(["mark", "rename", "main", "trunk"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(output.status.success()); let head = std::fs::read_to_string(dir.path().join(".arc").join("HEAD")).unwrap(); assert!(head.contains("trunk")); assert!(!head.contains("main")); } #[test] fn mark_rename_fails_if_target_exists() { let dir = init_repo(); commit_file(&dir, "a.txt", "hello\n", "first"); arc_cmd() .args(["mark", "add", "feature"]) .current_dir(dir.path()) .output() .expect("failed"); let output = arc_cmd() .args(["mark", "rename", "feature", "main"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("bookmark already exists")); } #[test] fn mark_rename_fails_if_source_missing() { let dir = init_repo(); commit_file(&dir, "a.txt", "hello\n", "first"); let output = arc_cmd() .args(["mark", "rename", "nonexistent", "newname"]) .current_dir(dir.path()) .output() .expect("failed"); assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("bookmark not found")); } #[test] fn mark_add_overwrites_existing_bookmark() { let dir = init_repo(); let id1 = commit_file(&dir, "a.txt", "v1\n", "first"); let id2 = commit_file(&dir, "a.txt", "v2\n", "second"); arc_cmd() .args(["mark", "add", "feature"]) .current_dir(dir.path()) .output() .expect("failed"); let short1 = &id1[..12]; arc_cmd() .args(["mark", "add", "feature", short1]) .current_dir(dir.path()) .output() .expect("failed"); let bookmark = std::fs::read_to_string(dir.path().join(".arc").join("bookmarks").join("feature")).unwrap(); assert!(bookmark.contains(&id1)); assert!(!bookmark.contains(&id2)); }