1use axum::{
7 body::Body,
8 http::{HeaderValue, Request, Response, StatusCode, header::HeaderName},
9};
10use bytes::Bytes;
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13use std::collections::HashMap;
14use std::future::Future;
15use std::pin::Pin;
16
17#[derive(Clone, Debug)]
24pub struct StaticResponse {
25 pub status: u16,
26 pub headers: Vec<(HeaderName, HeaderValue)>,
27 pub body: Bytes,
28 pub content_type: HeaderValue,
29}
30
31impl StaticResponse {
32 pub fn to_response(&self) -> Response<Body> {
38 let status = StatusCode::from_u16(self.status).unwrap_or(StatusCode::OK);
39 let mut builder = Response::builder()
40 .status(status)
41 .header(axum::http::header::CONTENT_TYPE, self.content_type.clone());
42 for (name, value) in &self.headers {
43 builder = builder.header(name.clone(), value.clone());
44 }
45 builder.body(Body::from(self.body.clone())).unwrap_or_else(|_| {
46 Response::builder()
47 .status(StatusCode::INTERNAL_SERVER_ERROR)
48 .body(Body::from("Failed to build static response"))
49 .expect("fallback response must build")
50 })
51 }
52}
53
54#[derive(Debug, Clone)]
64pub struct RequestData {
65 pub path_params: std::sync::Arc<HashMap<String, String>>,
66 pub query_params: std::sync::Arc<Value>,
67 pub validated_params: Option<std::sync::Arc<Value>>,
69 pub raw_query_params: std::sync::Arc<HashMap<String, Vec<String>>>,
70 pub body: std::sync::Arc<Value>,
71 pub raw_body: Option<bytes::Bytes>,
72 pub headers: std::sync::Arc<HashMap<String, String>>,
73 pub cookies: std::sync::Arc<HashMap<String, String>>,
74 pub method: String,
75 pub path: String,
76 #[cfg(feature = "di")]
78 pub dependencies: Option<std::sync::Arc<spikard_core::di::ResolvedDependencies>>,
79}
80
81impl Serialize for RequestData {
82 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
83 where
84 S: serde::Serializer,
85 {
86 use serde::ser::SerializeStruct;
87 #[cfg(feature = "di")]
88 let field_count = 11;
89 #[cfg(not(feature = "di"))]
90 let field_count = 10;
91
92 let mut state = serializer.serialize_struct("RequestData", field_count)?;
93 state.serialize_field("path_params", &*self.path_params)?;
94 state.serialize_field("query_params", &*self.query_params)?;
95 state.serialize_field("validated_params", &self.validated_params.as_deref())?;
96 state.serialize_field("raw_query_params", &*self.raw_query_params)?;
97 state.serialize_field("body", &*self.body)?;
98 state.serialize_field("raw_body", &self.raw_body.as_ref().map(|b| b.as_ref()))?;
99 state.serialize_field("headers", &*self.headers)?;
100 state.serialize_field("cookies", &*self.cookies)?;
101 state.serialize_field("method", &self.method)?;
102 state.serialize_field("path", &self.path)?;
103
104 #[cfg(feature = "di")]
105 {
106 state.serialize_field("has_dependencies", &self.dependencies.is_some())?;
107 }
108
109 state.end()
110 }
111}
112
113impl<'de> Deserialize<'de> for RequestData {
114 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
115 where
116 D: serde::Deserializer<'de>,
117 {
118 #[derive(Deserialize)]
119 #[serde(field_identifier, rename_all = "snake_case")]
120 enum Field {
121 PathParams,
122 QueryParams,
123 RawQueryParams,
124 ValidatedParams,
125 Body,
126 RawBody,
127 Headers,
128 Cookies,
129 Method,
130 Path,
131 #[cfg(feature = "di")]
132 HasDependencies,
133 }
134
135 struct RequestDataVisitor;
136
137 impl<'de> serde::de::Visitor<'de> for RequestDataVisitor {
138 type Value = RequestData;
139
140 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
141 formatter.write_str("struct RequestData")
142 }
143
144 fn visit_map<V>(self, mut map: V) -> Result<RequestData, V::Error>
145 where
146 V: serde::de::MapAccess<'de>,
147 {
148 let mut path_params = None;
149 let mut query_params = None;
150 let mut raw_query_params = None;
151 let mut validated_params = None;
152 let mut body = None;
153 let mut raw_body = None;
154 let mut headers = None;
155 let mut cookies = None;
156 let mut method = None;
157 let mut path = None;
158
159 while let Some(key) = map.next_key()? {
160 match key {
161 Field::PathParams => {
162 path_params = Some(std::sync::Arc::new(map.next_value()?));
163 }
164 Field::QueryParams => {
165 query_params = Some(std::sync::Arc::new(map.next_value()?));
166 }
167 Field::RawQueryParams => {
168 raw_query_params = Some(std::sync::Arc::new(map.next_value()?));
169 }
170 Field::ValidatedParams => {
171 validated_params = Some(std::sync::Arc::new(map.next_value()?));
172 }
173 Field::Body => {
174 body = Some(std::sync::Arc::new(map.next_value()?));
175 }
176 Field::RawBody => {
177 let bytes_vec: Option<Vec<u8>> = map.next_value()?;
178 raw_body = bytes_vec.map(bytes::Bytes::from);
179 }
180 Field::Headers => {
181 headers = Some(std::sync::Arc::new(map.next_value()?));
182 }
183 Field::Cookies => {
184 cookies = Some(std::sync::Arc::new(map.next_value()?));
185 }
186 Field::Method => {
187 method = Some(map.next_value()?);
188 }
189 Field::Path => {
190 path = Some(map.next_value()?);
191 }
192 #[cfg(feature = "di")]
193 Field::HasDependencies => {
194 let _: bool = map.next_value()?;
195 }
196 }
197 }
198
199 Ok(RequestData {
200 path_params: path_params.ok_or_else(|| serde::de::Error::missing_field("path_params"))?,
201 query_params: query_params.ok_or_else(|| serde::de::Error::missing_field("query_params"))?,
202 raw_query_params: raw_query_params
203 .ok_or_else(|| serde::de::Error::missing_field("raw_query_params"))?,
204 validated_params,
205 body: body.ok_or_else(|| serde::de::Error::missing_field("body"))?,
206 raw_body,
207 headers: headers.ok_or_else(|| serde::de::Error::missing_field("headers"))?,
208 cookies: cookies.ok_or_else(|| serde::de::Error::missing_field("cookies"))?,
209 method: method.ok_or_else(|| serde::de::Error::missing_field("method"))?,
210 path: path.ok_or_else(|| serde::de::Error::missing_field("path"))?,
211 #[cfg(feature = "di")]
212 dependencies: None,
213 })
214 }
215 }
216
217 #[cfg(feature = "di")]
218 const FIELDS: &[&str] = &[
219 "path_params",
220 "query_params",
221 "validated_params",
222 "raw_query_params",
223 "body",
224 "raw_body",
225 "headers",
226 "cookies",
227 "method",
228 "path",
229 "has_dependencies",
230 ];
231
232 #[cfg(not(feature = "di"))]
233 const FIELDS: &[&str] = &[
234 "path_params",
235 "query_params",
236 "validated_params",
237 "raw_query_params",
238 "body",
239 "raw_body",
240 "headers",
241 "cookies",
242 "method",
243 "path",
244 ];
245
246 deserializer.deserialize_struct("RequestData", FIELDS, RequestDataVisitor)
247 }
248}
249
250pub type HandlerResult = Result<Response<Body>, (StatusCode, String)>;
252
253pub trait Handler: Send + Sync {
258 fn call(
264 &self,
265 request: Request<Body>,
266 request_data: RequestData,
267 ) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>>;
268
269 fn prefers_raw_json_body(&self) -> bool {
275 false
276 }
277
278 fn prefers_parameter_extraction(&self) -> bool {
285 false
286 }
287
288 fn wants_headers(&self) -> bool {
293 true
294 }
295
296 fn wants_cookies(&self) -> bool {
300 true
301 }
302
303 fn wants_request_extensions(&self) -> bool {
308 false
309 }
310
311 fn static_response(&self) -> Option<StaticResponse> {
315 None
316 }
317}
318
319pub struct StaticResponseHandler {
325 response: StaticResponse,
326}
327
328impl StaticResponseHandler {
329 pub fn new(response: StaticResponse) -> Self {
331 Self { response }
332 }
333
334 pub fn from_parts(
339 status: u16,
340 body: impl Into<Bytes>,
341 content_type: Option<&str>,
342 extra_headers: Vec<(HeaderName, HeaderValue)>,
343 ) -> Self {
344 let ct = content_type
345 .and_then(|s| HeaderValue::from_str(s).ok())
346 .unwrap_or_else(|| HeaderValue::from_static("text/plain; charset=utf-8"));
347 Self {
348 response: StaticResponse {
349 status,
350 headers: extra_headers,
351 body: body.into(),
352 content_type: ct,
353 },
354 }
355 }
356}
357
358impl Handler for StaticResponseHandler {
359 fn call(
360 &self,
361 _request: Request<Body>,
362 _request_data: RequestData,
363 ) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>> {
364 let resp = self.response.to_response();
367 Box::pin(async move { Ok(resp) })
368 }
369
370 fn static_response(&self) -> Option<StaticResponse> {
371 Some(self.response.clone())
372 }
373}
374
375#[derive(Debug, Clone)]
377pub struct ValidatedParams {
378 pub params: HashMap<String, Value>,
379}
380
381#[cfg(test)]
382mod tests {
383 use super::*;
384 use std::collections::HashMap;
385
386 fn minimal_request_data() -> RequestData {
387 RequestData {
388 path_params: std::sync::Arc::new(HashMap::new()),
389 query_params: std::sync::Arc::new(Value::Object(serde_json::Map::new())),
390 validated_params: None,
391 raw_query_params: std::sync::Arc::new(HashMap::new()),
392 body: std::sync::Arc::new(Value::Null),
393 raw_body: None,
394 headers: std::sync::Arc::new(HashMap::new()),
395 cookies: std::sync::Arc::new(HashMap::new()),
396 method: "GET".to_string(),
397 path: "/".to_string(),
398 #[cfg(feature = "di")]
399 dependencies: None,
400 }
401 }
402
403 #[test]
404 fn test_request_data_serialization_minimal() {
405 let data = minimal_request_data();
406
407 let json = serde_json::to_value(&data).expect("serialization failed");
408
409 assert!(json["path_params"].is_object());
410 assert!(json["query_params"].is_object());
411 assert!(json["raw_query_params"].is_object());
412 assert!(json["body"].is_null());
413 assert!(json["headers"].is_object());
414 assert!(json["cookies"].is_object());
415 assert_eq!(json["method"], "GET");
416 assert_eq!(json["path"], "/");
417 }
418
419 #[test]
420 fn test_request_data_serialization_with_path_params() {
421 let mut path_params = HashMap::new();
422 path_params.insert("id".to_string(), "123".to_string());
423 path_params.insert("username".to_string(), "alice".to_string());
424
425 let data = RequestData {
426 path_params: std::sync::Arc::new(path_params),
427 ..minimal_request_data()
428 };
429
430 let json = serde_json::to_value(&data).expect("serialization failed");
431
432 assert_eq!(json["path_params"]["id"], "123");
433 assert_eq!(json["path_params"]["username"], "alice");
434 }
435
436 #[test]
437 fn test_request_data_serialization_with_query_params() {
438 let query_params = serde_json::json!({
439 "filter": "active",
440 "limit": 10,
441 "sort": "name"
442 });
443
444 let data = RequestData {
445 query_params: std::sync::Arc::new(query_params),
446 ..minimal_request_data()
447 };
448
449 let json = serde_json::to_value(&data).expect("serialization failed");
450
451 assert_eq!(json["query_params"]["filter"], "active");
452 assert_eq!(json["query_params"]["limit"], 10);
453 assert_eq!(json["query_params"]["sort"], "name");
454 }
455
456 #[test]
457 fn test_request_data_serialization_with_raw_query_params() {
458 let mut raw_query_params = HashMap::new();
459 raw_query_params.insert("tags".to_string(), vec!["rust".to_string(), "web".to_string()]);
460 raw_query_params.insert("category".to_string(), vec!["backend".to_string()]);
461
462 let data = RequestData {
463 raw_query_params: std::sync::Arc::new(raw_query_params),
464 ..minimal_request_data()
465 };
466
467 let json = serde_json::to_value(&data).expect("serialization failed");
468
469 assert!(json["raw_query_params"]["tags"].is_array());
470 assert_eq!(json["raw_query_params"]["tags"][0], "rust");
471 assert_eq!(json["raw_query_params"]["tags"][1], "web");
472 }
473
474 #[test]
475 fn test_request_data_serialization_with_headers() {
476 let mut headers = HashMap::new();
477 headers.insert("content-type".to_string(), "application/json".to_string());
478 headers.insert("authorization".to_string(), "Bearer token123".to_string());
479 headers.insert("user-agent".to_string(), "test-client/1.0".to_string());
480
481 let data = RequestData {
482 headers: std::sync::Arc::new(headers),
483 ..minimal_request_data()
484 };
485
486 let json = serde_json::to_value(&data).expect("serialization failed");
487
488 assert_eq!(json["headers"]["content-type"], "application/json");
489 assert_eq!(json["headers"]["authorization"], "Bearer token123");
490 assert_eq!(json["headers"]["user-agent"], "test-client/1.0");
491 }
492
493 #[test]
494 fn test_request_data_serialization_with_cookies() {
495 let mut cookies = HashMap::new();
496 cookies.insert("session_id".to_string(), "abc123def456".to_string());
497 cookies.insert("preferences".to_string(), "dark_mode=true".to_string());
498
499 let data = RequestData {
500 cookies: std::sync::Arc::new(cookies),
501 ..minimal_request_data()
502 };
503
504 let json = serde_json::to_value(&data).expect("serialization failed");
505
506 assert_eq!(json["cookies"]["session_id"], "abc123def456");
507 assert_eq!(json["cookies"]["preferences"], "dark_mode=true");
508 }
509
510 #[test]
511 fn test_request_data_serialization_with_json_body() {
512 let body = serde_json::json!({
513 "name": "test",
514 "age": 30,
515 "active": true,
516 "tags": ["a", "b"]
517 });
518
519 let data = RequestData {
520 body: std::sync::Arc::new(body),
521 ..minimal_request_data()
522 };
523
524 let json = serde_json::to_value(&data).expect("serialization failed");
525
526 assert_eq!(json["body"]["name"], "test");
527 assert_eq!(json["body"]["age"], 30);
528 assert_eq!(json["body"]["active"], true);
529 assert!(json["body"]["tags"].is_array());
530 }
531
532 #[test]
533 fn test_request_data_serialization_with_raw_body() {
534 let raw_body = bytes::Bytes::from("raw body content");
535 let data = RequestData {
536 raw_body: Some(raw_body),
537 ..minimal_request_data()
538 };
539
540 let json = serde_json::to_value(&data).expect("serialization failed");
541
542 assert!(json["raw_body"].is_array());
543 let serialized_bytes: Vec<u8> =
544 serde_json::from_value(json["raw_body"].clone()).expect("failed to deserialize bytes");
545 assert_eq!(serialized_bytes, b"raw body content".to_vec());
546 }
547
548 #[test]
549 fn test_request_data_serialization_with_empty_strings() {
550 let mut headers = HashMap::new();
551 headers.insert("x-empty".to_string(), "".to_string());
552
553 let data = RequestData {
554 method: "".to_string(),
555 path: "".to_string(),
556 headers: std::sync::Arc::new(headers),
557 ..minimal_request_data()
558 };
559
560 let json = serde_json::to_value(&data).expect("serialization failed");
561
562 assert_eq!(json["method"], "");
563 assert_eq!(json["path"], "");
564 assert_eq!(json["headers"]["x-empty"], "");
565 }
566
567 #[test]
568 fn test_request_data_serialization_with_nested_json_body() {
569 let body = serde_json::json!({
570 "user": {
571 "profile": {
572 "name": "Alice",
573 "contact": {
574 "email": "alice@example.com",
575 "phone": null
576 }
577 }
578 },
579 "settings": {
580 "notifications": [true, false, true]
581 }
582 });
583
584 let data = RequestData {
585 body: std::sync::Arc::new(body),
586 ..minimal_request_data()
587 };
588
589 let json = serde_json::to_value(&data).expect("serialization failed");
590
591 assert_eq!(json["body"]["user"]["profile"]["name"], "Alice");
592 assert_eq!(json["body"]["user"]["profile"]["contact"]["email"], "alice@example.com");
593 assert!(json["body"]["user"]["profile"]["contact"]["phone"].is_null());
594 assert_eq!(json["body"]["settings"]["notifications"][0], true);
595 }
596
597 #[test]
598 fn test_request_data_serialization_all_fields_complete() {
599 let mut path_params = HashMap::new();
600 path_params.insert("id".to_string(), "42".to_string());
601
602 let mut raw_query_params = HashMap::new();
603 raw_query_params.insert("filter".to_string(), vec!["active".to_string()]);
604
605 let mut headers = HashMap::new();
606 headers.insert("content-type".to_string(), "application/json".to_string());
607
608 let mut cookies = HashMap::new();
609 cookies.insert("session".to_string(), "xyz789".to_string());
610
611 let body = serde_json::json!({"action": "create"});
612 let raw_body = bytes::Bytes::from("body bytes");
613
614 let data = RequestData {
615 path_params: std::sync::Arc::new(path_params),
616 query_params: std::sync::Arc::new(serde_json::json!({"page": 1})),
617 validated_params: None,
618 raw_query_params: std::sync::Arc::new(raw_query_params),
619 body: std::sync::Arc::new(body),
620 raw_body: Some(raw_body),
621 headers: std::sync::Arc::new(headers),
622 cookies: std::sync::Arc::new(cookies),
623 method: "POST".to_string(),
624 path: "/api/users".to_string(),
625 #[cfg(feature = "di")]
626 dependencies: None,
627 };
628
629 let json = serde_json::to_value(&data).expect("serialization failed");
630
631 assert_eq!(json["path_params"]["id"], "42");
632 assert_eq!(json["query_params"]["page"], 1);
633 assert_eq!(json["raw_query_params"]["filter"][0], "active");
634 assert_eq!(json["body"]["action"], "create");
635 assert!(json["raw_body"].is_array());
636 assert_eq!(json["headers"]["content-type"], "application/json");
637 assert_eq!(json["cookies"]["session"], "xyz789");
638 assert_eq!(json["method"], "POST");
639 assert_eq!(json["path"], "/api/users");
640 }
641
642 #[test]
643 fn test_request_data_clone_shares_arc_data() {
644 let mut path_params = HashMap::new();
645 path_params.insert("id".to_string(), "original".to_string());
646
647 let data1 = RequestData {
648 path_params: std::sync::Arc::new(path_params),
649 ..minimal_request_data()
650 };
651
652 let data2 = data1.clone();
653
654 assert!(std::sync::Arc::ptr_eq(&data1.path_params, &data2.path_params));
655 }
656
657 #[test]
658 fn test_request_data_deserialization_complete() {
659 let json = serde_json::json!({
660 "path_params": {"id": "123"},
661 "query_params": {"filter": "active"},
662 "raw_query_params": {"tags": ["rust", "web"]},
663 "body": {"name": "test"},
664 "raw_body": null,
665 "headers": {"content-type": "application/json"},
666 "cookies": {"session": "abc"},
667 "method": "POST",
668 "path": "/api/test"
669 });
670
671 let data: RequestData = serde_json::from_value(json).expect("deserialization failed");
672
673 assert_eq!(data.path_params.get("id").unwrap(), "123");
674 assert_eq!(data.query_params["filter"], "active");
675 assert_eq!(data.raw_query_params.get("tags").unwrap()[0], "rust");
676 assert_eq!(data.body["name"], "test");
677 assert!(data.raw_body.is_none());
678 assert_eq!(data.headers.get("content-type").unwrap(), "application/json");
679 assert_eq!(data.cookies.get("session").unwrap(), "abc");
680 assert_eq!(data.method, "POST");
681 assert_eq!(data.path, "/api/test");
682 }
683
684 #[test]
685 fn test_request_data_deserialization_with_raw_body_bytes() {
686 let json = serde_json::json!({
687 "path_params": {},
688 "query_params": {},
689 "raw_query_params": {},
690 "body": null,
691 "raw_body": [72, 101, 108, 108, 111],
692 "headers": {},
693 "cookies": {},
694 "method": "GET",
695 "path": "/"
696 });
697
698 let data: RequestData = serde_json::from_value(json).expect("deserialization failed");
699
700 assert!(data.raw_body.is_some());
701 assert_eq!(data.raw_body.unwrap().as_ref(), b"Hello");
702 }
703
704 #[test]
705 fn test_request_data_deserialization_missing_required_field_path_params() {
706 let json = serde_json::json!({
707 "query_params": {},
708 "raw_query_params": {},
709 "body": null,
710 "headers": {},
711 "cookies": {},
712 "method": "GET",
713 "path": "/"
714 });
715
716 let result: Result<RequestData, _> = serde_json::from_value(json);
717 assert!(result.is_err());
718 assert!(result.unwrap_err().to_string().contains("path_params"));
719 }
720
721 #[test]
722 fn test_request_data_deserialization_missing_required_field_method() {
723 let json = serde_json::json!({
724 "path_params": {},
725 "query_params": {},
726 "raw_query_params": {},
727 "body": null,
728 "headers": {},
729 "cookies": {},
730 "path": "/"
731 });
732
733 let result: Result<RequestData, _> = serde_json::from_value(json);
734 assert!(result.is_err());
735 assert!(result.unwrap_err().to_string().contains("method"));
736 }
737
738 #[test]
739 fn test_request_data_serialization_roundtrip() {
740 let original = RequestData {
741 path_params: std::sync::Arc::new({
742 let mut map = HashMap::new();
743 map.insert("id".to_string(), "999".to_string());
744 map
745 }),
746 query_params: std::sync::Arc::new(serde_json::json!({"limit": 50, "offset": 10})),
747 validated_params: None,
748 raw_query_params: std::sync::Arc::new({
749 let mut map = HashMap::new();
750 map.insert("sort".to_string(), vec!["name".to_string(), "date".to_string()]);
751 map
752 }),
753 body: std::sync::Arc::new(serde_json::json!({"title": "New Post", "content": "Hello World"})),
754 raw_body: None,
755 headers: std::sync::Arc::new({
756 let mut map = HashMap::new();
757 map.insert("accept".to_string(), "application/json".to_string());
758 map
759 }),
760 cookies: std::sync::Arc::new({
761 let mut map = HashMap::new();
762 map.insert("user_id".to_string(), "user42".to_string());
763 map
764 }),
765 method: "PUT".to_string(),
766 path: "/blog/posts/999".to_string(),
767 #[cfg(feature = "di")]
768 dependencies: None,
769 };
770
771 let json = serde_json::to_value(&original).expect("serialization failed");
772 let restored: RequestData = serde_json::from_value(json).expect("deserialization failed");
773
774 assert_eq!(*original.path_params, *restored.path_params);
775 assert_eq!(original.query_params, restored.query_params);
776 assert_eq!(*original.raw_query_params, *restored.raw_query_params);
777 assert_eq!(original.body, restored.body);
778 assert_eq!(original.raw_body, restored.raw_body);
779 assert_eq!(*original.headers, *restored.headers);
780 assert_eq!(*original.cookies, *restored.cookies);
781 assert_eq!(original.method, restored.method);
782 assert_eq!(original.path, restored.path);
783 }
784
785 #[test]
786 fn test_request_data_serialization_large_body() {
787 let mut large_object = serde_json::Map::new();
788 for i in 0..100 {
789 large_object.insert(format!("key_{}", i), serde_json::Value::String(format!("value_{}", i)));
790 }
791
792 let data = RequestData {
793 body: std::sync::Arc::new(Value::Object(large_object)),
794 ..minimal_request_data()
795 };
796
797 let json = serde_json::to_value(&data).expect("serialization failed");
798
799 assert!(json["body"].is_object());
800 assert_eq!(json["body"].get("key_0").unwrap(), "value_0");
801 assert_eq!(json["body"].get("key_99").unwrap(), "value_99");
802 }
803
804 #[test]
805 fn test_request_data_empty_collections() {
806 let data = RequestData {
807 path_params: std::sync::Arc::new(HashMap::new()),
808 query_params: std::sync::Arc::new(Value::Object(serde_json::Map::new())),
809 validated_params: None,
810 raw_query_params: std::sync::Arc::new(HashMap::new()),
811 body: std::sync::Arc::new(Value::Object(serde_json::Map::new())),
812 raw_body: None,
813 headers: std::sync::Arc::new(HashMap::new()),
814 cookies: std::sync::Arc::new(HashMap::new()),
815 method: "GET".to_string(),
816 path: "/".to_string(),
817 #[cfg(feature = "di")]
818 dependencies: None,
819 };
820
821 let json = serde_json::to_value(&data).expect("serialization failed");
822
823 assert_eq!(json["path_params"].as_object().unwrap().len(), 0);
824 assert_eq!(json["query_params"].as_object().unwrap().len(), 0);
825 assert_eq!(json["raw_query_params"].as_object().unwrap().len(), 0);
826 assert_eq!(json["body"].as_object().unwrap().len(), 0);
827 assert!(json["raw_body"].is_null());
828 assert_eq!(json["headers"].as_object().unwrap().len(), 0);
829 assert_eq!(json["cookies"].as_object().unwrap().len(), 0);
830 }
831
832 #[test]
833 fn test_request_data_special_characters_in_strings() {
834 let mut headers = HashMap::new();
835 headers.insert("x-custom".to_string(), "value with \"quotes\"".to_string());
836 headers.insert("x-unicode".to_string(), "Café ☕ 🚀".to_string());
837
838 let data = RequestData {
839 method: "POST".to_string(),
840 path: "/api/v1/users\\test".to_string(),
841 headers: std::sync::Arc::new(headers),
842 body: std::sync::Arc::new(serde_json::json!({"note": "Contains\nnewline"})),
843 ..minimal_request_data()
844 };
845
846 let json = serde_json::to_value(&data).expect("serialization failed");
847
848 assert_eq!(json["headers"]["x-custom"], "value with \"quotes\"");
849 assert_eq!(json["headers"]["x-unicode"], "Café ☕ 🚀");
850 assert_eq!(json["path"], "/api/v1/users\\test");
851 assert_eq!(json["body"]["note"], "Contains\nnewline");
852 }
853
854 #[test]
855 #[cfg(feature = "di")]
856 fn test_request_data_serialization_with_di_feature_no_dependencies() {
857 let data = minimal_request_data();
858
859 let json = serde_json::to_value(&data).expect("serialization failed");
860
861 assert_eq!(json["has_dependencies"], false);
862 }
863
864 #[test]
865 fn test_request_data_method_variants() {
866 let methods = vec!["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
867
868 for method in methods {
869 let data = RequestData {
870 method: method.to_string(),
871 ..minimal_request_data()
872 };
873
874 let json = serde_json::to_value(&data).expect("serialization failed");
875
876 assert_eq!(json["method"], method);
877 }
878 }
879
880 #[test]
881 fn test_request_data_serialization_null_body() {
882 let data = RequestData {
883 body: std::sync::Arc::new(Value::Null),
884 ..minimal_request_data()
885 };
886
887 let json = serde_json::to_value(&data).expect("serialization failed");
888
889 assert!(json["body"].is_null());
890 }
891
892 #[test]
893 fn test_request_data_serialization_array_body() {
894 let data = RequestData {
895 body: std::sync::Arc::new(serde_json::json!([1, 2, 3, "four", {"five": 5}])),
896 ..minimal_request_data()
897 };
898
899 let json = serde_json::to_value(&data).expect("serialization failed");
900
901 assert!(json["body"].is_array());
902 assert_eq!(json["body"][0], 1);
903 assert_eq!(json["body"][1], 2);
904 assert_eq!(json["body"][3], "four");
905 assert_eq!(json["body"][4]["five"], 5);
906 }
907
908 #[test]
909 fn test_request_data_serialization_numeric_edge_cases() {
910 let data = RequestData {
911 body: std::sync::Arc::new(serde_json::json!({
912 "zero": 0,
913 "negative": -42,
914 "large": 9223372036854775807i64,
915 "float": 3.14159
916 })),
917 ..minimal_request_data()
918 };
919
920 let json = serde_json::to_value(&data).expect("serialization failed");
921
922 assert_eq!(json["body"]["zero"], 0);
923 assert_eq!(json["body"]["negative"], -42);
924 assert_eq!(json["body"]["large"], 9223372036854775807i64);
925 assert_eq!(json["body"]["float"], 3.14159);
926 }
927
928 #[test]
929 fn test_validated_params_basic_creation() {
930 let mut params = HashMap::new();
931 params.insert("id".to_string(), Value::String("123".to_string()));
932 params.insert("active".to_string(), Value::Bool(true));
933
934 let validated = ValidatedParams { params };
935
936 assert_eq!(validated.params.get("id").unwrap(), &Value::String("123".to_string()));
937 assert_eq!(validated.params.get("active").unwrap(), &Value::Bool(true));
938 }
939
940 #[test]
941 fn test_static_response_handler_new() {
942 let sr = StaticResponse {
943 status: 200,
944 headers: vec![],
945 body: Bytes::from("OK"),
946 content_type: HeaderValue::from_static("text/plain"),
947 };
948 let handler = StaticResponseHandler::new(sr);
949 let resp = handler.static_response();
950 assert!(resp.is_some());
951 let resp = resp.unwrap();
952 assert_eq!(resp.status, 200);
953 assert_eq!(resp.body.as_ref(), b"OK");
954 }
955
956 #[test]
957 fn test_static_response_handler_from_parts_defaults() {
958 let handler = StaticResponseHandler::from_parts(204, "No Content", None, vec![]);
959 let resp = handler.static_response().unwrap();
960 assert_eq!(resp.status, 204);
961 assert_eq!(resp.body.as_ref(), b"No Content");
962 assert_eq!(resp.content_type, "text/plain; charset=utf-8");
963 }
964
965 #[test]
966 fn test_static_response_handler_from_parts_custom_content_type() {
967 let handler = StaticResponseHandler::from_parts(200, r#"{"ok":true}"#, Some("application/json"), vec![]);
968 let resp = handler.static_response().unwrap();
969 assert_eq!(resp.content_type, "application/json");
970 }
971
972 #[test]
973 fn test_static_response_handler_from_parts_extra_headers() {
974 let handler = StaticResponseHandler::from_parts(
975 200,
976 "OK",
977 None,
978 vec![(HeaderName::from_static("x-custom"), HeaderValue::from_static("value"))],
979 );
980 let resp = handler.static_response().unwrap();
981 assert_eq!(resp.headers.len(), 1);
982 assert_eq!(resp.headers[0].0, "x-custom");
983 assert_eq!(resp.headers[0].1, "value");
984 }
985
986 #[tokio::test]
987 async fn test_static_response_handler_call_fallback() {
988 use http_body_util::BodyExt;
989
990 let handler = StaticResponseHandler::from_parts(201, "created", Some("text/plain"), vec![]);
991 let request = Request::builder().body(Body::empty()).unwrap();
992 let result = handler.call(request, minimal_request_data()).await;
993 assert!(result.is_ok());
994 let response = result.unwrap();
995 assert_eq!(response.status(), StatusCode::CREATED);
996 assert_eq!(response.headers().get("content-type").unwrap(), "text/plain");
997 let body = response.into_body().collect().await.unwrap().to_bytes();
998 assert_eq!(body.as_ref(), b"created");
999 }
1000
1001 #[test]
1002 fn test_default_handler_static_response_is_none() {
1003 struct DummyHandler;
1004 impl Handler for DummyHandler {
1005 fn call(
1006 &self,
1007 _: Request<Body>,
1008 _: RequestData,
1009 ) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>> {
1010 Box::pin(async { Err((StatusCode::OK, String::new())) })
1011 }
1012 }
1013 assert!(DummyHandler.static_response().is_none());
1014 }
1015}