use std::{fs, path::PathBuf, time::Duration}; 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() } #[test] fn docker_compose_files_parse_and_include_required_services() { let root = repo_root(); let compose = fs::read_to_string(root.join("docker-compose.yml")).unwrap(); let v: serde_yaml::Value = serde_yaml::from_str(&compose).unwrap(); let services = v .get("services") .and_then(|x| x.as_mapping()) .expect("missing services"); // Core + optional observability services are all declared in one compose file. for required in [ "grafana", "victoria-metrics", "vmagent", "loki", "tempo", "mailhog", ] { assert!( services.contains_key(serde_yaml::Value::String(required.to_string())), "missing service {required}" ); } } #[tokio::test] async fn docker_compose_config_validation_is_gated_and_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 compose validation"); return; } let root = repo_root(); let compose = root.join("docker-compose.yml"); let cmd = tokio::process::Command::new("docker") .args(["compose", "-f"]) .arg(&compose) .args(["config"]) .output(); let out = tokio::time::timeout(Duration::from_secs(10), cmd) .await .expect("docker compose config timed out") .expect("failed to run docker compose config"); assert!( out.status.success(), "docker compose config failed: {}", String::from_utf8_lossy(&out.stderr) ); // Validate full-stack profile wiring too. let cmd = tokio::process::Command::new("docker") .args(["compose", "-f"]) .arg(&compose) .args(["--profile", "observability", "config"]) .output(); let out = tokio::time::timeout(Duration::from_secs(10), cmd) .await .expect("docker compose config (observability profile) timed out") .expect("failed to run docker compose config (observability profile)"); assert!( out.status.success(), "docker compose config (observability profile) failed: {}", String::from_utf8_lossy(&out.stderr) ); }