use std::{ffi::CString, os::unix::fs::PermissionsExt as _, path::PathBuf};
use nix::unistd::{User, getegid, geteuid};
use rstest::rstest;
use super::*;
use crate::test_support::capture_info_logs;
fn current_user() -> User {
User {
name: String::from("pg-embed-test"),
passwd: CString::default(),
uid: geteuid(),
gid: getegid(),
#[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
gecos: CString::default(),
dir: PathBuf::from("/nonexistent"),
shell: PathBuf::from("/nonexistent"),
#[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly"))]
class: CString::default(),
#[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly"))]
change: 0,
#[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly"))]
expire: 0,
}
}
fn utf8(path: &std::path::Path) -> Option<&Utf8Path> { Utf8Path::from_path(path) }
fn mode_of(path: &Utf8Path) -> std::io::Result<u32> {
Ok(std::fs::metadata(path)?.permissions().mode() & 0o777)
}
fn apply_world_readable(dir: &Utf8Path, user: &User) -> PrivilegeResult<()> {
make_dir_accessible(dir, user)
}
fn apply_owner_only(dir: &Utf8Path, user: &User) -> PrivilegeResult<()> {
make_data_dir_private(dir, user)
}
#[rstest]
#[case::world_readable(apply_world_readable, "install", 0o755)]
#[case::owner_only(apply_owner_only, "data", 0o700)]
fn directory_permission_helper_sets_expected_mode(
#[case] helper: fn(&Utf8Path, &User) -> PrivilegeResult<()>,
#[case] subdir: &str,
#[case] expected_mode: u32,
) {
let temp = tempfile::tempdir().expect("tempdir");
let dir = utf8(temp.path()).expect("temp path is UTF-8").join(subdir);
let user = current_user();
helper(&dir, &user).expect("apply directory permissions");
assert!(dir.is_dir(), "directory should exist");
assert_eq!(
mode_of(&dir).expect("stat directory"),
expected_mode,
"unexpected directory mode"
);
}
#[test]
fn ensure_dir_for_user_is_idempotent() {
let temp = tempfile::tempdir().expect("tempdir");
let dir = utf8(temp.path())
.expect("temp path is UTF-8")
.join("nested/child");
let user = current_user();
ensure_dir_for_user(&dir, &user, 0o750).expect("first call");
ensure_dir_for_user(&dir, &user, 0o750).expect("second call");
assert_eq!(mode_of(&dir).expect("stat directory"), 0o750);
}
#[test]
fn ensure_tree_owned_by_user_walks_nested_entries() {
let temp = tempfile::tempdir().expect("tempdir");
let root = utf8(temp.path()).expect("temp path is UTF-8");
let nested = root.join("a/b");
std::fs::create_dir_all(nested.as_std_path()).expect("create nested dirs");
std::fs::write(nested.join("file.txt").as_std_path(), b"data").expect("write file");
std::fs::write(root.join("top.txt").as_std_path(), b"data").expect("write top file");
let user = current_user();
let (logs, result) = capture_info_logs(|| ensure_tree_owned_by_user(root, &user));
result.expect("chown tree to self");
assert!(
logs.iter().any(|line| {
line.contains("ensured tree ownership for user") && line.contains("updated_entries=4")
}),
"expected all four nested entries to be chowned, got logs: {logs:?}"
);
}
#[test]
fn ensure_tree_owned_by_user_ignores_missing_root() {
let temp = tempfile::tempdir().expect("tempdir");
let missing = utf8(temp.path())
.expect("temp path is UTF-8")
.join("does-not-exist");
ensure_tree_owned_by_user(&missing, ¤t_user())
.expect("missing root should be treated as an empty tree");
}
#[test]
fn nobody_uid_is_non_root() {
assert!(nobody_uid().as_raw() > 0, "nobody must not map to root");
}
#[test]
fn default_paths_for_derive_install_and_data_dirs() {
let (install, data) = default_paths_for(Uid::from_raw(4321));
assert_eq!(install.as_str(), "/var/tmp/pg-embed-4321/install");
assert_eq!(data.as_str(), "/var/tmp/pg-embed-4321/data");
}