Implement tests for `pijul-config`
Dependencies
- [2]
FIMDS32ZUse subcommand-specific repository config paths - [3]
Z4PPQZUGRefactor `pijul-config` to support layered configuration - [4]
H4AU6QRPNew config for HTTP remotes - [5]
HGJETVANCreate `pijul_config::global_config_directory()` - [6]
HM6QW3CYHandle missing configurations in `pijul_config` - [7]
SXEYMYF7Fixing the bad changes in history (unfortunately, by rebooting). - [8]
VQPAUKBQchannel switch as an alias to reset - [9]
VL7ZYKHBRunning hooks through shell on Windows and Unix - [10]
FVQYZQFLCreate dialoguer themes based on global config - [11]
2ZKE4XMJReplace `pijul_repository::init_dot_ignore` with `pijul_config::Config::dot_ignore_contents` - [12]
Y6EVFMTADon't output files if they aren't in the current channel - [*]
7UU3TV5WRefactor `pijul::config` into new crate - [*]
OWO4EWK7Add global `--config` argument to override configuration values
Change contents
- file addition: tests[14.24]
- file addition: parse_config_arg.rs[0.17]
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();} - file addition: layering.rs[0.17]
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(())} - file addition: config.rs[0.17]
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(())} - edit in pijul-config/src/lib.rs at line 50
#[serde(default)] - edit in pijul-config/src/lib.rs at line 74[15.137][3.6162]
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> { - replacement in pijul-config/src/lib.rs at line 131
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)); - replacement in pijul-config/src/lib.rs at line 138
Some(global_config)}Err(error) => {warn!("Unable to read global config file: {error:#?}");None}},None => {warn!("Unable to find global configuration path");NoneSome(global_config) - edit in pijul-config/src/lib.rs at line 140
None => None, - replacement in pijul-config/src/lib.rs at line 144
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)); - replacement in pijul-config/src/lib.rs at line 151
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");NoneSome(global_config) - edit in pijul-config/src/lib.rs at line 153
None => None, - replacement in pijul-config/src/lib.rs at line 225
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]