//! Test support utilities shared between unit and integration tests. //! //! Gated behind `cfg(any(test, feature = "integration"))`. //! Provides thin wrappers around `pub(crate)` server internals so integration //! tests can start a real server on a random port without exposing internal APIs, //! plus common helpers (temp dirs, cleanup) used across unit test modules. #![allow(clippy::unwrap_used, clippy::expect_used)] use crate::server::{AppState, run_with_server}; use std::path::{Path, PathBuf}; use std::sync::Arc; use tiny_http::Server; /// Start the HTTP server, returning a `JoinHandle` for the request-handling thread. /// /// The server shuts down when `shutdown` resolves (calls `server.unblock()`). /// Callers should join the handle after triggering shutdown. pub fn run_server( state: AppState, server: Arc, shutdown: impl std::future::Future + Send + 'static, ) -> std::thread::JoinHandle<()> { run_with_server(state, server, shutdown) } /// Install the SIGHUP configuration-reload handler for `state`. /// /// Call this before sending SIGHUP in tests that exercise hot-reload. /// It replaces the default signal disposition (terminate) with the production /// reload handler, so the process stays alive after receiving the signal. pub fn setup_sighup_handler(state: &AppState) { crate::server::setup_sighup_handler(state.clone()); } /// Generate a unique ID for test isolation (timestamp + counter). /// /// # Panics /// /// Panics if the system clock is before the Unix epoch. pub fn uuid() -> String { use std::sync::atomic::{AtomicU64, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; static COUNTER: AtomicU64 = AtomicU64::new(0); let duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let count = COUNTER.fetch_add(1, Ordering::SeqCst); format!( "{}-{}-{}", duration.as_secs(), duration.subsec_nanos(), count ) } /// Create a unique temporary directory for a test. /// /// # Panics /// /// Panics if the directory cannot be created. pub async fn temp_dir(prefix: &str) -> PathBuf { let dir = std::env::temp_dir().join(format!("witryna-{}-{}", prefix, uuid())); tokio::fs::create_dir_all(&dir).await.unwrap(); dir } /// Remove a temporary directory (ignores errors). pub async fn cleanup(dir: &Path) { let _ = tokio::fs::remove_dir_all(dir).await; }