use super::{CONFIG_FILE_NAME, EARLIER_CONFIG_FILE_NAME, LoadedConfig, load_repository_config, settings_file_of};
use crate::config_schema::CliGitConfig;
use crate::policy_registry::{PolicyId, Severity};
use crate::wrapper_names_old_and_new::SETTINGS_FILE;
use std::path::{Path, PathBuf};
fn fixture(name: &str) -> PathBuf {
return crate::test_support::fixture_with_prefix("native-config-earlier", name);
}
fn document(severity: &str) -> String {
return format!("{{ \"policies\": {{ \"final-newline\": \"{severity}\" }} }}");
}
fn final_newline(loaded: &LoadedConfig) -> Severity {
return loaded.config.policies.setting(PolicyId::FinalNewline).severity;
}
#[test]
fn the_loader_reads_the_pair_of_settings_names() {
assert_eq!(CONFIG_FILE_NAME, SETTINGS_FILE.current);
assert_eq!(EARLIER_CONFIG_FILE_NAME, SETTINGS_FILE.earlier);
}
#[test]
fn the_earlier_file_alone_is_read_with_one_notice_naming_the_new_name() {
let root: PathBuf = fixture("earlier-only");
std::fs::write(root.join(EARLIER_CONFIG_FILE_NAME), document("error")).expect("write the earlier file");
let loaded: LoadedConfig = load_repository_config(root.as_path()).expect("the earlier file loads");
assert_eq!(final_newline(&loaded), Severity::Error);
assert_eq!(loaded.source, Some(root.join(EARLIER_CONFIG_FILE_NAME)));
let notice: String = loaded.earlier_name_notice.expect("one notice");
assert!(notice.contains(CONFIG_FILE_NAME) && notice.contains(EARLIER_CONFIG_FILE_NAME), "{notice}");
assert_eq!(notice.matches('\n').count(), 1, "one line: {notice:?}");
assert!(notice.ends_with('\n'));
assert!(root.join(EARLIER_CONFIG_FILE_NAME).is_file(), "the wrapper never renames a committed file");
assert!(!root.join(CONFIG_FILE_NAME).exists(), "and never creates the new one");
std::fs::remove_dir_all(root.as_path()).expect("remove only the fixture");
}
#[test]
fn the_new_file_alone_is_read_without_a_notice() {
let root: PathBuf = fixture("new-only");
std::fs::write(root.join(CONFIG_FILE_NAME), document("error")).expect("write the new file");
let loaded: LoadedConfig = load_repository_config(root.as_path()).expect("the new file loads");
assert_eq!(final_newline(&loaded), Severity::Error);
assert_eq!(loaded.source, Some(root.join(CONFIG_FILE_NAME)));
assert_eq!(loaded.earlier_name_notice, None);
std::fs::remove_dir_all(root.as_path()).expect("remove only the fixture");
}
#[test]
fn both_files_read_the_new_one_and_report_the_earlier_as_ignored() {
let root: PathBuf = fixture("both");
std::fs::write(root.join(CONFIG_FILE_NAME), document("warn")).expect("write the new file");
std::fs::write(root.join(EARLIER_CONFIG_FILE_NAME), document("error")).expect("write the earlier file");
let loaded: LoadedConfig = load_repository_config(root.as_path()).expect("the new file loads");
assert_eq!(final_newline(&loaded), Severity::Warn, "the earlier file's settings are not read");
assert_eq!(loaded.source, Some(root.join(CONFIG_FILE_NAME)));
let notice: String = loaded.earlier_name_notice.expect("one notice");
assert!(notice.contains(EARLIER_CONFIG_FILE_NAME) && notice.contains("ignored"), "{notice}");
assert_eq!(notice.matches('\n').count(), 1, "one line: {notice:?}");
assert!(root.join(EARLIER_CONFIG_FILE_NAME).is_file(), "the earlier file is left as it is");
std::fs::remove_dir_all(root.as_path()).expect("remove only the fixture");
}
#[test]
fn an_invalid_earlier_file_fails_only_when_it_is_the_one_read() {
let root: PathBuf = fixture("invalid-earlier");
std::fs::write(root.join(EARLIER_CONFIG_FILE_NAME), "{ \"unknown\": 1 }").expect("write the earlier file");
assert!(load_repository_config(root.as_path()).is_err());
std::fs::write(root.join(CONFIG_FILE_NAME), "{}").expect("write the new file");
assert!(load_repository_config(root.as_path()).is_ok(), "the new file shadows the invalid one");
std::fs::remove_dir_all(root.as_path()).expect("remove only the fixture");
}
#[test]
fn neither_file_gives_the_defaults_and_no_notice() {
let root: PathBuf = fixture("neither");
let loaded: LoadedConfig = load_repository_config(root.as_path()).expect("defaults");
assert_eq!(loaded.config, CliGitConfig::defaults());
assert_eq!(loaded.source, None);
assert_eq!(loaded.earlier_name_notice, None);
std::fs::remove_dir_all(root.as_path()).expect("remove only the fixture");
}
#[test]
fn a_typescript_era_file_beside_the_earlier_file_is_still_reported() {
let root: PathBuf = fixture("legacy-beside-earlier");
std::fs::write(root.join(EARLIER_CONFIG_FILE_NAME), document("error")).expect("write the earlier file");
std::fs::write(root.join("cli-git.config.mjs"), "export default {};").expect("write the module");
let loaded: LoadedConfig = load_repository_config(root.as_path()).expect("loads");
assert_eq!(final_newline(&loaded), Severity::Error);
assert_eq!(loaded.ignored_legacy, vec![root.join("cli-git.config.mjs")]);
std::fs::remove_dir_all(root.as_path()).expect("remove only the fixture");
}
#[test]
fn a_typescript_era_file_alone_names_the_new_file_in_its_error() {
let root: PathBuf = fixture("legacy-alone");
std::fs::write(root.join("cli-git.config.ts"), "export default {};").expect("write the module");
let error: String = load_repository_config(root.as_path()).expect_err("migration is required").message;
assert!(error.contains(CONFIG_FILE_NAME), "{error}");
std::fs::remove_dir_all(root.as_path()).expect("remove only the fixture");
}
#[test]
fn a_symbolic_link_under_the_earlier_name_is_refused() {
let root: PathBuf = fixture("earlier-link");
let elsewhere: PathBuf = root.join("elsewhere.jsonc");
std::fs::write(elsewhere.as_path(), "{}").expect("write the target");
std::os::unix::fs::symlink(elsewhere.as_path(), root.join(EARLIER_CONFIG_FILE_NAME)).expect("link");
let error: String = load_repository_config(root.as_path()).expect_err("a link is refused").message;
assert!(error.contains("symbolic link"), "{error}");
std::fs::remove_dir_all(root.as_path()).expect("remove only the fixture");
}
#[test]
fn an_awkward_repository_path_does_not_change_the_notice_or_what_is_read() {
let outer: PathBuf = fixture("awkward");
let root: PathBuf = outer.join("a repo's \"top\"\nlevel");
std::fs::create_dir_all(root.as_path()).expect("create the repository");
std::fs::write(root.join(EARLIER_CONFIG_FILE_NAME), document("error")).expect("write the earlier file");
let loaded: LoadedConfig = load_repository_config(Path::new(root.as_path())).expect("loads");
assert_eq!(final_newline(&loaded), Severity::Error);
let notice: String = loaded.earlier_name_notice.expect("one notice");
assert_eq!(notice.matches('\n').count(), 1, "one line: {notice:?}");
assert!(!notice.contains("level"), "the notice names files, not the path: {notice:?}");
std::fs::remove_dir_all(outer.as_path()).expect("remove only the fixture");
}
#[test]
fn the_settings_file_of_a_repository_is_found_current_name_first_then_the_earlier() {
let root: PathBuf = fixture("settings-file-of");
assert_eq!(settings_file_of(root.as_path()), root.join(CONFIG_FILE_NAME), "neither");
std::fs::write(root.join(EARLIER_CONFIG_FILE_NAME), "{}").expect("write the earlier file");
assert_eq!(settings_file_of(root.as_path()), root.join(EARLIER_CONFIG_FILE_NAME), "earlier only");
std::fs::write(root.join(CONFIG_FILE_NAME), "{}").expect("write the new file");
assert_eq!(settings_file_of(root.as_path()), root.join(CONFIG_FILE_NAME), "both");
std::fs::remove_dir_all(root.as_path()).expect("remove only the fixture");
}