use std::time::Instant;
use axum::{Extension, Json};
use serde::Serialize;
#[derive(Clone, Debug)]
pub struct ApiHealthState {
pub version: &'static str,
pub started_at: Instant,
}
impl ApiHealthState {
pub fn new() -> Self {
Self {
version: env!("CARGO_PKG_VERSION"),
started_at: Instant::now(),
}
}
}
impl Default for ApiHealthState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Serialize, PartialEq, Eq)]
pub struct ApiHealthBody {
pub status: String,
pub version: String,
pub api_version: String,
pub uptime_secs: u64,
}
pub async fn api_health(Extension(state): Extension<ApiHealthState>) -> Json<ApiHealthBody> {
Json(ApiHealthBody {
status: "ok".to_string(),
version: state.version.to_string(),
api_version: "v1".to_string(),
uptime_secs: state.started_at.elapsed().as_secs(),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn body_serialises_to_documented_shape() {
let body = ApiHealthBody {
status: "ok".into(),
version: "0.0.1".into(),
api_version: "v1".into(),
uptime_secs: 7,
};
let json = serde_json::to_value(&body).expect("ApiHealthBody must serialise");
assert_eq!(json["status"], "ok");
assert_eq!(json["version"], "0.0.1");
assert_eq!(json["api_version"], "v1");
assert_eq!(json["uptime_secs"], 7);
}
#[tokio::test]
async fn handler_returns_ok_body() {
let Json(body) = api_health(Extension(ApiHealthState::new())).await;
assert_eq!(body.status, "ok");
assert_eq!(body.api_version, "v1");
assert_eq!(body.version, env!("CARGO_PKG_VERSION"));
}
}