Files
codex/codex-rs/git-utils/src/git_config_tests.rs

155 lines
5.3 KiB
Rust

use super::*;
#[test]
fn parses_effective_last_value_with_scope_and_origin() {
let output = b"global\0file:/tmp/global\0filter.demo.clean\ngit-lfs clean -- %f\0\
local\0file:/repo/.git/config\0filter.demo.clean\n\0\
command\0command line:\0merge.Name.driver\nhelper %A %B\0";
let entries = parse_effective_config(output).expect("parse config");
assert_eq!(entries.len(), 2);
assert_eq!(
entries.get("filter.demo.clean"),
Some(&GitConfigEntry {
scope: GitConfigScope::Local,
origin: GitConfigOrigin::File("/repo/.git/config".into()),
key: "filter.demo.clean".to_string(),
value: String::new(),
})
);
assert_eq!(
entries.get("merge.Name.driver"),
Some(&GitConfigEntry {
scope: GitConfigScope::Command,
origin: GitConfigOrigin::CommandLine,
key: "merge.Name.driver".to_string(),
value: "helper %A %B".to_string(),
})
);
}
#[test]
fn entry_parsers_preserve_duplicate_include_directives_in_order() {
let scoped = b"local\0file:.git/config\0include.path\n../unsafe.gitconfig\0\
local\0file:.git/config\0include.path\n/absolute/external.gitconfig\0";
let legacy = b"file:.git/config\0include.path\n../unsafe.gitconfig\0\
file:.git/config\0include.path\n/absolute/external.gitconfig\0";
let expected_scoped = vec![
GitConfigEntry {
scope: GitConfigScope::Local,
origin: GitConfigOrigin::File(".git/config".into()),
key: "include.path".to_string(),
value: "../unsafe.gitconfig".to_string(),
},
GitConfigEntry {
scope: GitConfigScope::Local,
origin: GitConfigOrigin::File(".git/config".into()),
key: "include.path".to_string(),
value: "/absolute/external.gitconfig".to_string(),
},
];
assert_eq!(
parse_config_entries(scoped).expect("scoped entries"),
expected_scoped
);
assert_eq!(
parse_config_entries_with_origins(legacy).expect("legacy entries"),
expected_scoped
);
}
#[cfg(unix)]
#[test]
fn config_origin_parser_preserves_non_utf8_unix_path_bytes() {
use std::os::unix::ffi::OsStrExt;
let entries = parse_config_entries(b"local\0file:/tmp/config-\xff\0include.path\n/tmp/safe\0")
.expect("non-UTF-8 config origin");
let GitConfigOrigin::File(path) = &entries[0].origin else {
panic!("expected file origin");
};
assert_eq!(path.as_os_str().as_bytes(), b"/tmp/config-\xff");
}
#[test]
fn rejects_malformed_config_records() {
for output in [
b"global\0file:/tmp/config\0key\nvalue".as_slice(),
b"global\0file:/tmp/config\0key\0".as_slice(),
b"mystery\0file:/tmp/config\0key\nvalue\0".as_slice(),
b"global\0\0key\nvalue\0".as_slice(),
b"global\0file:/tmp/config\0\nvalue\0".as_slice(),
] {
assert!(parse_effective_config(output).is_err(), "{output:?}");
}
}
#[test]
fn parses_legacy_origin_records_in_effective_order() {
let output = b"file:/tmp/global\0filter.demo.clean\nfirst\0\
file:/repo/.git/config\0filter.demo.clean\n\0\
command line:\0merge.Name.driver\nhelper %A %B\0";
let entries = parse_effective_config_with_origins(output).expect("parse legacy config");
assert_eq!(
entries.get("filter.demo.clean"),
Some(&GitConfigEntry {
scope: GitConfigScope::Local,
origin: GitConfigOrigin::File("/repo/.git/config".into()),
key: "filter.demo.clean".to_string(),
value: String::new(),
})
);
assert_eq!(
entries.get("merge.Name.driver"),
Some(&GitConfigEntry {
scope: GitConfigScope::Command,
origin: GitConfigOrigin::CommandLine,
key: "merge.Name.driver".to_string(),
value: "helper %A %B".to_string(),
})
);
}
#[test]
fn path_containment_uses_component_boundaries() {
let root = Path::new("/repo/root");
assert!(path_is_within(Path::new("/repo/root"), root));
assert!(path_is_within(Path::new("/repo/root/config"), root));
assert!(!path_is_within(Path::new("/repo/rooted/config"), root));
assert!(!path_is_within(Path::new("/repo"), root));
}
#[test]
fn git_normalizes_case_insensitive_key_parts_before_effective_parsing() {
let mut command = std::process::Command::new("git");
crate::safe_git::isolate_git_command_environment(&mut command);
let output = command
.args([
"-c",
"FiLtEr.DeMo.ClEaN=first",
"-c",
"filter.DeMo.clean=second",
"config",
"--null",
"--show-scope",
"--show-origin",
"--get-regexp",
r"^filter\.DeMo\.clean$",
])
.output()
.expect("query mixed-case Git config");
assert!(
output.status.success(),
"Git config query failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let entries = parse_effective_config(&output.stdout).expect("parse Git-normalized config");
assert_eq!(entries.len(), 1);
let entry = entries
.get("filter.DeMo.clean")
.expect("canonical section and variable casing");
assert_eq!(entry.value, "second");
assert_eq!(entry.scope, GitConfigScope::Command);
}