Only notable change is moving the configuration logic loading into Config::load_with()
, so that the tests can override configuration files without touching the filesystem. Ideally this should eventually be merged back together, and the filesystem be mocked using libpijul::working_copy::memory::Memory
.
IWN4G7PCFKVK7IN5HMXS5NFCSIT5HFDLSUJ4ULP4QOZTBYFCFWDAC
FIMDS32ZN6OCZUBWYNKDHZJ6FNKUC7NRUC37Q45HXQRBSTDM4YSQC
7UU3TV5W23QA7LLRBSBXEYPRMIVXPW4FNENEEE7ZEJYXDLXHVX4AC
Z4PPQZUGHT5F5VFFBQBIW2J3OLHP4SF33QMT6POFCX6JS7L6G7GQC
SXEYMYF7P4RZMZ46WPL4IZUTSQ2ATBWYZX7QNVMS3SGOYXYOHAGQC
OWO4EWK7YVHW544XJTTXAYO553GQKWGXIWNECDPWPFOGNJ6KHWAAC
2ZKE4XMJ3Z3IBFJCJCXQ6E76T3QEVBB2VEZCY7ZD4EMRJ2BHKBTQC
HGJETVANHD25AZLOPYYEXCMLZHZWZ2NAKI33XQ7F43XM3UZVQNOQC
HM6QW3CYVZVXOM2K3OT7SQFQGJG3GCDLNYIYAUDEVSJCVCSUZ4CQC
VL7ZYKHBPKLNY5SA5QBW56SJ7LBBCKCGV5UAYLVF75KY6PPBOD4AC
use pijul_config::parse_config_arg;
#[test]
fn top_level() -> Result<(), anyhow::Error> {
let (key, value) = parse_config_arg("unrecord_changes=1")?;
assert_eq!(key, "unrecord_changes");
assert_eq!(value, "1");
Ok(())
}
#[test]
fn nested() -> Result<(), anyhow::Error> {
let (key, value) = parse_config_arg("author.username=Ferris")?;
assert_eq!(key, "author.username");
assert_eq!(value, "Ferris");
Ok(())
}
#[test]
fn missing_equals_sign() {
parse_config_arg("unrecord_changes1").unwrap_err();
}
#[test]
fn empty_argument() {
parse_config_arg("").unwrap_err();
}
use pijul_config::{Choice, Config};
use std::path::PathBuf;
const GLOBAL_CONFIG: &str = r#"
colors = "never"
pager = "never"
reset_overwrites_changes = "never"
"#;
const LOCAL_CONFIG: &str = r#"
colors = "always"
pager = "always"
reset_overwrites_changes = "always"
"#;
const CONFIG_OVERRIDES: [(&str, &str); 3] = [
("colors", "auto"),
("pager", "auto"),
("reset_overwrites_changes", "auto"),
];
fn check_config_fields(config: &Config, choice: Choice) {
assert_eq!(config.colors, choice);
assert_eq!(config.pager, choice);
assert_eq!(config.reset_overwrites_changes, choice);
}
/// Default config values should be Choice::Auto
#[test]
fn default() -> Result<(), anyhow::Error> {
let default_config = Config::load(None, Vec::new())?;
// Double-check that the defaults still use Choice::Auto
check_config_fields(&default_config, Choice::Auto);
Ok(())
}
/// Global config values should override defaults
#[test]
fn global() -> Result<(), anyhow::Error> {
let global_config = Config::load_with(
Some((PathBuf::new(), String::from(GLOBAL_CONFIG))),
None,
Vec::new(),
)?;
// Make sure the global config overrides these fields
check_config_fields(&global_config, Choice::Never);
Ok(())
}
/// Local config values should override defaults and global config
#[test]
fn local() -> Result<(), anyhow::Error> {
let local_config = Config::load_with(
Some((PathBuf::new(), String::from(GLOBAL_CONFIG))),
Some((PathBuf::new(), String::from(LOCAL_CONFIG))),
Vec::new(),
)?;
// Make sure the local config overrides these fields
check_config_fields(&local_config, Choice::Always);
Ok(())
}
/// Config overrides should override everything
#[test]
fn overrides() -> Result<(), anyhow::Error> {
let override_config = Config::load_with(
Some((PathBuf::new(), String::from(GLOBAL_CONFIG))),
Some((PathBuf::new(), String::from(LOCAL_CONFIG))),
CONFIG_OVERRIDES
.iter()
.map(|(key, value)| (key.to_string(), value.to_string()))
.collect(),
)?;
// Make sure the overrides apply to these fields
check_config_fields(&override_config, Choice::Auto);
Ok(())
}
/// Different layers merge correctly
#[test]
fn merging() -> Result<(), anyhow::Error> {
let layered_config = Config::load_with(
Some((PathBuf::new(), String::from(r#"colors = "never""#))),
Some((PathBuf::new(), String::from(r#"pager = "always""#))),
vec![(
String::from("reset_overwrites_changes"),
String::from("never"),
)],
)?;
// Make sure the layers merge correctly
assert_eq!(layered_config.colors, Choice::Never);
assert_eq!(layered_config.pager, Choice::Always);
assert_eq!(layered_config.reset_overwrites_changes, Choice::Never);
Ok(())
}
use pijul_config::Config;
use std::path::PathBuf;
#[test]
fn load_simple() -> Result<(), anyhow::Error> {
Config::load(None, Vec::new())?;
Ok(())
}
#[test]
fn load_defaults() -> Result<(), anyhow::Error> {
Config::load_with(None, None, Vec::new())?;
Ok(())
}
#[test]
fn empty_global_config() -> Result<(), anyhow::Error> {
let empty_config = Some((PathBuf::new(), String::new()));
Config::load_with(empty_config, None, Vec::new())?;
Ok(())
}
#[test]
fn empty_local_config() -> Result<(), anyhow::Error> {
let empty_config = Some((PathBuf::new(), String::new()));
Config::load_with(None, empty_config, Vec::new())?;
Ok(())
}
let global_config = match Global::config_file() {
Some(global_config_path) => match Global::read_contents(&global_config_path) {
Ok(contents) => Some((global_config_path, contents)),
Err(error) => {
warn!("Unable to read global config file: {error:#?}");
None
}
},
None => {
warn!("Unable to find global configuration path");
None
}
};
let local_config = match repository_path {
Some(repository_path) => match Local::read_contents(&repository_path) {
Ok(contents) => Some((repository_path.to_path_buf(), contents)),
Err(error) => {
warn!("Unable to read global config file: {error:#?}");
None
}
},
None => {
info!(
"Skipping local configuration path - repository path was not supplied by caller"
);
None
}
};
Self::load_with(global_config, local_config, config_overrides)
}
pub fn load_with(
global_config_file: Option<(PathBuf, String)>,
local_config_file: Option<(PathBuf, String)>,
config_overrides: Vec<(String, String)>,
) -> Result<Self, anyhow::Error> {
let global_config = match Global::config_file() {
Some(global_config_path) => match Global::read_contents(&global_config_path) {
Ok(contents) => {
// Parse the config (and make sure it's valid!)
let global_config = Global::parse_contents(&global_config_path, &contents)?;
// Add the configuration layer as a string
layers = layers.merge(Toml::string(&contents));
let global_config = match global_config_file {
Some((path, contents)) => {
// Parse the config (and make sure it's valid!)
let global_config = Global::parse_contents(&path, &contents)?;
// Add the configuration layer as a string
layers = layers.merge(Toml::string(&contents));
let local_config = match repository_path {
Some(repository_path) => match Local::read_contents(&repository_path) {
Ok(contents) => {
// Parse the config (and make sure it's valid!)
let local_config = Local::parse_contents(&repository_path, &contents)?;
// Add the configuration layer as a string
layers = layers.merge(Toml::string(&contents));
let local_config = match local_config_file {
Some((path, contents)) => {
// Parse the config (and make sure it's valid!)
let global_config = Local::parse_contents(&path, &contents)?;
// Add the configuration layer as a string
layers = layers.merge(Toml::string(&contents));
Some(local_config)
}
Err(error) => {
warn!("Unable to read global config file: {error:#?}");
None
}
},
None => {
info!(
"Skipping local configuration path - repository path was not supplied by caller"
);
None
Some(global_config)