Skip to main content

bhtune_server/routes/
health.rs

1//! `GET /api/health` -- an unauthenticated liveness probe, deliberately the only endpoint
2//! that touches neither [`crate::state::AppState`] nor the database: a load balancer or the
3//! Windows Service manager (`server-windows-service`) needs to be able to tell "the process
4//! is up and answering HTTP" apart from "the process is up but the database is unreachable"
5//! (the latter would fail on essentially every other route already). It also exposes the
6//! server package version for the web application's shell.
7
8use axum::Json;
9use axum::routing::get;
10use serde::Serialize;
11use utoipa::ToSchema;
12
13use crate::state::AppState;
14
15#[derive(Serialize, ToSchema)]
16pub(crate) struct Health {
17    status: &'static str,
18    version: &'static str,
19}
20
21/// Liveness probe.
22#[utoipa::path(
23    get,
24    path = "/api/health",
25    tag = "health",
26    responses(
27        (
28            status = 200,
29            description = "The process is up and answering HTTP, with its application version.",
30            body = Health
31        ),
32    ),
33)]
34pub(crate) async fn health() -> Json<Health> {
35    Json(Health {
36        status: "ok",
37        version: env!("CARGO_PKG_VERSION"),
38    })
39}
40
41pub fn router() -> axum::Router<AppState> {
42    axum::Router::new().route("/api/health", get(health))
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use axum::body::{Body, to_bytes};
49    use axum::http::{Request, StatusCode};
50    use tower::ServiceExt;
51
52    #[tokio::test]
53    async fn health_returns_200_and_ok_status() {
54        let app = router().with_state(crate::test_support::in_memory_state().await);
55        let response = app
56            .oneshot(Request::get("/api/health").body(Body::empty()).unwrap())
57            .await
58            .unwrap();
59        assert_eq!(response.status(), StatusCode::OK);
60        let bytes = to_bytes(response.into_body(), usize::MAX).await.unwrap();
61        let body: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
62        assert_eq!(
63            body,
64            serde_json::json!({
65                "status": "ok",
66                "version": env!("CARGO_PKG_VERSION"),
67            })
68        );
69    }
70}