summaryrefslogtreecommitdiff
path: root/src/test_support.rs
blob: d6a0a960fbfcfe7279f7f9c2bc55b410fa1309af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//! 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<Server>,
    shutdown: impl std::future::Future<Output = ()> + 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;
}