diff --git a/lib/crates/fabro-cli/tests/it/cmd/server_start.rs b/lib/crates/fabro-cli/tests/it/cmd/server_start.rs index 8fd05cbbc..2415c28a2 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/server_start.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/server_start.rs @@ -2,66 +2,10 @@ use std::process::Stdio; use std::sync::{Arc, Barrier}; use std::time::{Duration, Instant}; -use fabro_test::{apply_test_isolation, fabro_snapshot, test_context}; - -fn isolated_storage_dir() -> tempfile::TempDir { - let root = tempfile::tempdir_in("/tmp").unwrap(); - std::fs::create_dir_all(root.path().join("storage")).unwrap(); - root -} - -fn server_log_files(logs_dir: &std::path::Path) -> Vec { - let Ok(entries) = std::fs::read_dir(logs_dir) else { - return Vec::new(); - }; - - entries - .flatten() - .map(|entry| entry.path()) - .filter(|path| { - path.file_name() - .and_then(|name| name.to_str()) - .is_some_and(|name| name.starts_with("server.") && name.ends_with(".log")) - }) - .collect() -} - -fn wait_for_path(path: &std::path::Path) { - let deadline = Instant::now() + Duration::from_secs(5); - while Instant::now() < deadline { - if path.exists() { - return; - } - std::thread::sleep(Duration::from_millis(50)); - } - panic!("timed out waiting for {}", path.display()); -} - -fn wait_for_log_line(path: &std::path::Path, needle: &str) { - let deadline = Instant::now() + Duration::from_secs(5); - while Instant::now() < deadline { - if std::fs::read_to_string(path) - .ok() - .is_some_and(|contents| contents.contains(needle)) - { - return; - } - std::thread::sleep(Duration::from_millis(50)); - } - panic!("timed out waiting for {needle:?} in {}", path.display()); -} - -fn stop_pid(pid: u32) { - fabro_proc::sigterm(pid); - let deadline = Instant::now() + Duration::from_secs(5); - while Instant::now() < deadline { - if !fabro_proc::process_alive(pid) { - return; - } - std::thread::sleep(Duration::from_millis(50)); - } - fabro_proc::sigkill(pid); -} +use fabro_test::{ + apply_test_isolation, fabro_snapshot, isolated_storage_dir, server_log_files, stop_pid, + test_context, wait_for_log_line, wait_for_path, +}; #[test] fn help() { diff --git a/lib/crates/fabro-cli/tests/it/cmd/server_status.rs b/lib/crates/fabro-cli/tests/it/cmd/server_status.rs index 587aa67a0..a29f63d68 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/server_status.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/server_status.rs @@ -1,33 +1,4 @@ -use fabro_test::{fabro_snapshot, test_context}; - -fn isolated_storage_dir() -> tempfile::TempDir { - let root = tempfile::tempdir_in("/tmp").unwrap(); - std::fs::create_dir_all(root.path().join("storage")).unwrap(); - root -} - -fn wait_for_path(path: &std::path::Path) { - let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5); - while std::time::Instant::now() < deadline { - if path.exists() { - return; - } - std::thread::sleep(std::time::Duration::from_millis(50)); - } - panic!("timed out waiting for {}", path.display()); -} - -fn stop_pid(pid: u32) { - fabro_proc::sigterm(pid); - let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5); - while std::time::Instant::now() < deadline { - if !fabro_proc::process_alive(pid) { - return; - } - std::thread::sleep(std::time::Duration::from_millis(50)); - } - fabro_proc::sigkill(pid); -} +use fabro_test::{fabro_snapshot, isolated_storage_dir, stop_pid, test_context, wait_for_path}; #[test] fn help() { diff --git a/lib/crates/fabro-cli/tests/it/cmd/server_stop.rs b/lib/crates/fabro-cli/tests/it/cmd/server_stop.rs index 19ff9f313..a7f0664db 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/server_stop.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/server_stop.rs @@ -1,10 +1,4 @@ -use fabro_test::{fabro_snapshot, test_context}; - -fn isolated_storage_dir() -> tempfile::TempDir { - let root = tempfile::tempdir_in("/tmp").unwrap(); - std::fs::create_dir_all(root.path().join("storage")).unwrap(); - root -} +use fabro_test::{fabro_snapshot, isolated_storage_dir, test_context}; #[test] fn help() { diff --git a/lib/crates/fabro-cli/tests/it/cmd/uninstall.rs b/lib/crates/fabro-cli/tests/it/cmd/uninstall.rs index 346151ed1..daccce700 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/uninstall.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/uninstall.rs @@ -1,7 +1,6 @@ use std::fs; -use std::time::{Duration, Instant}; -use fabro_test::{fabro_snapshot, test_context}; +use fabro_test::{fabro_snapshot, stop_pid, test_context, wait_for_path}; use serde_json::Value; #[test] @@ -38,29 +37,6 @@ fn command_with_no_fabro_home(context: &fabro_test::TestContext) -> assert_cmd:: cmd } -fn wait_for_path(path: &std::path::Path) { - let deadline = Instant::now() + Duration::from_secs(5); - while Instant::now() < deadline { - if path.exists() { - return; - } - std::thread::sleep(Duration::from_millis(50)); - } - panic!("timed out waiting for {}", path.display()); -} - -fn stop_pid(pid: u32) { - fabro_proc::sigterm(pid); - let deadline = Instant::now() + Duration::from_secs(5); - while Instant::now() < deadline { - if !fabro_proc::process_alive(pid) { - return; - } - std::thread::sleep(Duration::from_millis(50)); - } - fabro_proc::sigkill(pid); -} - #[test] fn not_installed_prints_message() { let context = test_context!(); diff --git a/lib/crates/fabro-test/src/lib.rs b/lib/crates/fabro-test/src/lib.rs index fa5af894b..4eee2c974 100644 --- a/lib/crates/fabro-test/src/lib.rs +++ b/lib/crates/fabro-test/src/lib.rs @@ -150,6 +150,75 @@ fn apply_test_isolation_with_lookup( cmd.env(TEST_IN_MEMORY_STORE_ENV, "1"); } +/// Create a fresh tempdir containing an empty `storage/` subdirectory, for +/// isolating server lifecycle tests from the shared nextest session storage. +#[must_use] +pub fn isolated_storage_dir() -> tempfile::TempDir { + let root = tempfile::tempdir_in("/tmp").expect("tempdir under /tmp"); + std::fs::create_dir_all(root.path().join("storage")).expect("create storage dir"); + root +} + +/// Poll up to 5s for a path to appear; panic on timeout. +pub fn wait_for_path(path: &Path) { + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5); + while std::time::Instant::now() < deadline { + if path.exists() { + return; + } + std::thread::sleep(std::time::Duration::from_millis(50)); + } + panic!("timed out waiting for {}", path.display()); +} + +/// Poll up to 5s for `needle` to appear in the contents of `path`; panic on +/// timeout. +pub fn wait_for_log_line(path: &Path, needle: &str) { + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5); + while std::time::Instant::now() < deadline { + if std::fs::read_to_string(path) + .ok() + .is_some_and(|contents| contents.contains(needle)) + { + return; + } + std::thread::sleep(std::time::Duration::from_millis(50)); + } + panic!("timed out waiting for {needle:?} in {}", path.display()); +} + +/// SIGTERM the pid, wait up to 5s for it to exit, then SIGKILL if still alive. +pub fn stop_pid(pid: u32) { + fabro_proc::sigterm(pid); + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5); + while std::time::Instant::now() < deadline { + if !fabro_proc::process_alive(pid) { + return; + } + std::thread::sleep(std::time::Duration::from_millis(50)); + } + fabro_proc::sigkill(pid); +} + +/// List any `server.*.log` files under `logs_dir`. Used by server-lifecycle +/// tests to assert that no server logs leak into the home logs directory. +#[must_use] +pub fn server_log_files(logs_dir: &Path) -> Vec { + let Ok(entries) = std::fs::read_dir(logs_dir) else { + return Vec::new(); + }; + + entries + .flatten() + .map(|entry| entry.path()) + .filter(|path| { + path.file_name() + .and_then(|name| name.to_str()) + .is_some_and(|name| name.starts_with("server.") && name.ends_with(".log")) + }) + .collect() +} + /// A test context for running fabro CLI commands. /// /// Each context gets isolated home/temp directories. The storage directory is