openzeppelin_relayer/api/routes/
health.rs1use actix_web::{get, web, Responder};
8
9use crate::api::controllers::health;
10use crate::models::DefaultAppState;
11
12#[get("/health")]
18async fn health_route() -> impl Responder {
19 health::health().await
20}
21
22#[get("/ready")]
31async fn readiness_route(data: web::ThinData<DefaultAppState>) -> impl Responder {
32 health::readiness(data).await
33}
34
35pub fn init(cfg: &mut web::ServiceConfig) {
39 cfg.service(health_route);
40 cfg.service(readiness_route);
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46 use actix_web::{http::StatusCode, test, App};
47
48 #[actix_web::test]
53 async fn test_health_route_registered() {
54 let app = test::init_service(App::new().configure(init)).await;
55
56 let req = test::TestRequest::get().uri("/health").to_request();
57 let resp = test::call_service(&app, req).await;
58
59 assert_eq!(resp.status(), StatusCode::OK);
60 let body = test::read_body(resp).await;
61 assert_eq!(body, "OK");
62 }
63
64 #[actix_web::test]
65 async fn test_ready_route_registered() {
66 let app = test::init_service(App::new().configure(init)).await;
67
68 let req = test::TestRequest::get().uri("/ready").to_request();
69 let resp = test::call_service(&app, req).await;
70
71 assert_ne!(resp.status(), StatusCode::NOT_FOUND);
73 }
74
75 #[actix_web::test]
80 async fn test_health_rejects_post() {
81 let app = test::init_service(App::new().configure(init)).await;
82
83 let req = test::TestRequest::post().uri("/health").to_request();
84 let resp = test::call_service(&app, req).await;
85
86 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
88 }
89
90 #[actix_web::test]
91 async fn test_ready_rejects_post() {
92 let app = test::init_service(App::new().configure(init)).await;
93
94 let req = test::TestRequest::post().uri("/ready").to_request();
95 let resp = test::call_service(&app, req).await;
96
97 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
98 }
99
100 #[actix_web::test]
105 async fn test_ready_returns_json_content_type() {
106 let app = test::init_service(App::new().configure(init)).await;
107
108 let req = test::TestRequest::get().uri("/ready").to_request();
109 let resp = test::call_service(&app, req).await;
110
111 if resp.status() != StatusCode::INTERNAL_SERVER_ERROR {
113 let content_type = resp.headers().get("content-type");
114 assert!(content_type.is_some(), "Should have content-type header");
115 assert!(
116 content_type
117 .unwrap()
118 .to_str()
119 .unwrap()
120 .contains("application/json"),
121 "Content-Type should be application/json"
122 );
123 }
124 }
125
126 #[actix_web::test]
131 async fn test_health_is_stable_across_requests() {
132 let app = test::init_service(App::new().configure(init)).await;
133
134 for _ in 0..5 {
135 let req = test::TestRequest::get().uri("/health").to_request();
136 let resp = test::call_service(&app, req).await;
137
138 assert_eq!(resp.status(), StatusCode::OK);
139 let body = test::read_body(resp).await;
140 assert_eq!(body, "OK");
141 }
142 }
143
144 #[actix_web::test]
149 async fn test_ready_returns_valid_status_code() {
150 let app = test::init_service(App::new().configure(init)).await;
151
152 let req = test::TestRequest::get().uri("/ready").to_request();
153 let resp = test::call_service(&app, req).await;
154
155 let status = resp.status().as_u16();
156 assert!(
158 status == 200 || status == 503 || status == 500,
159 "Status should be 200, 503, or 500, got {status}"
160 );
161 }
162
163 #[actix_web::test]
164 async fn test_ready_response_is_valid_json() {
165 let app = test::init_service(App::new().configure(init)).await;
166
167 let req = test::TestRequest::get().uri("/ready").to_request();
168 let resp = test::call_service(&app, req).await;
169
170 let status = resp.status().as_u16();
171 if status == 200 || status == 503 {
173 let body = test::read_body(resp).await;
174 let body_str = String::from_utf8(body.to_vec()).unwrap();
175 let json: serde_json::Value =
176 serde_json::from_str(&body_str).expect("Response should be valid JSON");
177
178 assert!(json.get("ready").is_some(), "Should have 'ready' field");
180 assert!(json.get("status").is_some(), "Should have 'status' field");
181 assert!(
182 json.get("components").is_some(),
183 "Should have 'components' field"
184 );
185 assert!(
186 json.get("timestamp").is_some(),
187 "Should have 'timestamp' field"
188 );
189 }
190 }
191
192 #[actix_web::test]
193 async fn test_ready_status_code_correlates_with_ready_field() {
194 let app = test::init_service(App::new().configure(init)).await;
195
196 let req = test::TestRequest::get().uri("/ready").to_request();
197 let resp = test::call_service(&app, req).await;
198
199 let status_code = resp.status().as_u16();
200
201 if status_code == 200 || status_code == 503 {
203 let body = test::read_body(resp).await;
204 let body_str = String::from_utf8(body.to_vec()).unwrap();
205 let json: serde_json::Value = serde_json::from_str(&body_str).unwrap();
206
207 let ready = json["ready"].as_bool().unwrap();
208
209 if ready {
210 assert_eq!(status_code, 200, "ready=true should return 200");
211 } else {
212 assert_eq!(status_code, 503, "ready=false should return 503");
213 }
214 }
215 }
216
217 #[actix_web::test]
218 async fn test_ready_components_have_required_fields() {
219 let app = test::init_service(App::new().configure(init)).await;
220
221 let req = test::TestRequest::get().uri("/ready").to_request();
222 let resp = test::call_service(&app, req).await;
223
224 let status = resp.status().as_u16();
225 if status == 200 || status == 503 {
226 let body = test::read_body(resp).await;
227 let body_str = String::from_utf8(body.to_vec()).unwrap();
228 let json: serde_json::Value = serde_json::from_str(&body_str).unwrap();
229
230 let components = &json["components"];
231
232 assert!(components.get("system").is_some());
234 let system = &components["system"];
235 assert!(system.get("status").is_some());
236 assert!(system.get("fd_count").is_some());
237 assert!(system.get("fd_limit").is_some());
238
239 assert!(components.get("redis").is_some());
241 let redis = &components["redis"];
242 assert!(redis.get("status").is_some());
243 assert!(redis.get("primary_pool").is_some());
244 assert!(redis.get("reader_pool").is_some());
245
246 assert!(components.get("queue").is_some());
248 let queue = &components["queue"];
249 assert!(queue.get("status").is_some());
250 }
251 }
252
253 #[actix_web::test]
254 async fn test_ready_timestamp_is_rfc3339() {
255 let app = test::init_service(App::new().configure(init)).await;
256
257 let req = test::TestRequest::get().uri("/ready").to_request();
258 let resp = test::call_service(&app, req).await;
259
260 let status = resp.status().as_u16();
261 if status == 200 || status == 503 {
262 let body = test::read_body(resp).await;
263 let body_str = String::from_utf8(body.to_vec()).unwrap();
264 let json: serde_json::Value = serde_json::from_str(&body_str).unwrap();
265
266 let timestamp = json["timestamp"].as_str().unwrap();
267 chrono::DateTime::parse_from_rfc3339(timestamp)
268 .expect("Timestamp should be valid RFC3339");
269 }
270 }
271
272 #[actix_web::test]
273 async fn test_ready_status_values_are_valid() {
274 let app = test::init_service(App::new().configure(init)).await;
275
276 let req = test::TestRequest::get().uri("/ready").to_request();
277 let resp = test::call_service(&app, req).await;
278
279 let status = resp.status().as_u16();
280 if status == 200 || status == 503 {
281 let body = test::read_body(resp).await;
282 let body_str = String::from_utf8(body.to_vec()).unwrap();
283 let json: serde_json::Value = serde_json::from_str(&body_str).unwrap();
284
285 let valid_statuses = ["healthy", "degraded", "unhealthy"];
286
287 let overall_status = json["status"].as_str().unwrap();
289 assert!(
290 valid_statuses.contains(&overall_status),
291 "Overall status '{overall_status}' should be valid"
292 );
293
294 let system_status = json["components"]["system"]["status"].as_str().unwrap();
296 let redis_status = json["components"]["redis"]["status"].as_str().unwrap();
297 let queue_status = json["components"]["queue"]["status"].as_str().unwrap();
298
299 assert!(valid_statuses.contains(&system_status));
300 assert!(valid_statuses.contains(&redis_status));
301 assert!(valid_statuses.contains(&queue_status));
302 }
303 }
304
305 #[actix_web::test]
306 async fn test_ready_plugins_field_is_optional() {
307 let app = test::init_service(App::new().configure(init)).await;
308
309 let req = test::TestRequest::get().uri("/ready").to_request();
310 let resp = test::call_service(&app, req).await;
311
312 let status = resp.status().as_u16();
313 if status == 200 || status == 503 {
314 let body = test::read_body(resp).await;
315 let body_str = String::from_utf8(body.to_vec()).unwrap();
316 let json: serde_json::Value = serde_json::from_str(&body_str).unwrap();
317
318 if let Some(plugins) = json["components"].get("plugins") {
320 if !plugins.is_null() {
321 assert!(plugins.get("status").is_some());
323 assert!(plugins.get("enabled").is_some());
324 }
325 }
326 }
327 }
328}