use super::*; #[test] fn unreadable_metadata_is_invalid_not_a_proven_policy_crossing() { let marker = PathBuf::from("repository/.git/commondir"); let error = super::helpers::invalid_metadata( &marker, io::Error::new( io::ErrorKind::PermissionDenied, "metadata marker is unreadable", ), ); assert_eq!( error, crate::errors::GitReadError::InvalidRepositoryMetadata { path: marker, reason: "metadata marker is unreadable".to_string(), } ); assert_eq!(error.io_kind(), io::ErrorKind::InvalidData); } fn write_common_config(body: &[u8]) -> tempfile::TempDir { let common = tempfile::tempdir().expect("common dir"); std::fs::write(common.path().join("config"), body).expect("write common config"); common } fn git_config_path(path: &Path) -> String { let path = path.to_string_lossy(); #[cfg(windows)] let path = path.replace('\\', "/"); #[cfg(not(windows))] let path = path.into_owned(); format!("\"{}\"", path.replace('\\', "\\\\").replace('"', "\\\"")) } fn native_bare_value(config: &Path, includes: bool) -> io::Result> { let mut command = std::process::Command::new("git"); crate::safe_git::isolate_git_command_environment(&mut command); command .args(["config", "--file"]) .arg(config) .arg(if includes { "--includes" } else { "--no-includes" }) .args(["--type=bool", "--get", "core.bare"]); let output = command.output()?; match output.status.code() { Some(0) => Ok(Some( String::from_utf8_lossy(&output.stdout).trim() == "true", )), Some(1) if output.stdout.is_empty() => Ok(None), _ => Err(io::Error::other(format!( "native core.bare query failed: {}", String::from_utf8_lossy(&output.stderr) ))), } } #[test] fn plain_common_bare_parser_matches_native_boolean_syntax_and_is_stricter_on_duplicates() { for (name, body, expected, native) in [ ( "explicit true", b"[core]\n\tbare = true\n".as_slice(), CommonConfigAuthority::Bare, Some(true), ), ( "implicit true", b"[core]\n\tbare\n".as_slice(), CommonConfigAuthority::Unproven, Some(true), ), ( "quoted true", b"[core]\n\tbare = \"true\"\n".as_slice(), CommonConfigAuthority::Bare, Some(true), ), ( "yes", b"[core]\n\tbare = yes\n".as_slice(), CommonConfigAuthority::Bare, Some(true), ), ( "on", b"[core]\n\tbare = on\n".as_slice(), CommonConfigAuthority::Bare, Some(true), ), ( "numeric true", b"[core]\n\tbare = 1\n".as_slice(), CommonConfigAuthority::Bare, Some(true), ), ( "hex true", b"[core]\n\tbare = 0x1\n".as_slice(), CommonConfigAuthority::Bare, Some(true), ), ( "suffixed true", b"[core]\n\tbare = 1k\n".as_slice(), CommonConfigAuthority::Bare, Some(true), ), ( "false", b"[core]\n\tbare = false\n".as_slice(), CommonConfigAuthority::Unproven, Some(false), ), ( "duplicate last true", b"[core]\n\tbare = false\n\tbare = true\n".as_slice(), CommonConfigAuthority::Unproven, Some(true), ), ( "duplicate last false", b"[core]\n\tbare = true\n\tbare = false\n".as_slice(), CommonConfigAuthority::Unproven, Some(false), ), ( "mixed implicit then explicit", b"[core]\n\tbare\n\tbare = true\n".as_slice(), CommonConfigAuthority::Unproven, Some(true), ), ( "mixed explicit then implicit", b"[core]\n\tbare = false\n\tbare\n".as_slice(), CommonConfigAuthority::Unproven, Some(true), ), ( "mixed case duplicate", b"[core]\n\tbare = false\n\tBaRe = true\n".as_slice(), CommonConfigAuthority::Unproven, Some(true), ), ] { let common = write_common_config(body); assert_eq!( inspect_plain_common_config_authority(common.path()).expect(name), expected, "authority result for {name}" ); assert_eq!( native_bare_value(&common.path().join("config"), /*includes*/ false).expect(name), native, "native result for {name}" ); } } #[test] fn common_bare_proof_rejects_invalid_git_boolean_values() { for (name, body) in [ ("leading zero", b"[core]\n\tbare = 08\n".as_slice()), ( "negative leading zero", b"[core]\n\tbare = -08\n".as_slice(), ), ( "positive overflow", b"[core]\n\tbare = 2147483648\n".as_slice(), ), ( "negative overflow", b"[core]\n\tbare = -2147483649\n".as_slice(), ), ( "i64 max", b"[core]\n\tbare = 9223372036854775807\n".as_slice(), ), ("suffix overflow", b"[core]\n\tbare = 2g\n".as_slice()), ("trailing junk", b"[core]\n\tbare = 1foo\n".as_slice()), ] { let common = write_common_config(body); assert_eq!( inspect_plain_common_config_authority(common.path()).expect(name), CommonConfigAuthority::Unproven, "authority result for {name}" ); } } #[test] fn common_bare_proof_accepts_explicit_false_worktree_config_spellings() { for value in ["false", "no", "off", "", "0", "-0", "+0", "00", "0x0"] { let common = write_common_config( format!("[core]\n\tbare = yes\n[extensions]\n\tworktreeConfig = {value}\n").as_bytes(), ); assert_eq!( inspect_plain_common_config_authority(common.path()).expect(value), CommonConfigAuthority::Bare, "authority result for worktreeConfig={value:?}" ); } } #[test] fn common_bare_proof_rejects_explicit_true_worktree_config_spellings() { for value in ["true", "yes", "on", "1", "-1", "0x1", "1k"] { let common = write_common_config( format!("[core]\n\tbare = yes\n[extensions]\n\tworktreeConfig = {value}\n").as_bytes(), ); assert_eq!( inspect_plain_common_config_authority(common.path()).expect(value), CommonConfigAuthority::Unproven, "authority result for worktreeConfig={value:?}" ); } } #[test] fn common_bare_proof_rejects_includes_worktree_config_and_relative_worktree() { let include_target = tempfile::NamedTempFile::new().expect("included config"); std::fs::write(include_target.path(), "[core]\n\tbare = false\n") .expect("write included config"); let common = write_common_config( format!( "[core]\n\tbare = true\n[include]\n\tpath = {}\n", git_config_path(include_target.path()) ) .as_bytes(), ); assert_eq!( native_bare_value(&common.path().join("config"), /*includes*/ false) .expect("direct native bare"), Some(true) ); assert_eq!( native_bare_value(&common.path().join("config"), /*includes*/ true) .expect("included native bare"), Some(false) ); assert_eq!( inspect_plain_common_config_authority(common.path()).expect("include authority"), CommonConfigAuthority::Unproven ); for body in [ b"[core]\n\tbare = true\n[includeIf \"gitdir:**\"]\n\tpath = /tmp/ignored\n".as_slice(), b"[core]\n\tbare = true\n[extensions]\n\tworktreeConfig = true\n".as_slice(), b"[core]\n\tbare = true\n\tworktree = relative/path\n".as_slice(), b"[core]\n\tbare = true\n[extensions]\n\tworktreeConfig\n".as_slice(), b"[core]\n\tbare = true\n[extensions]\n\tworktreeConfig = false\n\tworktreeConfig\n" .as_slice(), b"[core]\n\tbare = true\n\tworktree\n".as_slice(), b"[core]\n\tbare = true\n\tworktree = /tmp/one\n\tworktree\n".as_slice(), b"[core \"unexpected\"]\n\tbare = true\n".as_slice(), b"[extensions \"unexpected\"]\n\tworktreeConfig = false\n[core]\n\tbare = true\n" .as_slice(), ] { let common = write_common_config(body); assert_eq!( inspect_plain_common_config_authority(common.path()).expect("ambiguous authority"), CommonConfigAuthority::Unproven ); } } #[test] fn common_config_absolute_worktree_is_returned_as_authority() { let worktree = tempfile::tempdir().expect("worktree"); let common = write_common_config( format!( "[core]\n\tworktree = {}\n", git_config_path(worktree.path()) ) .as_bytes(), ); assert_eq!( inspect_plain_common_config_authority(common.path()).expect("worktree authority"), CommonConfigAuthority::Worktree(worktree.path().to_path_buf()) ); } #[test] fn common_config_false_bare_spellings_allow_absolute_worktree() { let worktree = tempfile::tempdir().expect("worktree"); for bare in ["false", "no", "off", "", "0", "-0", "+0", "00", "0x0"] { let common = write_common_config( format!( "[core]\n\tbare = {bare}\n\tworktree = {}\n", git_config_path(worktree.path()) ) .as_bytes(), ); assert_eq!( inspect_plain_common_config_authority(common.path()).expect(bare), CommonConfigAuthority::Worktree(worktree.path().to_path_buf()), "authority result for core.bare={bare:?}" ); } } #[test] fn common_config_rejects_contradictory_or_malformed_bare_with_absolute_worktree() { let worktree = tempfile::tempdir().expect("worktree"); for (name, bare) in [("contradictory bare", "true"), ("malformed bare", "08")] { let common = write_common_config( format!( "[core]\n\tbare = {bare}\n\tworktree = {}\n", git_config_path(worktree.path()) ) .as_bytes(), ); assert_eq!( inspect_plain_common_config_authority(common.path()).expect(name), CommonConfigAuthority::Unproven, "authority result for {name}" ); } } #[test] fn windows_authority_path_grammar_rejects_ambiguous_primary_spellings() { for path in [ r"C:\primary.\repo", r"C:\primary \repo", r"C:\primary:stream\repo", r"C:\NUL\repo", r"\\?\GLOBALROOT\Device\HarddiskVolume1\repo", r"\??\C:\primary\repo", r"C:\outside\..\primary", ] { assert!( crate::path_authority::windows_authority_path_is_ambiguous(path), "ambiguous Windows authority path accepted: {path:?}" ); } for path in [r"C:\primary\repo", r"\\server\share\primary\repo"] { assert!( !crate::path_authority::windows_authority_path_is_ambiguous(path), "ordinary Windows authority path rejected: {path:?}" ); } }