#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
use color_eyre::eyre::ensure;
use rstest::rstest;
use super::*;
#[cfg(unix)]
struct StagedDirCleanup(Option<PathBuf>);
#[cfg(unix)]
impl StagedDirCleanup {
fn new(staged_path: &std::path::Path) -> Self {
Self(staged_path.parent().map(std::path::Path::to_path_buf))
}
}
#[cfg(unix)]
impl Drop for StagedDirCleanup {
fn drop(&mut self) {
if let Some(ref staged_dir) = self.0 {
drop(fs::remove_dir_all(staged_dir));
}
}
}
#[cfg(unix)]
fn debug_target_dir() -> color_eyre::Result<tempfile::TempDir> {
let dir = tempfile::tempdir()?;
let debug_dir = dir.path().join("target").join("debug");
fs::create_dir_all(&debug_dir)?;
Ok(dir)
}
#[cfg(all(unix, privileged_unix_platform))]
#[rstest]
fn staged_worker_is_world_executable_and_in_temp_dir() -> color_eyre::Result<()> {
let debug_target_dir = debug_target_dir()?;
let debug_dir = debug_target_dir.path().join("target").join("debug");
let source = debug_dir.join("pg_worker");
fs::write(&source, b"#!/bin/sh\nexit 0\n")?;
let mut perms = fs::metadata(&source)?.permissions();
perms.set_mode(0o700);
fs::set_permissions(&source, perms)?;
let staged = try_stage_worker_binary(&source.into_os_string())?;
let staged_path = PathBuf::from(&staged);
let _cleanup = StagedDirCleanup::new(&staged_path);
let temp_dir = std::env::temp_dir();
ensure!(
staged_path.starts_with(&temp_dir),
"staged worker should be in temp dir {}, got: {}",
temp_dir.display(),
staged_path.display()
);
let parent = staged_path.parent().ok_or_else(|| {
color_eyre::eyre::eyre!(
"staged worker path missing parent: {}",
staged_path.display()
)
})?;
let parent_name = parent.file_name().and_then(|n| n.to_str()).unwrap_or("");
ensure!(
parent_name.starts_with("pg-worker-debug-"),
"staging dir should match pg-worker-debug-{{hash}}, got: {parent_name}"
);
let mode = fs::metadata(&staged)?.permissions().mode();
ensure!(
mode & 0o001 != 0,
"staged worker should be executable by others"
);
let tmp_meta = fs::metadata(&temp_dir)?;
ensure!(tmp_meta.is_dir(), "temp dir must be a directory");
let tmp_mode = tmp_meta.permissions().mode();
ensure!(
tmp_mode & 0o001 != 0,
"temp dir must be world-executable for nobody to access staged binary"
);
Ok(())
}
fn assert_staging_directory_for_profile(
input_path: &str,
expected_profile: &str,
expected_target_dir: &str,
) {
let path = PathBuf::from(input_path);
let (staged_dir, target_dir) = find_staging_directory(&path);
let temp_dir = std::env::temp_dir();
assert!(
staged_dir.starts_with(&temp_dir),
"staged dir should be in temp dir {}, got: {}",
temp_dir.display(),
staged_dir.display()
);
let staged_name = staged_dir
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("");
let expected_prefix = format!("pg-worker-{expected_profile}-");
assert!(
staged_name.starts_with(&expected_prefix),
"staged dir should match {expected_prefix}*, got: {staged_name}"
);
assert_eq!(
target_dir,
Some(PathBuf::from(expected_target_dir)),
"target dir should match expected path"
);
}
#[rstest]
#[case::debug(
"/project/target/debug/deps/pg_worker-abc123",
"debug",
"/project/target/debug"
)]
#[case::release(
"/project/target/release/pg_worker",
"release",
"/project/target/release"
)]
fn find_staging_directory_detects_profile(
#[case] source_path: &str,
#[case] expected_profile: &str,
#[case] expected_target_dir: &str,
) {
assert_staging_directory_for_profile(source_path, expected_profile, expected_target_dir);
}
#[rstest]
fn pointer_file_writer_records_staged_path_bytes() -> color_eyre::Result<()> {
let dir = tempfile::tempdir()?;
let target_dir = dir.path().join("target").join("debug");
fs::create_dir_all(&target_dir)?;
let staged_path = dir.path().join("staged").join("pg_worker");
write_pointer_file(&target_dir, &staged_path)?;
let pointer_path = target_dir.join("pg_worker_staged.path");
let pointer_content = fs::read(&pointer_path)?;
ensure!(
pointer_content == staged_path.as_os_str().as_encoded_bytes(),
"pointer file should contain staged path bytes"
);
Ok(())
}
#[cfg(unix)]
#[rstest]
fn should_restage_returns_true_for_missing_staged() {
let dir = tempfile::tempdir().expect("tempdir");
let source = dir.path().join("source");
fs::write(&source, b"content").expect("write source");
let staged = dir.path().join("staged");
assert!(should_restage(&source, &staged).expect("should_restage"));
}
#[cfg(unix)]
#[rstest]
fn should_restage_returns_false_when_staged_is_newer() {
use std::time::{Duration, SystemTime};
let dir = tempfile::tempdir().expect("tempdir");
let source = dir.path().join("source");
let staged = dir.path().join("staged");
fs::write(&source, b"content").expect("write source");
fs::write(&staged, b"content").expect("write staged");
let past = SystemTime::now() - Duration::from_secs(3600);
let past_filetime = filetime::FileTime::from_system_time(past);
filetime::set_file_mtime(&source, past_filetime).expect("set source mtime");
assert!(!should_restage(&source, &staged).expect("should_restage"));
}
#[cfg(unix)]
#[rstest]
fn pointer_file_written_to_target_dir() -> color_eyre::Result<()> {
let debug_target_dir = debug_target_dir()?;
let debug_dir = debug_target_dir.path().join("target").join("debug");
let source = debug_dir.join("pg_worker");
fs::write(&source, b"#!/bin/sh\nexit 0\n")?;
let mut perms = fs::metadata(&source)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&source, perms)?;
let staged = try_stage_worker_binary(&source.into_os_string())?;
let staged_path = PathBuf::from(&staged);
let _cleanup = StagedDirCleanup::new(&staged_path);
let pointer_path = debug_dir.join("pg_worker_staged.path");
ensure!(
pointer_path.exists(),
"pointer file should exist at {}",
pointer_path.display()
);
let pointer_content = fs::read(&pointer_path)?;
let staged_path_bytes = staged_path.as_os_str().as_bytes();
ensure!(
pointer_content == staged_path_bytes,
"pointer file should contain staged path"
);
Ok(())
}
#[test]
fn is_worker_binary_accepts_regular_worker_file() {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("pg_worker");
fs::write(&path, b"x").expect("write worker file");
assert!(
is_worker_binary(&path),
"a regular pg_worker file is accepted"
);
}
#[test]
fn is_worker_binary_rejects_dep_files_and_non_workers() {
let dir = tempfile::tempdir().expect("tempdir");
let dep_file = dir.path().join("pg_worker.d");
fs::write(&dep_file, b"x").expect("write dep file");
assert!(
!is_worker_binary(&dep_file),
"dependency files are rejected"
);
let other = dir.path().join("something-else");
fs::write(&other, b"x").expect("write other file");
assert!(!is_worker_binary(&other), "non-worker names are rejected");
let missing = dir.path().join("pg_worker");
assert!(!is_worker_binary(&missing), "missing files are rejected");
}
#[test]
fn find_staging_directory_returns_no_target_without_profile() {
let (staged, target) = find_staging_directory(std::path::Path::new("/no/profile/pg_worker"));
assert!(
staged.starts_with(std::env::temp_dir()),
"staged dir should live under the temp directory"
);
assert_eq!(target, None, "no profile directory should be discovered");
}
#[test]
fn find_staging_directory_handles_custom_profile_under_deps() {
let (staged, target) =
find_staging_directory(std::path::Path::new("/build/custom/deps/pg_worker-a1b2"));
let staged_name = staged.file_name().and_then(|n| n.to_str()).unwrap_or("");
assert!(
staged_name.starts_with("pg-worker-unknown-"),
"staged dir should be named pg-worker-unknown-*, got: {staged_name}"
);
assert_eq!(
target,
Some(PathBuf::from("/build/custom")),
"the deps parent should be treated as the profile directory"
);
}