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("observability/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"); for required in ["grafana", "victoria-metrics", "vmagent", "loki", "tempo"] { assert!( services.contains_key(serde_yaml::Value::String(required.to_string())), "missing service {required}" ); } } #[tokio::test] #[ignore] async fn docker_compose_config_validation_is_gated_and_fast() { let enabled = std::env::var("CONTROL_TEST_DOCKER").ok(); assert_eq!(enabled.as_deref(), Some("1")); let root = repo_root(); let compose = root.join("observability/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) ); }