use assert_cmd::cargo::cargo_bin_cmd;
use predicates::prelude::*;
const HELP_BANNER_LINE: &str =
" ██████╗ ██████╗ ████████╗ ██████╗ ██████╗ ██╗ ██╗ ███████╗";
#[test]
fn help_exits_successfully() {
cargo_bin_cmd!("or")
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains(HELP_BANNER_LINE));
}
#[test]
fn version_exits_successfully() {
cargo_bin_cmd!("or")
.arg("--version")
.assert()
.success()
.stdout(predicate::str::starts_with("or "));
}
#[test]
fn init_help_exits_successfully() {
cargo_bin_cmd!("or")
.args(["init", "--help"])
.assert()
.success();
}
#[test]
fn no_args_does_not_print_help() {
let output = cargo_bin_cmd!("or")
.timeout(std::time::Duration::from_secs(3))
.output()
.expect("failed to execute");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
!stdout.contains("Usage"),
"no-args should enter Cockpit, not print help"
);
}
#[test]
fn invalid_repo_exits_with_error() {
cargo_bin_cmd!("or")
.args(["--repo", "invalid/nonexistent-repo-12345", "--pr", "1"])
.assert()
.failure();
}
#[test]
fn pr_flag_only_enters_pr_list() {
cargo_bin_cmd!("or")
.args(["--repo", "invalid/nonexistent-repo-12345", "--pr"])
.assert()
.failure()
.stdout(predicate::str::contains("Usage").not());
}
#[test]
fn pr_short_flag_only_enters_pr_list() {
cargo_bin_cmd!("or")
.args(["--repo", "invalid/nonexistent-repo-12345", "-p"])
.assert()
.failure()
.stdout(predicate::str::contains("Usage").not());
}
#[test]
fn issue_flag_only_enters_issue_list() {
cargo_bin_cmd!("or")
.args(["--repo", "invalid/nonexistent-repo-12345", "--issue"])
.assert()
.failure()
.stdout(predicate::str::contains("Usage").not());
}
#[test]
fn issue_short_flag_only_enters_issue_list() {
cargo_bin_cmd!("or")
.args(["--repo", "invalid/nonexistent-repo-12345", "-i"])
.assert()
.failure()
.stdout(predicate::str::contains("Usage").not());
}
#[test]
fn update_local_comment_missing_id_exits_non_zero() {
let tmp = tempfile::tempdir().expect("create tempdir");
cargo_bin_cmd!("or")
.args([
"update-local-comment",
"--repo",
"owner/repo",
"--working-dir",
tmp.path().to_str().unwrap(),
"--resolve",
"999",
])
.env("XDG_CACHE_HOME", tmp.path())
.assert()
.failure()
.stdout(predicate::str::contains("Missing IDs: 999"))
.stderr(predicate::str::contains("unknown local comment ID"));
}
#[test]
fn local_comments_purge_removes_file_and_reports_count() {
let tmp = tempfile::tempdir().expect("create tempdir");
let workdir = tmp.path().join("worktree");
std::fs::create_dir_all(&workdir).unwrap();
let comments_dir = tmp.path().join("octorus").join("local-comments");
std::fs::create_dir_all(&comments_dir).unwrap();
let mut hasher = std::collections::hash_map::DefaultHasher::new();
std::hash::Hash::hash(&workdir.to_string_lossy().as_ref(), &mut hasher);
let workdir_hash = std::hash::Hasher::finish(&hasher);
let path = comments_dir.join(format!("owner_repo-{:016x}.json", workdir_hash));
std::fs::write(
&path,
r#"{"version":1,"comments":[{"id":1,"path":"a.rs","line":1,"body":"x","user":{"login":"u"},"created_at":"2026-04-27T00:00:00Z"}]}"#,
)
.unwrap();
cargo_bin_cmd!("or")
.args([
"local-comments",
"--repo",
"owner/repo",
"--working-dir",
workdir.to_str().unwrap(),
"--purge",
])
.env("XDG_CACHE_HOME", tmp.path())
.assert()
.success()
.stdout(predicate::str::contains("Purged 1 local comment"));
assert!(!path.exists());
}
#[test]
fn local_comments_purge_with_no_file_reports_zero() {
let tmp = tempfile::tempdir().expect("create tempdir");
cargo_bin_cmd!("or")
.args([
"local-comments",
"--repo",
"owner/repo",
"--working-dir",
tmp.path().to_str().unwrap(),
"--purge",
])
.env("XDG_CACHE_HOME", tmp.path())
.assert()
.success()
.stdout(predicate::str::contains("Purged 0 local comments"));
}