use crate::common::{
BareRepoTest, TestRepo, TestRepoBase, make_snapshot_cmd, repo, setup_snapshot_settings,
setup_temp_snapshot_settings, wt_command,
};
use insta_cmd::assert_cmd_snapshot;
use rstest::rstest;
use worktrunk::config::Approvals;
fn snapshot_add_approvals(test_name: &str, repo: &TestRepo, args: &[&str]) {
let settings = setup_snapshot_settings(repo);
settings.bind(|| {
let mut cmd = make_snapshot_cmd(repo, "hook", &[], None);
cmd.arg("approvals").arg("add").args(args);
assert_cmd_snapshot!(test_name, cmd);
});
}
fn snapshot_clear_approvals(test_name: &str, repo: &TestRepo, args: &[&str]) {
let settings = setup_snapshot_settings(repo);
settings.bind(|| {
let mut cmd = make_snapshot_cmd(repo, "hook", &[], None);
cmd.arg("approvals").arg("clear").args(args);
assert_cmd_snapshot!(test_name, cmd);
});
}
#[rstest]
fn test_add_approvals_no_config(repo: TestRepo) {
snapshot_add_approvals("add_approvals_no_config", &repo, &[]);
}
#[rstest]
fn test_add_approvals_all_with_none_approved(repo: TestRepo) {
repo.write_project_config(r#"post-create = "echo 'test'""#);
repo.commit("Add config");
snapshot_add_approvals("add_approvals_all_none_approved", &repo, &["--all"]);
}
#[rstest]
fn test_add_approvals_empty_config(repo: TestRepo) {
repo.write_project_config("");
repo.commit("Add empty config");
snapshot_add_approvals("add_approvals_empty_config", &repo, &[]);
}
#[rstest]
fn test_clear_approvals_no_approvals(repo: TestRepo) {
snapshot_clear_approvals("clear_approvals_no_approvals", &repo, &[]);
}
#[rstest]
fn test_clear_approvals_with_approvals(repo: TestRepo) {
repo.run_git(&["remote", "remove", "origin"]);
let project_id = format!("{}/origin", repo.root_path().display());
repo.commit("Initial commit");
repo.write_project_config(r#"post-create = "echo 'test'""#);
repo.commit("Add config");
let mut approvals = Approvals::default();
approvals
.approve_command(
project_id,
"echo 'test'".to_string(),
Some(repo.test_approvals_path()),
)
.unwrap();
snapshot_clear_approvals("clear_approvals_with_approvals", &repo, &[]);
}
#[rstest]
fn test_clear_approvals_global_no_approvals(repo: TestRepo) {
snapshot_clear_approvals("clear_approvals_global_no_approvals", &repo, &["--global"]);
}
#[rstest]
fn test_clear_approvals_global_with_approvals(repo: TestRepo) {
repo.run_git(&["remote", "remove", "origin"]);
let project_id = format!("{}/origin", repo.root_path().display());
repo.commit("Initial commit");
repo.write_project_config(r#"post-create = "echo 'test'""#);
repo.commit("Add config");
let mut approvals = Approvals::default();
approvals
.approve_command(
project_id,
"echo 'test'".to_string(),
Some(repo.test_approvals_path()),
)
.unwrap();
snapshot_clear_approvals(
"clear_approvals_global_with_approvals",
&repo,
&["--global"],
);
}
#[rstest]
fn test_clear_approvals_after_clear(repo: TestRepo) {
repo.run_git(&["remote", "remove", "origin"]);
let project_id = format!("{}/origin", repo.root_path().display());
repo.commit("Initial commit");
repo.write_project_config(r#"post-create = "echo 'test'""#);
repo.commit("Add config");
let mut approvals = Approvals::default();
approvals
.approve_command(
project_id.clone(),
"echo 'test'".to_string(),
Some(repo.test_approvals_path()),
)
.unwrap();
let mut cmd = make_snapshot_cmd(&repo, "hook", &[], None);
cmd.arg("approvals").arg("clear");
cmd.output().unwrap();
snapshot_clear_approvals("clear_approvals_after_clear", &repo, &[]);
}
#[rstest]
fn test_clear_approvals_multiple_approvals(repo: TestRepo) {
repo.run_git(&["remote", "remove", "origin"]);
repo.write_project_config(
r#"
post-create = "echo 'first'"
post-start = "echo 'second'"
[pre-commit]
lint = "echo 'third'"
"#,
);
repo.commit("Add config with multiple commands");
let project_id = format!("{}/origin", repo.root_path().display());
let mut approvals = Approvals::default();
approvals
.approve_command(
project_id.clone(),
"echo 'first'".to_string(),
Some(repo.test_approvals_path()),
)
.unwrap();
approvals
.approve_command(
project_id.clone(),
"echo 'second'".to_string(),
Some(repo.test_approvals_path()),
)
.unwrap();
approvals
.approve_command(
project_id,
"echo 'third'".to_string(),
Some(repo.test_approvals_path()),
)
.unwrap();
snapshot_clear_approvals("clear_approvals_multiple_approvals", &repo, &[]);
}
#[rstest]
fn test_add_approvals_all_already_approved(repo: TestRepo) {
let project_id = format!("{}/origin", repo.root_path().display());
repo.commit("Initial commit");
repo.write_project_config(r#"post-create = "echo 'test'""#);
repo.commit("Add config");
let mut approvals = Approvals::default();
approvals
.approve_command(
project_id,
"echo 'test'".to_string(),
Some(repo.test_approvals_path()),
)
.unwrap();
snapshot_add_approvals("add_approvals_all_already_approved", &repo, &[]);
}
#[rstest]
fn test_add_approvals_project_config_no_commands(repo: TestRepo) {
repo.write_project_config(
r#"# Project config without any hook sections
[list]
url = "http://localhost:8080"
"#,
);
repo.commit("Add config without hooks");
snapshot_add_approvals("add_approvals_no_commands", &repo, &[]);
}
#[test]
fn test_add_approvals_bare_repo_config_in_primary_worktree() {
let test = BareRepoTest::new();
let main_worktree = test.create_worktree("main", "main");
test.commit_in(&main_worktree, "Initial commit");
let config_dir = main_worktree.join(".config");
std::fs::create_dir_all(&config_dir).unwrap();
std::fs::write(
config_dir.join("wt.toml"),
r#"post-create = "echo 'hello'"
"#,
)
.unwrap();
let settings = setup_temp_snapshot_settings(test.temp_path());
settings.bind(|| {
let mut cmd = wt_command();
test.configure_wt_cmd(&mut cmd);
cmd.current_dir(&main_worktree)
.args(["hook", "approvals", "add", "--all"]);
assert_cmd_snapshot!("add_approvals_bare_repo_config_in_primary_worktree", cmd);
});
}
#[test]
fn test_config_create_project_bare_repo_no_worktrees_errors() {
let test = BareRepoTest::new();
let mut cmd = wt_command();
test.configure_wt_cmd(&mut cmd);
cmd.current_dir(test.bare_repo_path())
.args(["config", "create", "--project"]);
let output = cmd.output().unwrap();
assert!(
!output.status.success(),
"wt config create --project should fail with no worktrees"
);
let bare_root_config = test.bare_repo_path().join(".config").join("wt.toml");
assert!(
!bare_root_config.exists(),
"Config should NOT be created in bare repo root at {:?}",
bare_root_config
);
}
#[test]
fn test_hook_commands_bare_repo_no_worktrees_errors() {
let test = BareRepoTest::new();
let mut cmd = wt_command();
test.configure_wt_cmd(&mut cmd);
cmd.current_dir(test.bare_repo_path())
.args(["hook", "approvals", "add", "--all"]);
let output = cmd.output().unwrap();
assert!(
!output.status.success(),
"hook approvals add should fail with no worktrees"
);
let mut cmd = wt_command();
test.configure_wt_cmd(&mut cmd);
cmd.current_dir(test.bare_repo_path())
.args(["hook", "show"]);
let output = cmd.output().unwrap();
assert!(
!output.status.success(),
"hook show should fail with no worktrees"
);
}
#[test]
fn test_config_create_project_bare_repo_uses_primary_worktree() {
let test = BareRepoTest::new();
let main_worktree = test.create_worktree("main", "main");
test.commit_in(&main_worktree, "Initial commit");
let mut cmd = wt_command();
test.configure_wt_cmd(&mut cmd);
cmd.current_dir(test.bare_repo_path())
.args(["config", "create", "--project"]);
let output = cmd.output().unwrap();
assert!(
output.status.success(),
"wt config create --project failed:\nstderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let primary_config = main_worktree.join(".config").join("wt.toml");
let bare_root_config = test.bare_repo_path().join(".config").join("wt.toml");
assert!(
primary_config.exists(),
"Config should be created in primary worktree at {:?}",
primary_config
);
assert!(
!bare_root_config.exists(),
"Config should NOT be created in bare repo root at {:?}",
bare_root_config
);
}