mod common;
use std::process::ExitCode;
use common::{
add_local_remote, git_cmd, git_push_to_remote, git_set_remote_head, run_cursus,
temp_real_git_repo,
};
use tempfile::TempDir;
fn setup_verify_repo() -> (TempDir, TempDir) {
let working = temp_real_git_repo();
let dir = working.path();
let remote = add_local_remote(dir);
git_push_to_remote(dir);
git_set_remote_head(dir, "main");
git_cmd(dir, &["checkout", "-b", "feature/my-change"]);
(working, remote)
}
#[tokio::test]
async fn verify_exits_0_when_changeset_added() {
let (working, _remote) = setup_verify_repo();
let dir = working.path();
std::fs::create_dir_all(dir.join(".cursus")).unwrap();
std::fs::write(
dir.join(".cursus/test.md"),
"+++\nmy-app = \"minor\"\n+++\n\nA change\n",
)
.unwrap();
git_cmd(dir, &["add", ".cursus/test.md"]);
git_cmd(dir, &["commit", "-m", "feat: add changeset"]);
let result = run_cursus(["cursus", "--no-interactive", "verify"], dir)
.await
.unwrap();
assert_eq!(result, ExitCode::SUCCESS);
}
#[tokio::test]
async fn verify_exits_2_when_no_changeset() {
let (working, _remote) = setup_verify_repo();
let dir = working.path();
std::fs::write(dir.join("some-file.txt"), "hello").unwrap();
git_cmd(dir, &["add", "some-file.txt"]);
git_cmd(dir, &["commit", "-m", "feat: add non-changeset file"]);
let result = run_cursus(["cursus", "--no-interactive", "verify"], dir)
.await
.unwrap();
assert_eq!(result, ExitCode::from(2));
}
#[tokio::test]
async fn verify_exits_2_on_empty_branch() {
let (working, _remote) = setup_verify_repo();
let dir = working.path();
let result = run_cursus(["cursus", "--no-interactive", "verify"], dir)
.await
.unwrap();
assert_eq!(result, ExitCode::from(2));
}
#[tokio::test]
async fn verify_ignores_readme_md() {
let (working, _remote) = setup_verify_repo();
let dir = working.path();
std::fs::create_dir_all(dir.join(".cursus")).unwrap();
std::fs::write(dir.join(".cursus/README.md"), "# Changesets\n").unwrap();
git_cmd(dir, &["add", ".cursus/README.md"]);
git_cmd(dir, &["commit", "-m", "docs: add README to .cursus"]);
let result = run_cursus(["cursus", "--no-interactive", "verify"], dir)
.await
.unwrap();
assert_eq!(result, ExitCode::from(2));
}
#[tokio::test]
async fn verify_ignores_readme_case_insensitive() {
let (working, _remote) = setup_verify_repo();
let dir = working.path();
std::fs::create_dir_all(dir.join(".cursus")).unwrap();
std::fs::write(dir.join(".cursus/Readme.md"), "# Changesets\n").unwrap();
git_cmd(dir, &["add", ".cursus/Readme.md"]);
git_cmd(dir, &["commit", "-m", "docs: add readme variant"]);
let result = run_cursus(["cursus", "--no-interactive", "verify"], dir)
.await
.unwrap();
assert_eq!(result, ExitCode::from(2));
}
#[tokio::test]
async fn verify_custom_base_ref() {
let (working, _remote) = setup_verify_repo();
let dir = working.path();
std::fs::create_dir_all(dir.join(".cursus")).unwrap();
std::fs::write(
dir.join(".cursus/my-change.md"),
"+++\nmy-app = \"patch\"\n+++\n\n",
)
.unwrap();
git_cmd(dir, &["add", ".cursus/my-change.md"]);
git_cmd(dir, &["commit", "-m", "feat: add changeset"]);
let result = run_cursus(
["cursus", "--no-interactive", "verify", "--base", "main"],
dir,
)
.await
.unwrap();
assert_eq!(result, ExitCode::SUCCESS);
}
#[tokio::test]
async fn verify_error_on_invalid_base_ref() {
let (working, _remote) = setup_verify_repo();
let dir = working.path();
let result = run_cursus(
[
"cursus",
"--no-interactive",
"verify",
"--base",
"nonexistent-ref",
],
dir,
)
.await;
assert!(result.is_err());
}
#[tokio::test]
async fn verify_ignores_modified_changesets() {
let working = temp_real_git_repo();
let dir = working.path();
std::fs::create_dir_all(dir.join(".cursus")).unwrap();
std::fs::write(
dir.join(".cursus/existing.md"),
"+++\nmy-app = \"patch\"\n+++\n\n",
)
.unwrap();
git_cmd(dir, &["add", ".cursus/existing.md"]);
git_cmd(dir, &["commit", "-m", "feat: existing changeset on main"]);
let _remote = add_local_remote(dir);
git_push_to_remote(dir);
git_set_remote_head(dir, "main");
git_cmd(dir, &["checkout", "-b", "feature/modify-only"]);
std::fs::write(
dir.join(".cursus/existing.md"),
"+++\nmy-app = \"minor\"\n+++\n\nModified\n",
)
.unwrap();
git_cmd(dir, &["add", ".cursus/existing.md"]);
git_cmd(dir, &["commit", "-m", "feat: modify changeset"]);
let result = run_cursus(
["cursus", "--no-interactive", "verify", "--base", "main"],
dir,
)
.await
.unwrap();
assert_eq!(result, ExitCode::from(2));
}
#[tokio::test]
async fn verify_works_without_config() {
let (working, _remote) = setup_verify_repo();
let dir = working.path();
std::fs::create_dir_all(dir.join(".cursus")).unwrap();
std::fs::write(
dir.join(".cursus/change.md"),
"+++\nmy-app = \"minor\"\n+++\n\n",
)
.unwrap();
git_cmd(dir, &["add", ".cursus/change.md"]);
git_cmd(dir, &["commit", "-m", "feat: add changeset"]);
let result = run_cursus(["cursus", "--no-interactive", "verify"], dir)
.await
.unwrap();
assert_eq!(result, ExitCode::SUCCESS);
}
#[tokio::test]
async fn verify_dry_run_behaves_identically() {
let (working, _remote) = setup_verify_repo();
let dir = working.path();
std::fs::create_dir_all(dir.join(".cursus")).unwrap();
std::fs::write(
dir.join(".cursus/change.md"),
"+++\nmy-app = \"minor\"\n+++\n\n",
)
.unwrap();
git_cmd(dir, &["add", ".cursus/change.md"]);
git_cmd(dir, &["commit", "-m", "feat: add changeset"]);
let result_normal = run_cursus(["cursus", "--no-interactive", "verify"], dir)
.await
.unwrap();
let result_dry = run_cursus(["cursus", "--no-interactive", "--dry-run", "verify"], dir)
.await
.unwrap();
assert_eq!(result_normal, result_dry);
}
#[tokio::test]
async fn verify_exits_2_when_on_default_branch() {
let working = temp_real_git_repo();
let dir = working.path();
let _remote = add_local_remote(dir);
git_push_to_remote(dir);
git_set_remote_head(dir, "main");
let result = run_cursus(["cursus", "--no-interactive", "verify"], dir)
.await
.unwrap();
assert_eq!(result, ExitCode::from(2));
}
#[tokio::test]
async fn verify_lists_multiple_changesets() {
let (working, _remote) = setup_verify_repo();
let dir = working.path();
std::fs::create_dir_all(dir.join(".cursus")).unwrap();
std::fs::write(
dir.join(".cursus/change-a.md"),
"+++\nmy-app = \"minor\"\n+++\n\n",
)
.unwrap();
std::fs::write(
dir.join(".cursus/change-b.md"),
"+++\nmy-lib = \"patch\"\n+++\n\n",
)
.unwrap();
git_cmd(dir, &["add", ".cursus/change-a.md", ".cursus/change-b.md"]);
git_cmd(dir, &["commit", "-m", "feat: add two changesets"]);
let result = run_cursus(["cursus", "--no-interactive", "verify"], dir)
.await
.unwrap();
assert_eq!(result, ExitCode::SUCCESS);
}