use reqwest::Client;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::process::Command;
use std::sync::Once;
pub fn get_wiremock_base_url() -> String {
std::env::var("WIREMOCK_URL").unwrap_or_else(|_| "http://localhost:8080".to_string())
}
fn get_wiremock_admin_url() -> String {
format!("{}/__admin", get_wiremock_base_url())
}
const WIREMOCK_COMPOSE_FILE: &str = "wiremock/docker-compose.test.yml";
static START: Once = Once::new();
fn is_wiremock_running() -> bool {
Command::new("curl")
.args(["-s", "-f", &format!("{}/health", get_wiremock_admin_url())])
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
#[ctor::ctor]
fn start_wiremock() {
if std::env::var("RUN_WIRE_TESTS").as_deref() == Ok("true") {
if std::env::var("WIREMOCK_URL").is_ok() {
return;
}
START.call_once(|| {
if !std::path::Path::new(WIREMOCK_COMPOSE_FILE).exists() {
return;
}
if is_wiremock_running() {
return;
}
println!("Starting WireMock container...");
let status = Command::new("docker")
.args(["compose", "-f", WIREMOCK_COMPOSE_FILE, "up", "-d", "--wait"])
.status();
if let Err(e) = status {
eprintln!("Failed to start WireMock container: {}", e);
return;
}
let port_output = Command::new("docker")
.args([
"compose",
"-f",
WIREMOCK_COMPOSE_FILE,
"port",
"wiremock",
"8080",
])
.output();
if let Ok(output) = port_output {
let port_str = String::from_utf8_lossy(&output.stdout);
let port_str = port_str.trim();
if let Some(port) = port_str.split(':').last() {
let url = format!("http://localhost:{}", port.trim());
std::env::set_var("WIREMOCK_URL", &url);
println!("WireMock container is ready at {}", url);
}
}
});
}
}
pub async fn reset_wiremock_requests() -> Result<(), Box<dyn std::error::Error>> {
Client::new()
.delete(format!("{}/requests", get_wiremock_admin_url()))
.send()
.await?;
Ok(())
}
pub async fn verify_request_count(
method: &str,
url_path: &str,
query_params: Option<HashMap<String, serde_json::Value>>,
expected: usize,
) -> Result<(), Box<dyn std::error::Error>> {
let mut request_body = json!({
"method": method,
"urlPath": url_path,
});
if let Some(params) = query_params {
let query_parameters: Value = params
.into_iter()
.map(|(k, v)| {
let matcher = if v.is_array() {
let items: Vec<Value> = v
.as_array()
.unwrap()
.iter()
.filter_map(|item| item.as_str())
.map(|s| json!({"equalTo": s}))
.collect();
json!({"hasExactly": items})
} else if let Some(s) = v.as_str() {
json!({"equalTo": s})
} else {
json!({"equalTo": v.to_string()})
};
(k, matcher)
})
.collect();
request_body["queryParameters"] = query_parameters;
}
let response = Client::new()
.post(format!("{}/requests/find", get_wiremock_admin_url()))
.json(&request_body)
.send()
.await?;
let result: Value = response.json().await?;
let requests = result["requests"]
.as_array()
.ok_or("Invalid response from WireMock")?;
assert_eq!(
requests.len(),
expected,
"Expected {} requests, found {}",
expected,
requests.len()
);
Ok(())
}