lesavka/testing/tests/server_main_process_contract.rs

63 lines
1.9 KiB
Rust

//! Integration coverage for server process startup behavior.
//!
//! Scope: launch the real `lesavka-server` binary and assert startup reaches a
//! terminal state quickly in this non-gadget test environment.
//! Targets: `server/src/main.rs`.
//! Why: process-level boot behavior should remain deterministic when required
//! gadget endpoints are unavailable.
use serial_test::serial;
use std::path::PathBuf;
use std::process::Command;
use std::time::{Duration, Instant};
fn candidate_dirs() -> Vec<PathBuf> {
let exe = std::env::current_exe().expect("current exe path");
let mut dirs = Vec::new();
if let Some(parent) = exe.parent() {
dirs.push(parent.to_path_buf());
if let Some(grand) = parent.parent() {
dirs.push(grand.to_path_buf());
}
}
dirs.push(PathBuf::from("target/debug"));
dirs.push(PathBuf::from("target/llvm-cov-target/debug"));
dirs
}
fn find_binary(name: &str) -> Option<PathBuf> {
candidate_dirs()
.into_iter()
.map(|dir| dir.join(name))
.find(|path| path.exists() && path.is_file())
}
#[test]
#[serial]
fn server_binary_exits_quickly_without_hid_nodes() {
let Some(bin) = find_binary("lesavka-server") else {
return;
};
let mut child = Command::new(bin)
.env("LESAVKA_DISABLE_UVC", "1")
.spawn()
.expect("spawn lesavka-server");
let deadline = Instant::now() + Duration::from_secs(3);
loop {
if let Some(status) = child.try_wait().expect("poll child") {
assert!(
!status.success(),
"server unexpectedly succeeded in test environment"
);
break;
}
if Instant::now() >= deadline {
let _ = child.kill();
panic!("server did not terminate within startup timeout");
}
std::thread::sleep(Duration::from_millis(50));
}
}