//! 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 { 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 { 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)); } }