use std::{ net::TcpStream, path::PathBuf, process::Command, time::{Duration, Instant}, }; fn repo_root() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .parent() .and_then(|p| p.parent()) .expect("api crate should live under repo root") .to_path_buf() } fn wait_for_tcp(addr: &str, timeout: Duration) -> bool { let start = Instant::now(); while start.elapsed() < timeout { if TcpStream::connect_timeout( &addr.parse().expect("invalid socket addr"), Duration::from_secs(1), ) .is_ok() { return true; } std::thread::sleep(Duration::from_millis(250)); } false } #[test] fn observability_stack_reaches_healthy_state_fast() { let enabled = std::env::var("CONTROL_TEST_DOCKER").ok(); if enabled.as_deref() != Some("1") { eprintln!("skipping: set CONTROL_TEST_DOCKER=1 to enable docker observability smoke test"); return; } let root = repo_root(); let compose = root.join("observability/docker-compose.yml"); let up = Command::new("docker") .args(["compose", "-f"]) .arg(&compose) .args(["up", "-d"]) .status() .expect("failed to run docker compose up"); assert!(up.success(), "docker compose up failed"); let ok = wait_for_tcp("127.0.0.1:3000", Duration::from_secs(30)) && wait_for_tcp("127.0.0.1:8428", Duration::from_secs(30)) && wait_for_tcp("127.0.0.1:3100", Duration::from_secs(30)) && wait_for_tcp("127.0.0.1:3200", Duration::from_secs(30)); let _ = Command::new("docker") .args(["compose", "-f"]) .arg(&compose) .args(["down", "-v"]) .status(); assert!(ok, "observability stack did not become reachable in time"); }