1use axum::{
7 http::StatusCode,
8 response::{IntoResponse, Response},
9 Json,
10};
11use serde::Serialize;
12
13#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)]
17#[serde(rename_all = "snake_case")]
18pub enum LivenessStatus {
19 Ok,
20}
21
22#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
23pub struct LivenessBody {
24 pub status: LivenessStatus,
25}
26
27impl Default for LivenessBody {
28 fn default() -> Self {
29 Self {
30 status: LivenessStatus::Ok,
31 }
32 }
33}
34
35#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)]
39#[serde(rename_all = "snake_case")]
40pub enum ReadinessStatus {
41 Ready,
42 NotReady,
43}
44
45#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
46pub struct ReadinessBody {
47 pub status: ReadinessStatus,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub reason: Option<String>,
50}
51
52pub fn liveness_response() -> Response {
54 Json(LivenessBody::default()).into_response()
55}
56
57pub fn readiness_response<E: std::fmt::Display>(result: Result<(), E>) -> Response {
62 match result {
63 Ok(()) => (
64 StatusCode::OK,
65 Json(ReadinessBody {
66 status: ReadinessStatus::Ready,
67 reason: None,
68 }),
69 )
70 .into_response(),
71 Err(error) => (
72 StatusCode::SERVICE_UNAVAILABLE,
73 Json(ReadinessBody {
74 status: ReadinessStatus::NotReady,
75 reason: Some(error.to_string()),
76 }),
77 )
78 .into_response(),
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use axum::body::to_bytes;
85
86 use super::*;
87
88 #[tokio::test]
89 async fn liveness_response_reports_ok() {
90 let response = liveness_response();
91 assert_eq!(response.status(), StatusCode::OK);
92 let bytes = to_bytes(response.into_body(), usize::MAX).await.unwrap();
93 let value: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
94 assert_eq!(value["status"], "ok");
95 }
96
97 #[tokio::test]
98 async fn readiness_response_ok_reports_ready() {
99 let response = readiness_response::<std::convert::Infallible>(Ok(()));
100 assert_eq!(response.status(), StatusCode::OK);
101 let bytes = to_bytes(response.into_body(), usize::MAX).await.unwrap();
102 let value: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
103 assert_eq!(value["status"], "ready");
104 assert!(value.get("reason").is_none());
105 }
106
107 #[tokio::test]
108 async fn readiness_response_err_reports_reason_and_503() {
109 let response = readiness_response(Err("upstream unreachable"));
110 assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
111 let bytes = to_bytes(response.into_body(), usize::MAX).await.unwrap();
112 let value: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
113 assert_eq!(value["status"], "not_ready");
114 assert_eq!(value["reason"], "upstream unreachable");
115 }
116}