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::Autocheck_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 fieldscheck_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 fieldscheck_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 fieldscheck_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 correctlyassert_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 stringlayers = 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 stringlayers = 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 stringlayers = 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 stringlayers = 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)