use super::RequireRootExemption::{ConfigScope, NoCommand, Subcommand};
use super::{
NOT_AT_ROOT_CODE, RequireRootDecision, RequireRootFacts, RequireRootVerdict,
RequireRootViolation, decide_require_root, resolve_require_root,
};
use crate::command_test_support::{fixture, git, os_arguments, remove, repository};
use std::path::PathBuf;
use std::process::Output;
const HATCH: &[u8] = b"--no-enforce-require-root";
fn decide(line: &str) -> RequireRootDecision {
let values: Vec<&str> = line.split_whitespace().collect();
return decide_require_root(os_arguments(values.as_slice()).as_slice(), &[HATCH]);
}
fn facts(directory: &str, root: Option<&str>) -> RequireRootFacts {
let mut repository_root: Option<PathBuf> = None;
if let Some(found) = root {
repository_root = Some(PathBuf::from(found));
}
return RequireRootFacts {
effective_directory: PathBuf::from(directory),
repository_root,
};
}
fn violation(root: &str, directory: &str) -> RequireRootVerdict {
return RequireRootVerdict::NotAtRoot(RequireRootViolation {
message: format!(
"cli-git: not at the root of the git repository. Repo root is {root} but effective cwd is {directory}. Tip: cd to {root} or pass -C {root} before the subcommand."
),
});
}
#[test]
fn ports_the_incumbent_cases() {
assert_eq!(
decide("-C /tmp/x status"),
RequireRootDecision::NeedsRepositoryRoot
);
assert_eq!(
resolve_require_root(&facts("/tmp/x", None)),
RequireRootVerdict::Pass
);
assert_eq!(
resolve_require_root(&facts("/r", Some("/r"))),
RequireRootVerdict::Pass
);
assert_eq!(
resolve_require_root(&facts("/r/subdir", Some("/r"))),
violation("/r", "/r/subdir")
);
assert_eq!(
decide("-C /r/subdir clone https://example.invalid/repo.git"),
RequireRootDecision::Exempt(Subcommand)
);
assert_eq!(
decide("-C /r/subdir config --global user.name"),
RequireRootDecision::Exempt(ConfigScope)
);
assert_eq!(NOT_AT_ROOT_CODE, "not-at-root");
}
#[test]
fn exempts_commands_that_need_no_repository() {
for line in [
"init",
"clone x",
"version",
"help",
"-C sub init",
"-c a.b=c --no-pager help status",
] {
assert_eq!(
decide(line),
RequireRootDecision::Exempt(Subcommand),
"{line}"
);
}
for line in [
"status",
"initialize",
"Init",
"-C sub status",
"commit -m init",
"x-help",
] {
assert_eq!(
decide(line),
RequireRootDecision::NeedsRepositoryRoot,
"{line}"
);
}
}
#[test]
fn exempts_arguments_that_run_no_subcommand() {
for line in [
"",
"-C sub",
"--version status",
"-v",
"--help status",
"--exec-path",
"-C",
"--bogus status",
"--list-cmds=main",
] {
assert_eq!(
decide(line),
RequireRootDecision::Exempt(NoCommand),
"{line}"
);
}
}
#[test]
fn exempts_config_by_its_file_scope() {
for line in [
"config --global user.name",
"config --system user.name",
"config --list",
"config -l",
"config -lz",
"config --local --list",
"config list",
"config list --show-origin",
"config get --global user.name",
"config set --system a.b c",
"config -- --global user.name",
"config --no-enforce-require-root --global user.name",
"config --no-enforce-require-root list",
"-C sub config --glob user.name",
] {
assert_eq!(
decide(line),
RequireRootDecision::Exempt(ConfigScope),
"{line}"
);
}
for line in [
"config user.name",
"config user.name --global",
"config set user.name --system",
"config --no-enforce-require-root set user.name --global",
"config -f --global user.name",
"config --global --no-global user.name",
"config get user.name",
"config --bogus --global",
"config --global=1",
"config edit",
] {
assert_eq!(
decide(line),
RequireRootDecision::NeedsRepositoryRoot,
"{line}"
);
}
let refused: RequireRootDecision = decide_require_root(
os_arguments(&["config", "--no-enforce-require-root", "--global", "a"]).as_slice(),
&[],
);
assert_eq!(refused, RequireRootDecision::NeedsRepositoryRoot);
}
#[test]
fn compares_paths_by_component() {
assert_eq!(
resolve_require_root(&facts("/r/", Some("/r"))),
RequireRootVerdict::Pass
);
assert_eq!(
resolve_require_root(&facts("/r//", Some("/r"))),
RequireRootVerdict::Pass
);
assert_eq!(
resolve_require_root(&facts("/r/..", Some("/r"))),
violation("/r", "/r/..")
);
assert_eq!(
resolve_require_root(&facts("/", Some("/r"))),
violation("/r", "/")
);
}
#[cfg(unix)]
#[test]
fn reports_a_non_utf8_directory() {
use crate::command_test_support::byte_argument;
let directory: PathBuf = PathBuf::from(byte_argument(b"/r/sub\xff"));
let verdict: RequireRootVerdict = resolve_require_root(&RequireRootFacts {
effective_directory: directory,
repository_root: Some(PathBuf::from("/r")),
});
assert_eq!(verdict, violation("/r", "/r/sub\u{fffd}"));
}
#[test]
fn config_list_spellings_match_git() {
let directory: PathBuf = fixture("require-root-config-list");
let root: PathBuf = repository(directory.as_path(), "repository");
git(root.as_path(), &["config", "fixture.value", "one"]);
let subcommand: Output = git(root.as_path(), &["config", "list"]);
let option: Output = git(root.as_path(), &["config", "--list"]);
let short: Output = git(root.as_path(), &["config", "-l"]);
assert!(String::from_utf8_lossy(&subcommand.stdout).contains("fixture.value=one"));
assert_eq!(subcommand.stdout, option.stdout);
assert_eq!(subcommand.stdout, short.stdout);
remove(directory.as_path());
}