mod support;
use std::fs;
use grit_lib::config::environment::Environment;
use grit_lib::config::{ConfigScope, ConfigSet, IncludeContext};
use grit_lib::error::{ConfigError, Error};
use grit_lib::repo::{InitOptions, Repository};
use support::{grit_file_from_content, isolated_env};
use tempfile::tempdir;
fn ctx(git_dir: &std::path::Path, env: &Environment) -> IncludeContext {
IncludeContext {
git_dir: Some(git_dir.to_path_buf()),
cwd: env.cwd.clone(),
pwd: env.pwd.clone(),
env: std::sync::Arc::new(env.clone()),
..Default::default()
}
}
#[test]
fn includeif_onbranch_matches_current_branch() {
let dir = tempdir().expect("tempdir");
Repository::init(
dir.path(),
&InitOptions::new()
.with_initial_branch("feature/x".to_owned())
.with_ref_storage(grit_lib::RefStorageFormat::Files),
)
.expect("init");
let git_dir = dir.path().join(".git");
let mut cfg = fs::read_to_string(git_dir.join("config")).expect("read");
cfg.push_str("[includeIf \"onbranch:feature/x\"]\n\tpath = branch.conf\n");
fs::write(git_dir.join("config"), &cfg).expect("write cfg");
fs::write(git_dir.join("branch.conf"), "[hit]\n\tk = yes\n").expect("branch");
let env = Environment::empty();
let set = ConfigSet::load_with_options(
&env,
Some(&git_dir),
&support::default_load_opts(&git_dir, &env),
)
.expect("load");
assert_eq!(set.get("hit.k").as_deref(), Some("yes"));
}
#[test]
fn includeif_hasconfig_remote_url() {
let dir = tempdir().expect("tempdir");
Repository::init(
dir.path(),
&InitOptions::new()
.with_initial_branch("main".to_owned())
.with_ref_storage(grit_lib::RefStorageFormat::Files),
)
.expect("init");
let git_dir = dir.path().join(".git");
let mut cfg = fs::read_to_string(git_dir.join("config")).expect("read");
cfg.push_str(
"[remote \"origin\"]\n\turl = https://example.com/proj.git\n\
[includeIf \"hasconfig:remote.*.url:https://example.com/*\"]\n\tpath = remote.conf\n",
);
fs::write(git_dir.join("config"), &cfg).expect("write cfg");
fs::write(git_dir.join("remote.conf"), "[fromremote]\n\tk = 1\n").expect("remote inc");
let env = Environment::empty();
let set = ConfigSet::load_with_options(
&env,
Some(&git_dir),
&support::default_load_opts(&git_dir, &env),
)
.expect("load");
assert_eq!(set.get("fromremote.k").as_deref(), Some("1"));
}
#[test]
fn hasconfig_include_rejects_remote_url_in_included_file() {
let dir = tempdir().expect("tempdir");
let main = dir.path().join("main.conf");
fs::write(
&main,
"[includeIf \"hasconfig:remote.*.url:https://x/*\"]\n\tpath = bad.conf\n",
)
.expect("main");
fs::write(
dir.path().join("bad.conf"),
"[remote \"r\"]\n\turl = https://evil.example/x\n",
)
.expect("bad");
let content = fs::read_to_string(&main).expect("read");
let file = grit_file_from_content(&main, &content, ConfigScope::Local);
let mut set = ConfigSet::new();
let env = Environment::empty();
let err = set
.merge_file_with_includes(&file, true, &ctx(dir.path(), &env))
.expect_err("remote url in hasconfig include");
assert!(matches!(
err,
Error::Config(ConfigError::RemoteUrlInHasconfigInclude)
));
}
#[test]
fn parse_rejects_bad_lines_and_negotiation_algorithm() {
use grit_lib::config::ConfigFile;
let err = ConfigFile::parse(
std::path::Path::new("c"),
"[s]\n\tfetch.negotiationalgorithm\n",
ConfigScope::Local,
)
.expect_err("missing value");
assert!(matches!(err, Error::Message(_) | Error::Config(_)));
let err = ConfigFile::parse(
std::path::Path::new("c"),
"[s]\n\tkey = \"unclosed\n",
ConfigScope::Local,
)
.expect_err("unclosed quote");
assert!(matches!(
err,
Error::Config(ConfigError::BadConfigLineInFile { .. })
));
let file = ConfigFile::parse(
std::path::Path::new("c"),
"[inline \"sub\"]\n\tinside = 1\n",
ConfigScope::Local,
)
.expect("inline section key");
assert_eq!(file.get("inline.sub.inside").as_deref(), Some("1"));
}
#[test]
fn includeif_gitdir_icase_matches() {
let dir = tempdir().expect("tempdir");
let home = dir.path().join("home");
fs::create_dir_all(&home).expect("home");
let repo = home.join("Repo");
Repository::init(
&repo,
&InitOptions::new()
.with_initial_branch("main".to_owned())
.with_ref_storage(grit_lib::RefStorageFormat::Files),
)
.expect("init");
let git_dir = repo.join(".git");
let pattern = format!("gitdir/i:{}/", repo.display());
let mut cfg = fs::read_to_string(git_dir.join("config")).expect("read");
cfg.push_str(&format!("[includeIf \"{pattern}\"]\n\tpath = icase.conf\n"));
fs::write(git_dir.join("config"), &cfg).expect("cfg");
fs::write(git_dir.join("icase.conf"), "[icase]\n\tk = 1\n").expect("inc");
let mut env = isolated_env(&home);
env.cwd = repo.clone();
env.pwd = Some(repo.display().to_string());
let set = ConfigSet::load_with_options(
&env,
Some(&git_dir),
&support::default_load_opts(&git_dir, &env),
)
.expect("load");
assert_eq!(set.get("icase.k").as_deref(), Some("1"));
}