use actix_web::{HttpResponse, Responder};
use serde::Serialize;
const SERVER_VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Serialize)]
struct HealthStatus {
status: &'static str,
version: &'static str,
}
impl HealthStatus {
fn ok() -> Self {
Self {
status: "ok",
version: SERVER_VERSION,
}
}
}
pub async fn handler() -> impl Responder {
"OK"
}
pub async fn healthz() -> impl Responder {
HttpResponse::Ok().json(HealthStatus::ok())
}
pub async fn readyz() -> impl Responder {
HttpResponse::Ok().json(HealthStatus::ok())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_health_handler_returns_ok() {
let _response = handler().await;
}
#[tokio::test]
async fn healthz_returns_json_status_and_version() {
use actix_web::{test, web, App};
let app = test::init_service(App::new().route("/healthz", web::get().to(healthz))).await;
let req = test::TestRequest::get().uri("/healthz").to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), 200);
let content_type = resp
.headers()
.get("content-type")
.and_then(|value| value.to_str().ok())
.unwrap_or_default()
.to_string();
assert!(
content_type.starts_with("application/json"),
"expected JSON, got {content_type}"
);
let body: serde_json::Value = test::read_body_json(resp).await;
assert_eq!(body["status"], "ok");
assert_eq!(body["version"], SERVER_VERSION);
}
#[tokio::test]
async fn readyz_returns_json_status_and_version() {
use actix_web::{test, web, App};
let app = test::init_service(App::new().route("/readyz", web::get().to(readyz))).await;
let req = test::TestRequest::get().uri("/readyz").to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), 200);
let body: serde_json::Value = test::read_body_json(resp).await;
assert_eq!(body["status"], "ok");
assert_eq!(body["version"], SERVER_VERSION);
}
#[tokio::test]
async fn test_health_handler_responds_to_request() {
use actix_web::{test, web, App};
let app = test::init_service(App::new().route("/health", web::get().to(handler))).await;
let req = test::TestRequest::get().uri("/health").to_request();
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
let body = test::read_body(resp).await;
assert_eq!(body.as_ref(), b"OK");
}
#[tokio::test]
async fn test_health_handler_returns_200_status() {
use actix_web::{test, web, App};
let app = test::init_service(App::new().route("/health", web::get().to(handler))).await;
let req = test::TestRequest::get().uri("/health").to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), 200);
}
#[tokio::test]
async fn test_health_handler_content_type() {
use actix_web::{test, web, App};
let app = test::init_service(App::new().route("/health", web::get().to(handler))).await;
let req = test::TestRequest::get().uri("/health").to_request();
let resp = test::call_service(&app, req).await;
let content_type = resp.headers().get("content-type");
assert!(content_type.is_some());
let content_type_str = content_type.unwrap().to_str().unwrap();
assert!(content_type_str.starts_with("text/plain"));
}
#[tokio::test]
async fn test_health_handler_multiple_requests() {
use actix_web::{test, web, App};
let app = test::init_service(App::new().route("/health", web::get().to(handler))).await;
for _ in 0..10 {
let req = test::TestRequest::get().uri("/health").to_request();
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
let body = test::read_body(resp).await;
assert_eq!(body.as_ref(), b"OK");
}
}
}