arc/tests/mark.rs
hanna 2e0952f9fb
feat: add colored output with distinct symbols using the colored crate
Add a centralized ui module with Arc's visual identity: colored commit
IDs (magenta), bookmarks (cyan), tags (yellow), status symbols, and
diff highlighting. Update all command output and tests accordingly.
2026-02-09 03:51:59 +00:00

334 lines
8.8 KiB
Rust

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));
}