#![allow(clippy::unwrap_used)]
use std::fs;
use std::path::Path;
use std::process::{Command, Output};
fn mkit_bin() -> &'static str {
env!("CARGO_BIN_EXE_mkit")
}
fn run_in(cwd: &Path, args: &[&str]) -> Output {
let xdg = tempfile::tempdir().expect("xdg tempdir");
let out = Command::new(mkit_bin())
.args(args)
.current_dir(cwd)
.env("XDG_CONFIG_HOME", xdg.path())
.output()
.expect("spawn mkit");
drop(xdg);
out
}
fn run_in_with_xdg(cwd: &Path, xdg: &Path, args: &[&str]) -> Output {
Command::new(mkit_bin())
.args(args)
.current_dir(cwd)
.env("XDG_CONFIG_HOME", xdg)
.output()
.expect("spawn mkit")
}
fn stdout_str(out: &Output) -> String {
String::from_utf8(out.stdout.clone()).unwrap()
}
fn stderr_str(out: &Output) -> String {
String::from_utf8(out.stderr.clone()).unwrap()
}
fn init_repo() -> tempfile::TempDir {
let td = tempfile::tempdir().unwrap();
assert!(run_in(td.path(), &["init"]).status.success());
td
}
#[test]
fn unset_removes_a_repo_scoped_key() {
let td = init_repo();
assert!(
run_in(td.path(), &["config", "user.email", "a@example.com"])
.status
.success()
);
let show = run_in(td.path(), &["config", "user.email"]);
assert_eq!(stdout_str(&show).trim(), "a@example.com");
let unset = run_in(td.path(), &["config", "--unset", "user.email"]);
assert!(unset.status.success(), "unset failed: {unset:?}");
let show = run_in(td.path(), &["config", "user.email"]);
assert_eq!(
stdout_str(&show).trim(),
"",
"user.email should be empty after --unset"
);
let cfg = fs::read_to_string(td.path().join(".mkit/config")).unwrap_or_default();
assert!(
!cfg.contains("user.email"),
".mkit/config still contains user.email: {cfg}"
);
}
#[test]
fn unset_removes_a_user_scoped_forbidden_key() {
let td = init_repo();
let xdg = tempfile::tempdir().unwrap();
assert!(
run_in_with_xdg(
td.path(),
xdg.path(),
&["config", "user.identity", "mid:42"]
)
.status
.success()
);
let show = run_in_with_xdg(td.path(), xdg.path(), &["config", "user.identity"]);
assert!(
!stdout_str(&show).trim().is_empty(),
"user.identity should be set before unset"
);
let unset = run_in_with_xdg(
td.path(),
xdg.path(),
&["config", "--unset", "user.identity"],
);
assert!(unset.status.success(), "unset failed: {unset:?}");
let show = run_in_with_xdg(td.path(), xdg.path(), &["config", "user.identity"]);
assert_eq!(
stdout_str(&show).trim(),
"",
"user.identity should be empty after --unset"
);
let user_cfg = fs::read_to_string(xdg.path().join("mkit/config")).unwrap_or_default();
assert!(
!user_cfg.contains("user.identity"),
"user-scoped config still contains user.identity: {user_cfg}"
);
let repo_cfg = fs::read_to_string(td.path().join(".mkit/config")).unwrap_or_default();
assert!(!repo_cfg.contains("user.identity"));
}
#[test]
fn unset_of_an_already_unset_key_is_idempotent() {
let td = init_repo();
let out = run_in(td.path(), &["config", "--unset", "default_branch"]);
assert!(
out.status.success(),
"unsetting an absent key should succeed: {out:?}"
);
}
#[test]
fn unset_of_unknown_key_errors() {
let td = init_repo();
let out = run_in(td.path(), &["config", "--unset", "not.a.real.key"]);
assert!(!out.status.success(), "unknown key must be rejected");
let stderr = stderr_str(&out);
assert!(
stderr.contains("unknown config key"),
"expected unknown-key message: {stderr}"
);
}
#[test]
fn unset_rejects_positional_arguments() {
let td = init_repo();
let out = run_in(td.path(), &["config", "--unset", "default_branch", "extra"]);
assert!(!out.status.success());
}
#[test]
fn local_refuses_a_forbidden_key_on_set_and_unset() {
let td = init_repo();
let set = run_in(td.path(), &["config", "--local", "user.identity", "mid:7"]);
assert!(
!set.status.success(),
"--local set of forbidden key must fail"
);
let unset = run_in(
td.path(),
&["config", "--local", "--unset", "user.identity"],
);
assert!(
!unset.status.success(),
"--local unset of forbidden key must fail"
);
}
#[test]
fn global_forces_a_repo_safe_key_to_user_scope() {
let td = init_repo();
let xdg = tempfile::tempdir().unwrap();
let set = run_in_with_xdg(
td.path(),
xdg.path(),
&["config", "--global", "default_branch", "trunk"],
);
assert!(set.status.success(), "--global set failed: {set:?}");
let repo_cfg = fs::read_to_string(td.path().join(".mkit/config")).unwrap_or_default();
assert!(
!repo_cfg.contains("default_branch"),
"repo config should not contain default_branch when --global forced user scope: {repo_cfg}"
);
let user_cfg = fs::read_to_string(xdg.path().join("mkit/config")).unwrap_or_default();
assert!(
user_cfg.contains("default_branch"),
"user-scoped config should contain default_branch: {user_cfg}"
);
let show = run_in_with_xdg(td.path(), xdg.path(), &["config", "default_branch"]);
assert_eq!(stdout_str(&show).trim(), "trunk");
let unset = run_in_with_xdg(
td.path(),
xdg.path(),
&["config", "--global", "--unset", "default_branch"],
);
assert!(unset.status.success(), "--global unset failed: {unset:?}");
let user_cfg = fs::read_to_string(xdg.path().join("mkit/config")).unwrap_or_default();
assert!(!user_cfg.contains("default_branch"));
}
#[test]
fn local_and_global_are_mutually_exclusive() {
let td = init_repo();
let out = run_in(
td.path(),
&["config", "--local", "--global", "default_branch", "trunk"],
);
assert!(!out.status.success());
}