use hyper::StatusCode;
use crate::error::Result;
use crate::http::Response;
use crate::orm::Db;
pub(crate) async fn healthz(db: &Db) -> Result<Response> {
let db_ok = sqlx::query("SELECT 1").execute(db.pool()).await.is_ok();
Ok(build_response(db_ok))
}
fn build_response(db_ok: bool) -> Response {
let status = if db_ok {
StatusCode::OK
} else {
StatusCode::SERVICE_UNAVAILABLE
};
let body = serde_json::json!({
"ok": db_ok,
"db": if db_ok { "up" } else { "down" },
"version": env!("CARGO_PKG_VERSION"),
});
let s = serde_json::to_string(&body).unwrap_or_else(|_| {
format!(
r#"{{"ok":{db_ok},"db":"{}","version":"unknown"}}"#,
if db_ok { "up" } else { "down" }
)
});
Response::new(status, s)
.with_header("content-type", "application/json")
.with_header("cache-control", "no-store")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ok_response_is_200_with_db_up() {
let resp = build_response(true);
assert_eq!(resp.status.as_u16(), 200);
let body = std::str::from_utf8(&resp.body).expect("utf-8");
let v: serde_json::Value = serde_json::from_str(body).expect("valid json");
assert_eq!(v["ok"], true);
assert_eq!(v["db"], "up");
assert!(
v["version"].as_str().is_some_and(|s| !s.is_empty()),
"version field missing or empty: {body}",
);
}
#[test]
fn down_response_is_503_with_db_down() {
let resp = build_response(false);
assert_eq!(resp.status.as_u16(), 503);
let v: serde_json::Value = serde_json::from_slice(&resp.body).expect("valid json");
assert_eq!(v["ok"], false);
assert_eq!(v["db"], "down");
}
#[test]
fn response_headers_carry_json_and_no_store() {
let resp = build_response(true);
let get = |name: &str| {
resp.headers
.iter()
.find(|(k, _)| k.eq_ignore_ascii_case(name))
.map(|(_, v)| v.as_str())
.unwrap_or("")
};
assert_eq!(get("content-type"), "application/json");
assert_eq!(get("cache-control"), "no-store");
}
}