use reqwest::Client;
use serde_json::Value;
use std::time::Duration;
pub const TEST_API_URL: &str = "http://localhost:3021";
pub fn admin_email() -> String {
std::env::var("AXONML_TEST_ADMIN_EMAIL").unwrap_or_else(|_| "admin@axonml.local".to_string())
}
pub fn admin_password() -> String {
std::env::var("AXONML_TEST_ADMIN_PASSWORD").unwrap_or_else(|_| "admin".to_string())
}
pub async fn route_exists(client: &Client, path: &str, token: &str) -> bool {
let response = client
.get(format!("{}{}", TEST_API_URL, path))
.header("Authorization", format!("Bearer {}", token))
.send()
.await;
match response {
Ok(resp) => resp.status().as_u16() != 404,
Err(_) => false,
}
}
pub async fn post_route_exists(client: &Client, path: &str, token: &str) -> bool {
let response = client
.post(format!("{}{}", TEST_API_URL, path))
.header("Authorization", format!("Bearer {}", token))
.header("Content-Type", "application/json")
.body("{}")
.send()
.await;
match response {
Ok(resp) => resp.status().as_u16() != 404,
Err(_) => false,
}
}
pub fn test_client() -> Client {
Client::builder()
.timeout(Duration::from_secs(30))
.build()
.expect("Failed to create test client")
}
pub async fn login(client: &Client, email: &str, password: &str) -> Result<String, String> {
let response = client
.post(format!("{}/api/auth/login", TEST_API_URL))
.json(&serde_json::json!({
"email": email,
"password": password
}))
.send()
.await
.map_err(|e| format!("Request failed: {}", e))?;
if !response.status().is_success() {
return Err(format!("Login failed: {}", response.status()));
}
let body: Value = response
.json()
.await
.map_err(|e| format!("Failed to parse response: {}", e))?;
body.get("access_token")
.or_else(|| body.get("token"))
.and_then(|t| t.as_str())
.map(|s| s.to_string())
.ok_or_else(|| "No token in response".to_string())
}
pub async fn login_as_admin(client: &Client) -> Result<String, String> {
login(client, &admin_email(), &admin_password()).await
}
pub async fn auth_get(
client: &Client,
path: &str,
token: &str,
) -> Result<reqwest::Response, String> {
client
.get(format!("{}{}", TEST_API_URL, path))
.header("Authorization", format!("Bearer {}", token))
.send()
.await
.map_err(|e| format!("Request failed: {}", e))
}
pub async fn auth_post(
client: &Client,
path: &str,
token: &str,
body: Value,
) -> Result<reqwest::Response, String> {
client
.post(format!("{}{}", TEST_API_URL, path))
.header("Authorization", format!("Bearer {}", token))
.json(&body)
.send()
.await
.map_err(|e| format!("Request failed: {}", e))
}
pub async fn auth_put(
client: &Client,
path: &str,
token: &str,
body: Value,
) -> Result<reqwest::Response, String> {
client
.put(format!("{}{}", TEST_API_URL, path))
.header("Authorization", format!("Bearer {}", token))
.json(&body)
.send()
.await
.map_err(|e| format!("Request failed: {}", e))
}
pub async fn auth_delete(
client: &Client,
path: &str,
token: &str,
) -> Result<reqwest::Response, String> {
client
.delete(format!("{}{}", TEST_API_URL, path))
.header("Authorization", format!("Bearer {}", token))
.send()
.await
.map_err(|e| format!("Request failed: {}", e))
}
pub async fn is_server_running() -> bool {
let client = test_client();
client
.get(format!("{}/health", TEST_API_URL))
.send()
.await
.map(|r| r.status().is_success())
.unwrap_or(false)
}
#[macro_export]
macro_rules! require_server {
() => {
if !common::is_server_running().await {
eprintln!(
"SKIP: axonml-server not running on {}",
common::TEST_API_URL
);
return;
}
let _check_client = common::test_client();
if common::login_as_admin(&_check_client).await.is_err() {
eprintln!("SKIP: admin login failed (run AxonML_DB_Init.sh to set up test user)");
return;
}
};
}