use std::future::Future;
use axum::{routing::get, Router};
use crate::HealthResponse;
pub async fn liveness() -> HealthResponse {
HealthResponse::ok()
}
pub fn health_routes<S, F, Fut>(readiness: F) -> Router<S>
where
S: Clone + Send + Sync + 'static,
F: Fn() -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = HealthResponse> + Send + 'static,
{
#[allow(clippy::redundant_closure)]
let readyz = move || readiness();
Router::new()
.route("/healthz", get(liveness))
.route("/readyz", get(readyz))
}
#[cfg(test)]
mod tests {
use super::*;
use axum::{body::Body, http::Request, http::StatusCode};
use tower::ServiceExt;
async fn call(app: Router, path: &str) -> (StatusCode, serde_json::Value) {
let res = app
.oneshot(Request::builder().uri(path).body(Body::empty()).unwrap())
.await
.unwrap();
let status = res.status();
let bytes = axum::body::to_bytes(res.into_body(), usize::MAX)
.await
.unwrap();
let body = serde_json::from_slice(&bytes).unwrap_or(serde_json::Value::Null);
(status, body)
}
#[tokio::test]
async fn healthz_is_always_ok() {
let app: Router = health_routes(|| async { HealthResponse::unhealthy() });
let (status, body) = call(app, "/healthz").await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body["status"], "ok");
}
#[tokio::test]
async fn readyz_reports_ready() {
let app: Router = health_routes(|| async { HealthResponse::ok() });
let (status, body) = call(app, "/readyz").await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body["status"], "ok");
}
#[tokio::test]
async fn readyz_reports_unhealthy() {
let app: Router = health_routes(|| async { HealthResponse::unhealthy() });
let (status, body) = call(app, "/readyz").await;
assert_eq!(status, StatusCode::SERVICE_UNAVAILABLE);
assert_eq!(body["status"], "unhealthy");
}
#[tokio::test]
async fn merges_into_stateful_app() {
#[derive(Clone)]
struct AppState;
let app: Router<AppState> =
Router::new().merge(health_routes(|| async { liveness().await }));
let app: Router = app.with_state(AppState);
let (status, _) = call(app, "/healthz").await;
assert_eq!(status, StatusCode::OK);
}
}