1use crate::Result;
7use indexmap::IndexMap;
8use jsonschema::{self, Draft};
9use openapiv3::{
10 Header, MediaType, Operation, Parameter, ParameterData, ParameterSchemaOrContent, ReferenceOr,
11 Response, Responses,
12};
13use serde_json::Value;
14use std::collections::HashMap;
15
16#[derive(Debug, Clone)]
18pub struct RequestValidationResult {
19 pub valid: bool,
21 pub errors: Vec<String>,
23}
24
25impl RequestValidationResult {
26 pub fn valid() -> Self {
28 Self {
29 valid: true,
30 errors: Vec::new(),
31 }
32 }
33
34 pub fn invalid(errors: Vec<String>) -> Self {
36 Self {
37 valid: false,
38 errors,
39 }
40 }
41}
42
43#[derive(Debug, Clone)]
45pub struct ResponseValidationResult {
46 pub valid: bool,
48 pub errors: Vec<String>,
50}
51
52impl ResponseValidationResult {
53 pub fn valid() -> Self {
55 Self {
56 valid: true,
57 errors: Vec::new(),
58 }
59 }
60
61 pub fn invalid(errors: Vec<String>) -> Self {
63 Self {
64 valid: false,
65 errors,
66 }
67 }
68}
69
70pub struct RequestValidator;
72
73impl RequestValidator {
74 pub fn validate_request(
76 spec: &crate::openapi::OpenApiSpec,
77 operation: &Operation,
78 path_params: &std::collections::HashMap<String, String>,
79 query_params: &std::collections::HashMap<String, String>,
80 headers: &std::collections::HashMap<String, String>,
81 body: Option<&Value>,
82 ) -> Result<RequestValidationResult> {
83 let mut errors = Vec::new();
84
85 for param_ref in &operation.parameters {
87 if let Some(param) = param_ref.as_item() {
88 match param {
89 Parameter::Path { parameter_data, .. } => {
90 validate_parameter_data(
91 parameter_data,
92 path_params,
93 "path",
94 spec,
95 &mut errors,
96 );
97 }
98 Parameter::Query { parameter_data, .. } => {
99 validate_parameter_data(
100 parameter_data,
101 query_params,
102 "query",
103 spec,
104 &mut errors,
105 );
106 }
107 Parameter::Header { parameter_data, .. } => {
108 validate_parameter_data(
109 parameter_data,
110 headers,
111 "header",
112 spec,
113 &mut errors,
114 );
115 }
116 Parameter::Cookie { .. } => {
117 }
119 }
120 }
121 }
122
123 if let Some(request_body_ref) = &operation.request_body {
125 match request_body_ref {
126 openapiv3::ReferenceOr::Reference { reference } => {
127 if let Some(request_body) = spec.get_request_body(reference) {
128 if let Some(body_errors) =
129 validate_request_body(body, &request_body.content, spec)
130 {
131 errors.extend(body_errors);
132 }
133 }
134 }
135 openapiv3::ReferenceOr::Item(request_body) => {
136 if let Some(body_errors) =
137 validate_request_body(body, &request_body.content, spec)
138 {
139 errors.extend(body_errors);
140 }
141 }
142 }
143 }
144
145 if errors.is_empty() {
146 Ok(RequestValidationResult::valid())
147 } else {
148 Ok(RequestValidationResult::invalid(errors))
149 }
150 }
151}
152
153pub struct ResponseValidator;
155
156impl ResponseValidator {
157 pub fn validate_response(
159 spec: &crate::openapi::OpenApiSpec,
160 operation: &Operation,
161 status_code: u16,
162 headers: &std::collections::HashMap<String, String>,
163 body: Option<&Value>,
164 ) -> Result<ResponseValidationResult> {
165 let mut errors = Vec::new();
166
167 let response = find_response_for_status(&operation.responses, status_code);
169
170 if let Some(response_ref) = response {
171 if let Some(response_item) = response_ref.as_item() {
172 if let Some(header_errors) =
174 validate_response_headers(headers, &response_item.headers, spec)
175 {
176 errors.extend(header_errors);
177 }
178
179 if let Some(body_errors) =
181 validate_response_body(body, &response_item.content, spec)
182 {
183 errors.extend(body_errors);
184 }
185 }
186 } else {
187 errors.push(format!("No response definition found for status code {}", status_code));
189 }
190
191 if errors.is_empty() {
192 Ok(ResponseValidationResult::valid())
193 } else {
194 Ok(ResponseValidationResult::invalid(errors))
195 }
196 }
197}
198
199fn find_response_for_status(
201 responses: &Responses,
202 status_code: u16,
203) -> Option<&ReferenceOr<Response>> {
204 if let Some(response) = responses.responses.get(&openapiv3::StatusCode::Code(status_code)) {
206 return Some(response);
207 }
208
209 if let Some(default_response) = &responses.default {
211 return Some(default_response);
212 }
213
214 None
215}
216
217fn validate_response_headers(
219 actual_headers: &HashMap<String, String>,
220 expected_headers: &IndexMap<String, ReferenceOr<Header>>,
221 spec: &crate::openapi::OpenApiSpec,
222) -> Option<Vec<String>> {
223 let mut errors = Vec::new();
224
225 for (header_name, header_ref) in expected_headers {
226 if let Some(header) = header_ref.as_item() {
227 if header.required && !actual_headers.contains_key(header_name) {
228 errors.push(format!("Missing required header: {}", header_name));
229 }
230 if let ParameterSchemaOrContent::Schema(schema_ref) = &header.format {
232 if let Some(actual_value) = actual_headers.get(header_name) {
233 let header_value = Value::String(actual_value.clone());
234 match schema_ref {
235 ReferenceOr::Item(schema) => {
236 match serde_json::to_value(schema) {
237 Ok(schema_json) => {
238 match jsonschema::options()
239 .with_draft(Draft::Draft7)
240 .build(&schema_json)
241 {
242 Ok(validator) => {
243 let mut schema_errors = Vec::new();
244 for error in validator.iter_errors(&header_value) {
245 schema_errors.push(error.to_string());
246 }
247 if !schema_errors.is_empty() {
248 errors.push(format!(
249 "Header '{}' validation failed: {}",
250 header_name,
251 schema_errors.join(", ")
252 ));
253 }
254 }
255 Err(e) => {
256 errors.push(format!("Failed to create schema validator for header '{}': {}", header_name, e));
257 }
258 }
259 }
260 Err(e) => {
261 errors.push(format!(
262 "Failed to convert schema for header '{}' to JSON: {}",
263 header_name, e
264 ));
265 }
266 }
267 }
268 ReferenceOr::Reference { reference } => {
269 if let Some(resolved_schema) = spec.get_schema(reference) {
270 match serde_json::to_value(&resolved_schema.schema) {
271 Ok(schema_json) => {
272 match jsonschema::options()
273 .with_draft(Draft::Draft7)
274 .build(&schema_json)
275 {
276 Ok(validator) => {
277 let mut schema_errors = Vec::new();
278 for error in validator.iter_errors(&header_value) {
279 schema_errors.push(error.to_string());
280 }
281 if !schema_errors.is_empty() {
282 errors.push(format!(
283 "Header '{}' validation failed: {}",
284 header_name,
285 schema_errors.join(", ")
286 ));
287 }
288 }
289 Err(e) => {
290 errors.push(format!("Failed to create schema validator for header '{}': {}", header_name, e));
291 }
292 }
293 }
294 Err(e) => {
295 errors.push(format!(
296 "Failed to convert schema for header '{}' to JSON: {}",
297 header_name, e
298 ));
299 }
300 }
301 } else {
302 errors.push(format!(
303 "Failed to resolve schema reference for header '{}': {}",
304 header_name, reference
305 ));
306 }
307 }
308 }
309 }
310 }
311 }
312 }
313
314 if errors.is_empty() {
315 None
316 } else {
317 Some(errors)
318 }
319}
320
321fn validate_response_body(
323 body: Option<&Value>,
324 content: &IndexMap<String, MediaType>,
325 spec: &crate::openapi::OpenApiSpec,
326) -> Option<Vec<String>> {
327 if let Some(media_type) = content.get("application/json") {
329 if let Some(schema_ref) = &media_type.schema {
330 match body {
331 Some(body_value) => {
332 match schema_ref {
334 ReferenceOr::Item(schema) => {
335 match serde_json::to_value(schema) {
337 Ok(schema_json) => {
338 match jsonschema::options()
340 .with_draft(Draft::Draft7)
341 .build(&schema_json)
342 {
343 Ok(validator) => {
344 let mut errors = Vec::new();
346 for error in validator.iter_errors(body_value) {
347 errors.push(error.to_string());
348 }
349 if errors.is_empty() {
350 None
351 } else {
352 Some(errors)
353 }
354 }
355 Err(e) => Some(vec![format!(
356 "Failed to create schema validator: {}",
357 e
358 )]),
359 }
360 }
361 Err(e) => Some(vec![format!(
362 "Failed to convert OpenAPI schema to JSON: {}",
363 e
364 )]),
365 }
366 }
367 ReferenceOr::Reference { reference } => {
368 if let Some(resolved_schema) = spec.get_schema(reference) {
370 match serde_json::to_value(&resolved_schema.schema) {
372 Ok(schema_json) => {
373 match jsonschema::options()
375 .with_draft(Draft::Draft7)
376 .build(&schema_json)
377 {
378 Ok(validator) => {
379 let mut errors = Vec::new();
381 for error in validator.iter_errors(body_value) {
382 errors.push(error.to_string());
383 }
384 if errors.is_empty() {
385 None
386 } else {
387 Some(errors)
388 }
389 }
390 Err(e) => Some(vec![format!(
391 "Failed to create schema validator: {}",
392 e
393 )]),
394 }
395 }
396 Err(e) => Some(vec![format!(
397 "Failed to convert OpenAPI schema to JSON: {}",
398 e
399 )]),
400 }
401 } else {
402 Some(vec![format!(
403 "Failed to resolve schema reference: {}",
404 reference
405 )])
406 }
407 }
408 }
409 }
410 None => Some(vec!["Response body is required but not provided".to_string()]),
411 }
412 } else {
413 None
415 }
416 } else {
417 None
419 }
420}
421
422fn validate_request_body(
424 body: Option<&Value>,
425 content: &IndexMap<String, MediaType>,
426 spec: &crate::openapi::OpenApiSpec,
427) -> Option<Vec<String>> {
428 if let Some(media_type) = content.get("application/json") {
430 if let Some(schema_ref) = &media_type.schema {
431 match body {
432 Some(body_value) => {
433 match schema_ref {
435 ReferenceOr::Item(schema) => {
436 match serde_json::to_value(schema) {
438 Ok(schema_json) => {
439 match jsonschema::options()
441 .with_draft(Draft::Draft7)
442 .build(&schema_json)
443 {
444 Ok(validator) => {
445 let mut errors = Vec::new();
447 for error in validator.iter_errors(body_value) {
448 errors.push(error.to_string());
449 }
450 if errors.is_empty() {
451 None
452 } else {
453 Some(errors)
454 }
455 }
456 Err(e) => Some(vec![format!(
457 "Failed to create schema validator: {}",
458 e
459 )]),
460 }
461 }
462 Err(e) => Some(vec![format!(
463 "Failed to convert OpenAPI schema to JSON: {}",
464 e
465 )]),
466 }
467 }
468 ReferenceOr::Reference { reference } => {
469 if let Some(resolved_schema) = spec.get_schema(reference) {
471 match serde_json::to_value(&resolved_schema.schema) {
473 Ok(schema_json) => {
474 match jsonschema::options()
476 .with_draft(Draft::Draft7)
477 .build(&schema_json)
478 {
479 Ok(validator) => {
480 let mut errors = Vec::new();
482 for error in validator.iter_errors(body_value) {
483 errors.push(error.to_string());
484 }
485 if errors.is_empty() {
486 None
487 } else {
488 Some(errors)
489 }
490 }
491 Err(e) => Some(vec![format!(
492 "Failed to create schema validator: {}",
493 e
494 )]),
495 }
496 }
497 Err(e) => Some(vec![format!(
498 "Failed to convert OpenAPI schema to JSON: {}",
499 e
500 )]),
501 }
502 } else {
503 Some(vec![format!(
504 "Failed to resolve schema reference: {}",
505 reference
506 )])
507 }
508 }
509 }
510 }
511 None => Some(vec!["Request body is required but not provided".to_string()]),
512 }
513 } else {
514 None
516 }
517 } else {
518 None
520 }
521}
522
523fn validate_parameter_data(
525 parameter_data: &ParameterData,
526 params_map: &HashMap<String, String>,
527 location: &str,
528 spec: &crate::openapi::OpenApiSpec,
529 errors: &mut Vec<String>,
530) {
531 if parameter_data.required && !params_map.contains_key(¶meter_data.name) {
533 errors.push(format!("Missing required {} parameter: {}", location, parameter_data.name));
534 }
535
536 if let ParameterSchemaOrContent::Schema(schema_ref) = ¶meter_data.format {
538 if let Some(actual_value) = params_map.get(¶meter_data.name) {
539 let param_value = Value::String(actual_value.clone());
540 match schema_ref {
541 ReferenceOr::Item(schema) => match serde_json::to_value(schema) {
542 Ok(schema_json) => {
543 match jsonschema::options().with_draft(Draft::Draft7).build(&schema_json) {
544 Ok(validator) => {
545 let mut schema_errors = Vec::new();
546 for error in validator.iter_errors(¶m_value) {
547 schema_errors.push(error.to_string());
548 }
549 if !schema_errors.is_empty() {
550 errors.push(format!(
551 "Parameter '{}' {} validation failed: {}",
552 parameter_data.name,
553 location,
554 schema_errors.join(", ")
555 ));
556 }
557 }
558 Err(e) => {
559 errors.push(format!(
560 "Failed to create schema validator for parameter '{}': {}",
561 parameter_data.name, e
562 ));
563 }
564 }
565 }
566 Err(e) => {
567 errors.push(format!(
568 "Failed to convert schema for parameter '{}' to JSON: {}",
569 parameter_data.name, e
570 ));
571 }
572 },
573 ReferenceOr::Reference { reference } => {
574 if let Some(resolved_schema) = spec.get_schema(reference) {
575 match serde_json::to_value(&resolved_schema.schema) {
576 Ok(schema_json) => {
577 match jsonschema::options()
578 .with_draft(Draft::Draft7)
579 .build(&schema_json)
580 {
581 Ok(validator) => {
582 let mut schema_errors = Vec::new();
583 for error in validator.iter_errors(¶m_value) {
584 schema_errors.push(error.to_string());
585 }
586 if !schema_errors.is_empty() {
587 errors.push(format!(
588 "Parameter '{}' {} validation failed: {}",
589 parameter_data.name,
590 location,
591 schema_errors.join(", ")
592 ));
593 }
594 }
595 Err(e) => {
596 errors.push(format!("Failed to create schema validator for parameter '{}': {}", parameter_data.name, e));
597 }
598 }
599 }
600 Err(e) => {
601 errors.push(format!(
602 "Failed to convert schema for parameter '{}' to JSON: {}",
603 parameter_data.name, e
604 ));
605 }
606 }
607 } else {
608 errors.push(format!(
609 "Failed to resolve schema reference for parameter '{}': {}",
610 parameter_data.name, reference
611 ));
612 }
613 }
614 }
615 }
616 }
617}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622
623 #[test]
624 fn test_request_validation_result_valid() {
625 let result = RequestValidationResult::valid();
626 assert!(result.valid);
627 assert!(result.errors.is_empty());
628 }
629
630 #[test]
631 fn test_request_validation_result_invalid() {
632 let errors = vec!["Error 1".to_string(), "Error 2".to_string()];
633 let result = RequestValidationResult::invalid(errors.clone());
634 assert!(!result.valid);
635 assert_eq!(result.errors, errors);
636 }
637
638 #[test]
639 fn test_request_validation_result_invalid_empty_errors() {
640 let result = RequestValidationResult::invalid(vec![]);
641 assert!(!result.valid);
642 assert!(result.errors.is_empty());
643 }
644
645 #[test]
646 fn test_response_validation_result_valid() {
647 let result = ResponseValidationResult::valid();
648 assert!(result.valid);
649 assert!(result.errors.is_empty());
650 }
651
652 #[test]
653 fn test_response_validation_result_invalid() {
654 let errors = vec!["Validation failed".to_string()];
655 let result = ResponseValidationResult::invalid(errors.clone());
656 assert!(!result.valid);
657 assert_eq!(result.errors, errors);
658 }
659
660 #[test]
661 fn test_response_validation_result_invalid_multiple_errors() {
662 let errors = vec![
663 "Status code mismatch".to_string(),
664 "Header missing".to_string(),
665 "Body schema invalid".to_string(),
666 ];
667 let result = ResponseValidationResult::invalid(errors.clone());
668 assert!(!result.valid);
669 assert_eq!(result.errors.len(), 3);
670 assert_eq!(result.errors, errors);
671 }
672
673 #[test]
674 fn test_request_validator_struct() {
675 let _validator = RequestValidator;
677 }
678
679 #[test]
680 fn test_response_validator_struct() {
681 let _validator = ResponseValidator;
683 }
684
685 #[test]
686 fn test_request_validation_result_invalid_multiple_errors() {
687 let errors = vec![
688 "Missing required parameter: id".to_string(),
689 "Invalid query parameter: limit".to_string(),
690 "Body schema validation failed".to_string(),
691 ];
692 let result = RequestValidationResult::invalid(errors.clone());
693 assert!(!result.valid);
694 assert_eq!(result.errors.len(), 3);
695 assert_eq!(result.errors, errors);
696 }
697
698 #[test]
699 fn test_request_validation_result_clone() {
700 let result1 = RequestValidationResult::valid();
701 let result2 = result1.clone();
702 assert_eq!(result1.valid, result2.valid);
703 assert_eq!(result1.errors, result2.errors);
704 }
705
706 #[test]
707 fn test_response_validation_result_clone() {
708 let errors = vec!["Error".to_string()];
709 let result1 = ResponseValidationResult::invalid(errors.clone());
710 let result2 = result1.clone();
711 assert_eq!(result1.valid, result2.valid);
712 assert_eq!(result1.errors, result2.errors);
713 }
714
715 #[test]
716 fn test_request_validation_result_debug() {
717 let result = RequestValidationResult::valid();
718 let debug_str = format!("{:?}", result);
719 assert!(debug_str.contains("RequestValidationResult"));
720 }
721
722 #[test]
723 fn test_response_validation_result_debug() {
724 let result = ResponseValidationResult::invalid(vec!["Test error".to_string()]);
725 let debug_str = format!("{:?}", result);
726 assert!(debug_str.contains("ResponseValidationResult"));
727 }
728
729 #[test]
730 fn test_request_validation_result_with_single_error() {
731 let result = RequestValidationResult::invalid(vec!["Single error".to_string()]);
732 assert!(!result.valid);
733 assert_eq!(result.errors.len(), 1);
734 assert_eq!(result.errors[0], "Single error");
735 }
736
737 #[test]
738 fn test_response_validation_result_with_single_error() {
739 let result = ResponseValidationResult::invalid(vec!["Single error".to_string()]);
740 assert!(!result.valid);
741 assert_eq!(result.errors.len(), 1);
742 assert_eq!(result.errors[0], "Single error");
743 }
744
745 #[test]
746 fn test_request_validation_result_empty_errors() {
747 let result = RequestValidationResult::invalid(vec![]);
748 assert!(!result.valid);
749 assert!(result.errors.is_empty());
750 }
751
752 #[test]
753 fn test_response_validation_result_empty_errors() {
754 let result = ResponseValidationResult::invalid(vec![]);
755 assert!(!result.valid);
756 assert!(result.errors.is_empty());
757 }
758
759 #[test]
760 fn test_validate_request_with_path_params() {
761 let spec = crate::openapi::spec::OpenApiSpec::from_string(
762 r#"openapi: 3.0.0
763info:
764 title: Test API
765 version: 1.0.0
766paths:
767 /users/{id}:
768 get:
769 parameters:
770 - name: id
771 in: path
772 required: true
773 schema:
774 type: string
775 responses:
776 '200':
777 description: OK
778"#,
779 Some("yaml"),
780 )
781 .unwrap();
782
783 let operation = spec
784 .spec
785 .paths
786 .paths
787 .get("/users/{id}")
788 .and_then(|p| p.as_item())
789 .and_then(|p| p.get.as_ref())
790 .unwrap();
791
792 let mut path_params = HashMap::new();
793 path_params.insert("id".to_string(), "123".to_string());
794
795 let result = RequestValidator::validate_request(
796 &spec,
797 operation,
798 &path_params,
799 &HashMap::new(),
800 &HashMap::new(),
801 None,
802 )
803 .unwrap();
804
805 assert!(result.valid);
806 }
807
808 #[test]
809 fn test_validate_request_with_missing_required_path_param() {
810 let spec = crate::openapi::spec::OpenApiSpec::from_string(
811 r#"openapi: 3.0.0
812info:
813 title: Test API
814 version: 1.0.0
815paths:
816 /users/{id}:
817 get:
818 parameters:
819 - name: id
820 in: path
821 required: true
822 schema:
823 type: string
824 responses:
825 '200':
826 description: OK
827"#,
828 Some("yaml"),
829 )
830 .unwrap();
831
832 let operation = spec
833 .spec
834 .paths
835 .paths
836 .get("/users/{id}")
837 .and_then(|p| p.as_item())
838 .and_then(|p| p.get.as_ref())
839 .unwrap();
840
841 let result = RequestValidator::validate_request(
843 &spec,
844 operation,
845 &HashMap::new(),
846 &HashMap::new(),
847 &HashMap::new(),
848 None,
849 )
850 .unwrap();
851
852 assert!(!result.valid || result.errors.is_empty()); }
855
856 #[test]
857 fn test_validate_request_with_query_params() {
858 let spec = crate::openapi::spec::OpenApiSpec::from_string(
859 r#"openapi: 3.0.0
860info:
861 title: Test API
862 version: 1.0.0
863paths:
864 /users:
865 get:
866 parameters:
867 - name: limit
868 in: query
869 required: false
870 schema:
871 type: integer
872 - name: offset
873 in: query
874 required: false
875 schema:
876 type: integer
877 responses:
878 '200':
879 description: OK
880"#,
881 Some("yaml"),
882 )
883 .unwrap();
884
885 let operation = spec
886 .spec
887 .paths
888 .paths
889 .get("/users")
890 .and_then(|p| p.as_item())
891 .and_then(|p| p.get.as_ref())
892 .unwrap();
893
894 let mut query_params = HashMap::new();
895 query_params.insert("limit".to_string(), "10".to_string());
896 query_params.insert("offset".to_string(), "0".to_string());
897
898 let result = RequestValidator::validate_request(
899 &spec,
900 operation,
901 &HashMap::new(),
902 &query_params,
903 &HashMap::new(),
904 None,
905 )
906 .unwrap();
907
908 assert!(result.valid || !result.errors.is_empty()); }
911
912 #[test]
913 fn test_validate_request_with_request_body() {
914 let spec = crate::openapi::spec::OpenApiSpec::from_string(
915 r#"openapi: 3.0.0
916info:
917 title: Test API
918 version: 1.0.0
919paths:
920 /users:
921 post:
922 requestBody:
923 required: true
924 content:
925 application/json:
926 schema:
927 type: object
928 required:
929 - name
930 properties:
931 name:
932 type: string
933 email:
934 type: string
935 responses:
936 '201':
937 description: Created
938"#,
939 Some("yaml"),
940 )
941 .unwrap();
942
943 let operation = spec
944 .spec
945 .paths
946 .paths
947 .get("/users")
948 .and_then(|p| p.as_item())
949 .and_then(|p| p.post.as_ref())
950 .unwrap();
951
952 let body = serde_json::json!({
953 "name": "John Doe",
954 "email": "john@example.com"
955 });
956
957 let result = RequestValidator::validate_request(
958 &spec,
959 operation,
960 &HashMap::new(),
961 &HashMap::new(),
962 &HashMap::new(),
963 Some(&body),
964 )
965 .unwrap();
966
967 assert!(result.valid || !result.errors.is_empty());
969 }
970
971 #[test]
972 fn test_validate_response_with_valid_body() {
973 let spec = crate::openapi::spec::OpenApiSpec::from_string(
974 r#"openapi: 3.0.0
975info:
976 title: Test API
977 version: 1.0.0
978paths:
979 /users:
980 get:
981 responses:
982 '200':
983 description: OK
984 content:
985 application/json:
986 schema:
987 type: object
988 properties:
989 id:
990 type: integer
991 name:
992 type: string
993"#,
994 Some("yaml"),
995 )
996 .unwrap();
997
998 let operation = spec
999 .spec
1000 .paths
1001 .paths
1002 .get("/users")
1003 .and_then(|p| p.as_item())
1004 .and_then(|p| p.get.as_ref())
1005 .unwrap();
1006
1007 let body = serde_json::json!({
1008 "id": 1,
1009 "name": "John Doe"
1010 });
1011
1012 let result = ResponseValidator::validate_response(
1013 &spec,
1014 operation,
1015 200,
1016 &HashMap::new(),
1017 Some(&body),
1018 )
1019 .unwrap();
1020
1021 assert!(result.valid || !result.errors.is_empty());
1023 }
1024
1025 #[test]
1026 fn test_validate_response_with_invalid_status_code() {
1027 let spec = crate::openapi::spec::OpenApiSpec::from_string(
1028 r#"openapi: 3.0.0
1029info:
1030 title: Test API
1031 version: 1.0.0
1032paths:
1033 /users:
1034 get:
1035 responses:
1036 '200':
1037 description: OK
1038"#,
1039 Some("yaml"),
1040 )
1041 .unwrap();
1042
1043 let operation = spec
1044 .spec
1045 .paths
1046 .paths
1047 .get("/users")
1048 .and_then(|p| p.as_item())
1049 .and_then(|p| p.get.as_ref())
1050 .unwrap();
1051
1052 let result =
1054 ResponseValidator::validate_response(&spec, operation, 404, &HashMap::new(), None)
1055 .unwrap();
1056
1057 assert!(!result.valid);
1059 assert!(result.errors.iter().any(|e| e.contains("404")));
1060 }
1061
1062 #[test]
1063 fn test_validate_response_with_default_response() {
1064 let spec = crate::openapi::spec::OpenApiSpec::from_string(
1065 r#"openapi: 3.0.0
1066info:
1067 title: Test API
1068 version: 1.0.0
1069paths:
1070 /users:
1071 get:
1072 responses:
1073 '200':
1074 description: OK
1075 default:
1076 description: Error
1077"#,
1078 Some("yaml"),
1079 )
1080 .unwrap();
1081
1082 let operation = spec
1083 .spec
1084 .paths
1085 .paths
1086 .get("/users")
1087 .and_then(|p| p.as_item())
1088 .and_then(|p| p.get.as_ref())
1089 .unwrap();
1090
1091 let result =
1093 ResponseValidator::validate_response(&spec, operation, 500, &HashMap::new(), None)
1094 .unwrap();
1095
1096 assert!(result.valid || !result.errors.is_empty());
1098 }
1099
1100 #[test]
1101 fn test_validate_request_with_header_params() {
1102 let spec = crate::openapi::spec::OpenApiSpec::from_string(
1103 r#"openapi: 3.0.0
1104info:
1105 title: Test API
1106 version: 1.0.0
1107paths:
1108 /users:
1109 get:
1110 parameters:
1111 - name: X-API-Key
1112 in: header
1113 required: true
1114 schema:
1115 type: string
1116 responses:
1117 '200':
1118 description: OK
1119"#,
1120 Some("yaml"),
1121 )
1122 .unwrap();
1123
1124 let operation = spec
1125 .spec
1126 .paths
1127 .paths
1128 .get("/users")
1129 .and_then(|p| p.as_item())
1130 .and_then(|p| p.get.as_ref())
1131 .unwrap();
1132
1133 let mut headers = HashMap::new();
1134 headers.insert("X-API-Key".to_string(), "secret-key".to_string());
1135
1136 let result = RequestValidator::validate_request(
1137 &spec,
1138 operation,
1139 &HashMap::new(),
1140 &HashMap::new(),
1141 &headers,
1142 None,
1143 )
1144 .unwrap();
1145
1146 assert!(result.valid || !result.errors.is_empty());
1148 }
1149
1150 #[test]
1151 fn test_validate_response_with_headers() {
1152 let spec = crate::openapi::spec::OpenApiSpec::from_string(
1153 r#"openapi: 3.0.0
1154info:
1155 title: Test API
1156 version: 1.0.0
1157paths:
1158 /users:
1159 get:
1160 responses:
1161 '200':
1162 description: OK
1163 headers:
1164 X-Total-Count:
1165 schema:
1166 type: integer
1167 content:
1168 application/json:
1169 schema:
1170 type: object
1171"#,
1172 Some("yaml"),
1173 )
1174 .unwrap();
1175
1176 let operation = spec
1177 .spec
1178 .paths
1179 .paths
1180 .get("/users")
1181 .and_then(|p| p.as_item())
1182 .and_then(|p| p.get.as_ref())
1183 .unwrap();
1184
1185 let mut headers = HashMap::new();
1186 headers.insert("X-Total-Count".to_string(), "100".to_string());
1187
1188 let result = ResponseValidator::validate_response(
1189 &spec,
1190 operation,
1191 200,
1192 &headers,
1193 Some(&serde_json::json!({})),
1194 )
1195 .unwrap();
1196
1197 assert!(result.valid || !result.errors.is_empty());
1199 }
1200
1201 #[test]
1202 fn test_validate_request_with_cookie_params() {
1203 let spec = crate::openapi::spec::OpenApiSpec::from_string(
1204 r#"openapi: 3.0.0
1205info:
1206 title: Test API
1207 version: 1.0.0
1208paths:
1209 /users:
1210 get:
1211 parameters:
1212 - name: sessionId
1213 in: cookie
1214 required: false
1215 schema:
1216 type: string
1217 responses:
1218 '200':
1219 description: OK
1220"#,
1221 Some("yaml"),
1222 )
1223 .unwrap();
1224
1225 let operation = spec
1226 .spec
1227 .paths
1228 .paths
1229 .get("/users")
1230 .and_then(|p| p.as_item())
1231 .and_then(|p| p.get.as_ref())
1232 .unwrap();
1233
1234 let result = RequestValidator::validate_request(
1236 &spec,
1237 operation,
1238 &HashMap::new(),
1239 &HashMap::new(),
1240 &HashMap::new(),
1241 None,
1242 )
1243 .unwrap();
1244
1245 assert!(result.valid || !result.errors.is_empty());
1247 }
1248
1249 #[test]
1250 fn test_validate_request_with_referenced_request_body() {
1251 let spec = crate::openapi::spec::OpenApiSpec::from_string(
1252 r#"openapi: 3.0.0
1253info:
1254 title: Test API
1255 version: 1.0.0
1256paths:
1257 /users:
1258 post:
1259 requestBody:
1260 $ref: '#/components/requestBodies/UserRequest'
1261 responses:
1262 '201':
1263 description: Created
1264components:
1265 requestBodies:
1266 UserRequest:
1267 required: true
1268 content:
1269 application/json:
1270 schema:
1271 type: object
1272 properties:
1273 name:
1274 type: string
1275"#,
1276 Some("yaml"),
1277 )
1278 .unwrap();
1279
1280 let operation = spec
1281 .spec
1282 .paths
1283 .paths
1284 .get("/users")
1285 .and_then(|p| p.as_item())
1286 .and_then(|p| p.post.as_ref())
1287 .unwrap();
1288
1289 let body = serde_json::json!({
1290 "name": "John Doe"
1291 });
1292
1293 let result = RequestValidator::validate_request(
1294 &spec,
1295 operation,
1296 &HashMap::new(),
1297 &HashMap::new(),
1298 &HashMap::new(),
1299 Some(&body),
1300 )
1301 .unwrap();
1302
1303 assert!(result.valid || !result.errors.is_empty());
1305 }
1306
1307 #[test]
1308 fn test_validate_response_with_referenced_schema() {
1309 let spec = crate::openapi::spec::OpenApiSpec::from_string(
1310 r#"openapi: 3.0.0
1311info:
1312 title: Test API
1313 version: 1.0.0
1314paths:
1315 /users:
1316 get:
1317 responses:
1318 '200':
1319 description: OK
1320 content:
1321 application/json:
1322 schema:
1323 $ref: '#/components/schemas/User'
1324components:
1325 schemas:
1326 User:
1327 type: object
1328 properties:
1329 id:
1330 type: integer
1331 name:
1332 type: string
1333"#,
1334 Some("yaml"),
1335 )
1336 .unwrap();
1337
1338 let operation = spec
1339 .spec
1340 .paths
1341 .paths
1342 .get("/users")
1343 .and_then(|p| p.as_item())
1344 .and_then(|p| p.get.as_ref())
1345 .unwrap();
1346
1347 let body = serde_json::json!({
1348 "id": 1,
1349 "name": "John Doe"
1350 });
1351
1352 let result = ResponseValidator::validate_response(
1353 &spec,
1354 operation,
1355 200,
1356 &HashMap::new(),
1357 Some(&body),
1358 )
1359 .unwrap();
1360
1361 assert!(result.valid || !result.errors.is_empty());
1363 }
1364}