1use utoipa::openapi::{RefOr, Schema};
4
5pub fn json_value_to_schema(value: &serde_json::Value) -> Result<RefOr<Schema>, String> {
8 if let Some(type_str) = value.get("type").and_then(|t| t.as_str()) {
9 match type_str {
10 "object" => {
11 let mut object_schema = utoipa::openapi::ObjectBuilder::new();
12
13 if let Some(properties) = value.get("properties").and_then(|p| p.as_object()) {
14 for (prop_name, prop_schema) in properties {
15 let prop_openapi_schema = json_value_to_schema(prop_schema)?;
16 object_schema = object_schema.property(prop_name, prop_openapi_schema);
17 }
18 }
19
20 if let Some(required) = value.get("required").and_then(|r| r.as_array()) {
21 for field in required {
22 if let Some(field_name) = field.as_str() {
23 object_schema = object_schema.required(field_name);
24 }
25 }
26 }
27
28 Ok(RefOr::T(Schema::Object(object_schema.build())))
29 }
30 "array" => {
31 let mut array_schema = utoipa::openapi::ArrayBuilder::new();
32
33 if let Some(items) = value.get("items") {
34 let items_schema = json_value_to_schema(items)?;
35 array_schema = array_schema.items(items_schema);
36 }
37
38 Ok(RefOr::T(Schema::Array(array_schema.build())))
39 }
40 "string" => {
41 let mut schema_type = utoipa::openapi::schema::Type::String;
42
43 if let Some(format) = value.get("format").and_then(|f| f.as_str()) {
44 match format {
45 "date-time" => schema_type = utoipa::openapi::schema::Type::String,
46 "date" => schema_type = utoipa::openapi::schema::Type::String,
47 "email" => schema_type = utoipa::openapi::schema::Type::String,
48 "uri" => schema_type = utoipa::openapi::schema::Type::String,
49 _ => {}
50 }
51 }
52
53 Ok(RefOr::T(Schema::Object(
54 utoipa::openapi::ObjectBuilder::new().schema_type(schema_type).build(),
55 )))
56 }
57 "integer" => Ok(RefOr::T(Schema::Object(
58 utoipa::openapi::ObjectBuilder::new()
59 .schema_type(utoipa::openapi::schema::Type::Integer)
60 .build(),
61 ))),
62 "number" => Ok(RefOr::T(Schema::Object(
63 utoipa::openapi::ObjectBuilder::new()
64 .schema_type(utoipa::openapi::schema::Type::Number)
65 .build(),
66 ))),
67 "boolean" => Ok(RefOr::T(Schema::Object(
68 utoipa::openapi::ObjectBuilder::new()
69 .schema_type(utoipa::openapi::schema::Type::Boolean)
70 .build(),
71 ))),
72 _ => Err(format!("Unsupported schema type: {}", type_str)),
73 }
74 } else {
75 Ok(RefOr::T(Schema::Object(utoipa::openapi::ObjectBuilder::new().build())))
76 }
77}
78
79pub fn json_schema_to_request_body(
81 schema: &serde_json::Value,
82) -> Result<utoipa::openapi::request_body::RequestBody, String> {
83 use utoipa::openapi::content::ContentBuilder;
84
85 let openapi_schema = json_value_to_schema(schema)?;
86
87 let content = ContentBuilder::new().schema(Some(openapi_schema)).build();
88
89 let mut request_body = utoipa::openapi::request_body::RequestBody::new();
90 request_body.content.insert("application/json".to_string(), content);
91
92 request_body.required = Some(utoipa::openapi::Required::True);
93
94 Ok(request_body)
95}
96
97pub fn json_schema_to_response(schema: &serde_json::Value) -> Result<utoipa::openapi::Response, String> {
99 use utoipa::openapi::content::ContentBuilder;
100
101 let openapi_schema = json_value_to_schema(schema)?;
102
103 let content = ContentBuilder::new().schema(Some(openapi_schema)).build();
104
105 let mut response = utoipa::openapi::Response::new("Successful response");
106 response.content.insert("application/json".to_string(), content);
107
108 Ok(response)
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_json_value_to_schema_string() {
117 let schema_json = serde_json::json!({
118 "type": "string"
119 });
120
121 let result = json_value_to_schema(&schema_json);
122 assert!(result.is_ok());
123 }
124
125 #[test]
126 fn test_json_value_to_schema_integer() {
127 let schema_json = serde_json::json!({
128 "type": "integer"
129 });
130
131 let result = json_value_to_schema(&schema_json);
132 assert!(result.is_ok());
133 }
134
135 #[test]
136 fn test_json_value_to_schema_number() {
137 let schema_json = serde_json::json!({
138 "type": "number"
139 });
140
141 let result = json_value_to_schema(&schema_json);
142 assert!(result.is_ok());
143 }
144
145 #[test]
146 fn test_json_value_to_schema_boolean() {
147 let schema_json = serde_json::json!({
148 "type": "boolean"
149 });
150
151 let result = json_value_to_schema(&schema_json);
152 assert!(result.is_ok());
153 }
154
155 #[test]
156 fn test_json_value_to_schema_object() {
157 let schema_json = serde_json::json!({
158 "type": "object",
159 "properties": {
160 "name": { "type": "string" },
161 "age": { "type": "integer" }
162 },
163 "required": ["name"]
164 });
165
166 let result = json_value_to_schema(&schema_json);
167 assert!(result.is_ok());
168
169 if let Ok(RefOr::T(Schema::Object(obj))) = result {
170 assert!(obj.properties.contains_key("name"));
171 assert!(obj.properties.contains_key("age"));
172 assert!(obj.required.contains(&"name".to_string()));
173 } else {
174 panic!("Expected Object schema");
175 }
176 }
177
178 #[test]
179 fn test_json_value_to_schema_array() {
180 let schema_json = serde_json::json!({
181 "type": "array",
182 "items": {
183 "type": "string"
184 }
185 });
186
187 let result = json_value_to_schema(&schema_json);
188 assert!(result.is_ok());
189
190 if let Ok(RefOr::T(Schema::Array(_))) = result {
191 } else {
192 panic!("Expected Array schema");
193 }
194 }
195
196 #[test]
197 fn test_json_value_to_schema_nested_object() {
198 let schema_json = serde_json::json!({
199 "type": "object",
200 "properties": {
201 "user": {
202 "type": "object",
203 "properties": {
204 "name": { "type": "string" },
205 "email": { "type": "string" }
206 }
207 }
208 }
209 });
210
211 let result = json_value_to_schema(&schema_json);
212 assert!(result.is_ok());
213 }
214
215 #[test]
216 fn test_json_schema_to_request_body() {
217 let schema_json = serde_json::json!({
218 "type": "object",
219 "properties": {
220 "title": { "type": "string" },
221 "count": { "type": "integer" }
222 },
223 "required": ["title"]
224 });
225
226 let result = json_schema_to_request_body(&schema_json);
227 assert!(result.is_ok());
228
229 let request_body = result.unwrap();
230 assert!(request_body.content.contains_key("application/json"));
231 assert!(matches!(request_body.required, Some(utoipa::openapi::Required::True)));
232 }
233
234 #[test]
235 fn test_json_schema_to_request_body_array() {
236 let schema_json = serde_json::json!({
237 "type": "array",
238 "items": {
239 "type": "object",
240 "properties": {
241 "id": { "type": "integer" }
242 }
243 }
244 });
245
246 let result = json_schema_to_request_body(&schema_json);
247 assert!(result.is_ok());
248
249 let request_body = result.unwrap();
250 assert!(request_body.content.contains_key("application/json"));
251 }
252
253 #[test]
254 fn test_json_schema_to_response() {
255 let schema_json = serde_json::json!({
256 "type": "object",
257 "properties": {
258 "id": { "type": "integer" },
259 "name": { "type": "string" }
260 }
261 });
262
263 let result = json_schema_to_response(&schema_json);
264 assert!(result.is_ok());
265
266 let response = result.unwrap();
267 assert!(response.content.contains_key("application/json"));
268 assert_eq!(response.description, "Successful response");
269 }
270
271 #[test]
272 fn test_json_schema_to_response_array() {
273 let schema_json = serde_json::json!({
274 "type": "array",
275 "items": {
276 "type": "string"
277 }
278 });
279
280 let result = json_schema_to_response(&schema_json);
281 assert!(result.is_ok());
282
283 let response = result.unwrap();
284 assert!(response.content.contains_key("application/json"));
285 }
286
287 #[test]
288 fn test_json_value_to_schema_string_with_format() {
289 let schema_json = serde_json::json!({
290 "type": "string",
291 "format": "date-time"
292 });
293
294 let result = json_value_to_schema(&schema_json);
295 assert!(result.is_ok());
296 }
297
298 #[test]
299 fn test_json_schema_to_request_body_empty_object() {
300 let schema_json = serde_json::json!({
301 "type": "object",
302 "properties": {}
303 });
304
305 let result = json_schema_to_request_body(&schema_json);
306 assert!(result.is_ok());
307 }
308
309 #[test]
310 fn test_circular_reference_simple_cycle() {
311 let schema_json = serde_json::json!({
312 "type": "object",
313 "properties": {
314 "id": { "type": "integer" },
315 "parent": { "$ref": "#/properties/id" }
316 }
317 });
318
319 let result = json_value_to_schema(&schema_json);
320 assert!(result.is_ok());
321 }
322
323 #[test]
324 fn test_self_referential_schema_direct() {
325 let schema_json = serde_json::json!({
326 "type": "object",
327 "properties": {
328 "value": { "type": "string" },
329 "self": { "$ref": "#" }
330 }
331 });
332
333 let result = json_value_to_schema(&schema_json);
334 assert!(result.is_ok());
335 }
336
337 #[test]
338 fn test_deeply_nested_object_10_levels() {
339 let schema_json = serde_json::json!({
340 "type": "object",
341 "properties": {
342 "l1": {
343 "type": "object",
344 "properties": {
345 "l2": {
346 "type": "object",
347 "properties": {
348 "l3": {
349 "type": "object",
350 "properties": {
351 "l4": {
352 "type": "object",
353 "properties": {
354 "l5": {
355 "type": "object",
356 "properties": {
357 "l6": {
358 "type": "object",
359 "properties": {
360 "l7": {
361 "type": "object",
362 "properties": {
363 "l8": {
364 "type": "object",
365 "properties": {
366 "l9": {
367 "type": "object",
368 "properties": {
369 "l10": { "type": "string" }
370 }
371 }
372 }
373 }
374 }
375 }
376 }
377 }
378 }
379 }
380 }
381 }
382 }
383 }
384 }
385 }
386 }
387 }
388 }
389 });
390
391 let result = json_value_to_schema(&schema_json);
392 assert!(result.is_ok(), "Deep nesting should not cause stack overflow");
393 }
394
395 #[test]
396 fn test_deeply_nested_array_5_levels() {
397 let schema_json = serde_json::json!({
398 "type": "array",
399 "items": {
400 "type": "array",
401 "items": {
402 "type": "array",
403 "items": {
404 "type": "array",
405 "items": {
406 "type": "array",
407 "items": { "type": "string" }
408 }
409 }
410 }
411 }
412 });
413
414 let result = json_value_to_schema(&schema_json);
415 assert!(result.is_ok());
416 }
417
418 #[test]
419 fn test_type_coercion_integer_to_number() {
420 let schema_json = serde_json::json!({
421 "type": "integer"
422 });
423
424 let result = json_value_to_schema(&schema_json);
425 assert!(result.is_ok());
426 if let Ok(RefOr::T(Schema::Object(obj))) = result {
427 assert!(matches!(
428 obj.schema_type,
429 utoipa::openapi::schema::SchemaType::Type(utoipa::openapi::schema::Type::Integer)
430 ));
431 } else {
432 panic!("Expected Object schema with Type::Integer");
433 }
434 }
435
436 #[test]
437 fn test_type_coercion_number_vs_integer() {
438 let int_schema = serde_json::json!({ "type": "integer" });
439 let num_schema = serde_json::json!({ "type": "number" });
440
441 let int_result = json_value_to_schema(&int_schema);
442 let num_result = json_value_to_schema(&num_schema);
443
444 assert!(int_result.is_ok());
445 assert!(num_result.is_ok());
446
447 if let (Ok(RefOr::T(Schema::Object(int_obj))), Ok(RefOr::T(Schema::Object(num_obj)))) = (int_result, num_result)
448 {
449 assert!(matches!(
450 int_obj.schema_type,
451 utoipa::openapi::schema::SchemaType::Type(utoipa::openapi::schema::Type::Integer)
452 ));
453 assert!(matches!(
454 num_obj.schema_type,
455 utoipa::openapi::schema::SchemaType::Type(utoipa::openapi::schema::Type::Number)
456 ));
457 }
458 }
459
460 #[test]
461 fn test_nullable_property_in_object() {
462 let schema_json = serde_json::json!({
463 "type": "object",
464 "properties": {
465 "id": { "type": "integer" },
466 "optional_field": { "type": "string" }
467 },
468 "required": ["id"]
469 });
470
471 let result = json_value_to_schema(&schema_json);
472 assert!(result.is_ok());
473
474 if let Ok(RefOr::T(Schema::Object(obj))) = result {
475 assert!(obj.required.contains(&"id".to_string()));
476 assert!(!obj.required.contains(&"optional_field".to_string()));
477 } else {
478 panic!("Expected Object schema");
479 }
480 }
481
482 #[test]
483 fn test_required_array_with_multiple_fields() {
484 let schema_json = serde_json::json!({
485 "type": "object",
486 "properties": {
487 "id": { "type": "integer" },
488 "name": { "type": "string" },
489 "email": { "type": "string" },
490 "optional": { "type": "string" }
491 },
492 "required": ["id", "name", "email"]
493 });
494
495 let result = json_value_to_schema(&schema_json);
496 assert!(result.is_ok());
497
498 if let Ok(RefOr::T(Schema::Object(obj))) = result {
499 assert!(obj.required.contains(&"id".to_string()));
500 assert!(obj.required.contains(&"name".to_string()));
501 assert!(obj.required.contains(&"email".to_string()));
502 assert!(!obj.required.contains(&"optional".to_string()));
503 }
504 }
505
506 #[test]
507 fn test_format_uuid() {
508 let schema_json = serde_json::json!({
509 "type": "string",
510 "format": "uuid"
511 });
512
513 let result = json_value_to_schema(&schema_json);
514 assert!(result.is_ok());
515 }
516
517 #[test]
518 fn test_format_email() {
519 let schema_json = serde_json::json!({
520 "type": "string",
521 "format": "email"
522 });
523
524 let result = json_value_to_schema(&schema_json);
525 assert!(result.is_ok());
526 }
527
528 #[test]
529 fn test_format_date_time() {
530 let schema_json = serde_json::json!({
531 "type": "string",
532 "format": "date-time"
533 });
534
535 let result = json_value_to_schema(&schema_json);
536 assert!(result.is_ok());
537
538 if let Ok(RefOr::T(Schema::Object(obj))) = result {
539 assert!(matches!(
540 obj.schema_type,
541 utoipa::openapi::schema::SchemaType::Type(utoipa::openapi::schema::Type::String)
542 ));
543 }
544 }
545
546 #[test]
547 fn test_format_date() {
548 let schema_json = serde_json::json!({
549 "type": "string",
550 "format": "date"
551 });
552
553 let result = json_value_to_schema(&schema_json);
554 assert!(result.is_ok());
555 }
556
557 #[test]
558 fn test_format_uri() {
559 let schema_json = serde_json::json!({
560 "type": "string",
561 "format": "uri"
562 });
563
564 let result = json_value_to_schema(&schema_json);
565 assert!(result.is_ok());
566 }
567
568 #[test]
569 fn test_format_unknown_custom_format() {
570 let schema_json = serde_json::json!({
571 "type": "string",
572 "format": "custom-format"
573 });
574
575 let result = json_value_to_schema(&schema_json);
576 assert!(result.is_ok(), "Unknown formats should be gracefully handled");
577 }
578
579 #[test]
580 fn test_array_of_objects() {
581 let schema_json = serde_json::json!({
582 "type": "array",
583 "items": {
584 "type": "object",
585 "properties": {
586 "id": { "type": "integer" },
587 "name": { "type": "string" }
588 },
589 "required": ["id"]
590 }
591 });
592
593 let result = json_value_to_schema(&schema_json);
594 assert!(result.is_ok());
595
596 if let Ok(RefOr::T(Schema::Array(_))) = result {
597 } else {
598 panic!("Expected Array schema");
599 }
600 }
601
602 #[test]
603 fn test_array_of_arrays_of_objects() {
604 let schema_json = serde_json::json!({
605 "type": "array",
606 "items": {
607 "type": "array",
608 "items": {
609 "type": "object",
610 "properties": {
611 "value": { "type": "string" }
612 }
613 }
614 }
615 });
616
617 let result = json_value_to_schema(&schema_json);
618 assert!(result.is_ok());
619 }
620
621 #[test]
622 fn test_object_with_additional_properties_true() {
623 let schema_json = serde_json::json!({
624 "type": "object",
625 "properties": {
626 "id": { "type": "integer" }
627 },
628 "additionalProperties": true
629 });
630
631 let result = json_value_to_schema(&schema_json);
632 assert!(result.is_ok());
633 }
634
635 #[test]
636 fn test_object_with_additional_properties_false() {
637 let schema_json = serde_json::json!({
638 "type": "object",
639 "properties": {
640 "id": { "type": "integer" }
641 },
642 "additionalProperties": false
643 });
644
645 let result = json_value_to_schema(&schema_json);
646 assert!(result.is_ok(), "additionalProperties:false should not cause errors");
647 }
648
649 #[test]
650 fn test_object_with_additional_properties_schema() {
651 let schema_json = serde_json::json!({
652 "type": "object",
653 "properties": {
654 "id": { "type": "integer" }
655 },
656 "additionalProperties": { "type": "string" }
657 });
658
659 let result = json_value_to_schema(&schema_json);
660 assert!(result.is_ok());
661 }
662
663 #[test]
664 fn test_empty_schema() {
665 let schema_json = serde_json::json!({});
666
667 let result = json_value_to_schema(&schema_json);
668 assert!(result.is_ok(), "Empty schema should create basic object");
669 }
670
671 #[test]
672 fn test_schema_with_only_type_field() {
673 let schema_json = serde_json::json!({
674 "type": "object"
675 });
676
677 let result = json_value_to_schema(&schema_json);
678 assert!(result.is_ok());
679
680 if let Ok(RefOr::T(Schema::Object(obj))) = result {
681 assert!(obj.properties.is_empty());
682 }
683 }
684
685 #[test]
686 fn test_array_without_items_schema() {
687 let schema_json = serde_json::json!({
688 "type": "array"
689 });
690
691 let result = json_value_to_schema(&schema_json);
692 assert!(result.is_ok());
693 }
694
695 #[test]
696 fn test_object_with_mixed_property_types() {
697 let schema_json = serde_json::json!({
698 "type": "object",
699 "properties": {
700 "id": { "type": "integer" },
701 "name": { "type": "string" },
702 "active": { "type": "boolean" },
703 "score": { "type": "number" },
704 "tags": {
705 "type": "array",
706 "items": { "type": "string" }
707 },
708 "metadata": {
709 "type": "object",
710 "properties": {
711 "created": { "type": "string", "format": "date-time" }
712 }
713 }
714 }
715 });
716
717 let result = json_value_to_schema(&schema_json);
718 assert!(result.is_ok());
719
720 if let Ok(RefOr::T(Schema::Object(obj))) = result {
721 assert_eq!(obj.properties.len(), 6);
722 assert!(obj.properties.contains_key("id"));
723 assert!(obj.properties.contains_key("name"));
724 assert!(obj.properties.contains_key("active"));
725 assert!(obj.properties.contains_key("score"));
726 assert!(obj.properties.contains_key("tags"));
727 assert!(obj.properties.contains_key("metadata"));
728 }
729 }
730
731 #[test]
732 fn test_nullable_complex_types() {
733 let schema_json = serde_json::json!({
734 "type": "object",
735 "properties": {
736 "user": {
737 "oneOf": [
738 { "type": "object", "properties": { "id": { "type": "integer" } } },
739 { "type": "null" }
740 ]
741 }
742 }
743 });
744
745 let result = json_value_to_schema(&schema_json);
746 assert!(result.is_ok());
747 }
748
749 #[test]
750 fn test_unsupported_type_error() {
751 let schema_json = serde_json::json!({
752 "type": "unsupported_type"
753 });
754
755 let result = json_value_to_schema(&schema_json);
756 assert!(result.is_err());
757 if let Err(err) = result {
758 assert!(err.contains("Unsupported schema type"));
759 }
760 }
761
762 #[test]
763 fn test_required_with_non_string_elements() {
764 let schema_json = serde_json::json!({
765 "type": "object",
766 "properties": {
767 "a": { "type": "string" },
768 "b": { "type": "integer" }
769 },
770 "required": [123, null, "a"]
771 });
772
773 let result = json_value_to_schema(&schema_json);
774 assert!(result.is_ok(), "Non-string elements in required should be skipped");
775
776 if let Ok(RefOr::T(Schema::Object(obj))) = result {
777 assert!(obj.required.contains(&"a".to_string()));
778 assert_eq!(obj.required.len(), 1);
779 }
780 }
781
782 #[test]
783 fn test_properties_with_null_values() {
784 let schema_json = serde_json::json!({
785 "type": "object",
786 "properties": {
787 "valid": { "type": "string" },
788 "null_value": null
789 }
790 });
791
792 let result = json_value_to_schema(&schema_json);
793 assert!(result.is_ok());
794 }
795
796 #[test]
797 fn test_object_with_empty_required_array() {
798 let schema_json = serde_json::json!({
799 "type": "object",
800 "properties": {
801 "id": { "type": "integer" },
802 "name": { "type": "string" }
803 },
804 "required": []
805 });
806
807 let result = json_value_to_schema(&schema_json);
808 assert!(result.is_ok());
809
810 if let Ok(RefOr::T(Schema::Object(obj))) = result {
811 assert!(obj.required.is_empty());
812 }
813 }
814
815 #[test]
816 fn test_request_body_with_missing_items() {
817 let schema_json = serde_json::json!({
818 "type": "array"
819 });
820
821 let result = json_schema_to_request_body(&schema_json);
822 assert!(result.is_ok());
823 }
824
825 #[test]
826 fn test_response_with_all_scalar_types() {
827 let types = vec!["string", "integer", "number", "boolean"];
828
829 for type_name in types {
830 let schema_json = serde_json::json!({
831 "type": type_name
832 });
833
834 let result = json_schema_to_response(&schema_json);
835 assert!(
836 result.is_ok(),
837 "Response schema with type '{}' should succeed",
838 type_name
839 );
840
841 let response = result.unwrap();
842 assert!(response.content.contains_key("application/json"));
843 }
844 }
845
846 #[test]
847 fn test_string_format_datetime_creates_string_type() {
848 let schema_json = serde_json::json!({
849 "type": "string",
850 "format": "date-time"
851 });
852
853 let result = json_value_to_schema(&schema_json);
854 assert!(result.is_ok());
855
856 if let Ok(RefOr::T(Schema::Object(obj))) = result {
857 assert!(matches!(
858 obj.schema_type,
859 utoipa::openapi::schema::SchemaType::Type(utoipa::openapi::schema::Type::String)
860 ));
861 }
862 }
863
864 #[test]
865 fn test_string_format_email_creates_string_type() {
866 let schema_json = serde_json::json!({
867 "type": "string",
868 "format": "email"
869 });
870
871 let result = json_value_to_schema(&schema_json);
872 assert!(result.is_ok());
873
874 if let Ok(RefOr::T(Schema::Object(obj))) = result {
875 assert!(matches!(
876 obj.schema_type,
877 utoipa::openapi::schema::SchemaType::Type(utoipa::openapi::schema::Type::String)
878 ));
879 }
880 }
881
882 #[test]
883 fn test_string_format_uri_creates_string_type() {
884 let schema_json = serde_json::json!({
885 "type": "string",
886 "format": "uri"
887 });
888
889 let result = json_value_to_schema(&schema_json);
890 assert!(result.is_ok());
891
892 if let Ok(RefOr::T(Schema::Object(obj))) = result {
893 assert!(matches!(
894 obj.schema_type,
895 utoipa::openapi::schema::SchemaType::Type(utoipa::openapi::schema::Type::String)
896 ));
897 }
898 }
899
900 #[test]
901 fn test_array_nested_with_mixed_object_types() {
902 let schema_json = serde_json::json!({
903 "type": "array",
904 "items": {
905 "type": "array",
906 "items": {
907 "type": "object",
908 "properties": {
909 "id": { "type": "integer" },
910 "tags": {
911 "type": "array",
912 "items": { "type": "string" }
913 }
914 },
915 "required": ["id"]
916 }
917 }
918 });
919
920 let result = json_value_to_schema(&schema_json);
921 assert!(result.is_ok());
922 }
923
924 #[test]
925 fn test_object_with_deeply_nested_arrays() {
926 let schema_json = serde_json::json!({
927 "type": "object",
928 "properties": {
929 "level1": {
930 "type": "array",
931 "items": {
932 "type": "array",
933 "items": {
934 "type": "array",
935 "items": {
936 "type": "string"
937 }
938 }
939 }
940 }
941 }
942 });
943
944 let result = json_value_to_schema(&schema_json);
945 assert!(result.is_ok());
946 }
947
948 #[test]
949 fn test_object_with_many_properties() {
950 let mut properties = serde_json::Map::new();
951 for i in 0..50 {
952 properties.insert(
953 format!("prop_{}", i),
954 serde_json::json!({ "type": if i % 2 == 0 { "string" } else { "integer" } }),
955 );
956 }
957
958 let schema_json = serde_json::json!({
959 "type": "object",
960 "properties": properties
961 });
962
963 let result = json_value_to_schema(&schema_json);
964 assert!(result.is_ok());
965
966 if let Ok(RefOr::T(Schema::Object(obj))) = result {
967 assert_eq!(obj.properties.len(), 50);
968 }
969 }
970
971 #[test]
972 fn test_required_field_not_in_properties() {
973 let schema_json = serde_json::json!({
974 "type": "object",
975 "properties": {
976 "id": { "type": "integer" }
977 },
978 "required": ["id", "missing_field"]
979 });
980
981 let result = json_value_to_schema(&schema_json);
982 assert!(result.is_ok());
983
984 if let Ok(RefOr::T(Schema::Object(obj))) = result {
985 assert!(obj.required.contains(&"id".to_string()));
986 assert!(obj.required.contains(&"missing_field".to_string()));
987 }
988 }
989
990 #[test]
991 fn test_empty_object_with_required_fields() {
992 let schema_json = serde_json::json!({
993 "type": "object",
994 "properties": {},
995 "required": ["field1", "field2"]
996 });
997
998 let result = json_value_to_schema(&schema_json);
999 assert!(result.is_ok());
1000
1001 if let Ok(RefOr::T(Schema::Object(obj))) = result {
1002 assert!(obj.required.contains(&"field1".to_string()));
1003 assert!(obj.required.contains(&"field2".to_string()));
1004 }
1005 }
1006
1007 #[test]
1008 fn test_array_items_missing_completely() {
1009 let schema_json = serde_json::json!({
1010 "type": "array"
1011 });
1012
1013 let result = json_value_to_schema(&schema_json);
1014 assert!(result.is_ok());
1015
1016 if let Ok(RefOr::T(Schema::Array(_arr))) = result {
1017 } else {
1018 panic!("Expected Array schema");
1019 }
1020 }
1021
1022 #[test]
1023 fn test_nested_object_mixed_required_across_levels() {
1024 let schema_json = serde_json::json!({
1025 "type": "object",
1026 "properties": {
1027 "level1": {
1028 "type": "object",
1029 "properties": {
1030 "level2": {
1031 "type": "object",
1032 "properties": {
1033 "value": { "type": "string" }
1034 },
1035 "required": ["value"]
1036 }
1037 },
1038 "required": ["level2"]
1039 }
1040 },
1041 "required": ["level1"]
1042 });
1043
1044 let result = json_value_to_schema(&schema_json);
1045 assert!(result.is_ok());
1046
1047 if let Ok(RefOr::T(Schema::Object(outer))) = result {
1048 assert!(outer.required.contains(&"level1".to_string()));
1049 }
1050 }
1051
1052 #[test]
1053 fn test_string_format_all_known_formats() {
1054 let formats = vec!["date-time", "date", "email", "uri"];
1055
1056 for format in formats {
1057 let schema_json = serde_json::json!({
1058 "type": "string",
1059 "format": format
1060 });
1061
1062 let result = json_value_to_schema(&schema_json);
1063 assert!(result.is_ok(), "Format '{}' should be handled", format);
1064 }
1065 }
1066
1067 #[test]
1068 fn test_request_body_complex_nested_structure() {
1069 let schema_json = serde_json::json!({
1070 "type": "object",
1071 "properties": {
1072 "user": {
1073 "type": "object",
1074 "properties": {
1075 "id": { "type": "integer" },
1076 "profile": {
1077 "type": "object",
1078 "properties": {
1079 "name": { "type": "string" },
1080 "contacts": {
1081 "type": "array",
1082 "items": {
1083 "type": "object",
1084 "properties": {
1085 "email": { "type": "string" },
1086 "phone": { "type": "string" }
1087 }
1088 }
1089 }
1090 }
1091 }
1092 }
1093 }
1094 },
1095 "required": ["user"]
1096 });
1097
1098 let result = json_schema_to_request_body(&schema_json);
1099 assert!(result.is_ok());
1100
1101 let request_body = result.unwrap();
1102 assert!(request_body.content.contains_key("application/json"));
1103 assert!(matches!(request_body.required, Some(utoipa::openapi::Required::True)));
1104 }
1105
1106 #[test]
1107 fn test_response_array_of_complex_objects() {
1108 let schema_json = serde_json::json!({
1109 "type": "array",
1110 "items": {
1111 "type": "object",
1112 "properties": {
1113 "id": { "type": "integer" },
1114 "name": { "type": "string" },
1115 "created_at": { "type": "string", "format": "date-time" }
1116 },
1117 "required": ["id", "name"]
1118 }
1119 });
1120
1121 let result = json_schema_to_response(&schema_json);
1122 assert!(result.is_ok());
1123
1124 let response = result.unwrap();
1125 assert!(response.content.contains_key("application/json"));
1126 assert_eq!(response.description, "Successful response");
1127 }
1128
1129 #[test]
1130 fn test_object_property_with_format_but_type_string() {
1131 let schema_json = serde_json::json!({
1132 "type": "object",
1133 "properties": {
1134 "timestamp": {
1135 "type": "string",
1136 "format": "date-time"
1137 },
1138 "email": {
1139 "type": "string",
1140 "format": "email"
1141 }
1142 }
1143 });
1144
1145 let result = json_value_to_schema(&schema_json);
1146 assert!(result.is_ok());
1147
1148 if let Ok(RefOr::T(Schema::Object(obj))) = result {
1149 assert!(obj.properties.contains_key("timestamp"));
1150 assert!(obj.properties.contains_key("email"));
1151 }
1152 }
1153
1154 #[test]
1155 fn test_duplicate_required_fields() {
1156 let schema_json = serde_json::json!({
1157 "type": "object",
1158 "properties": {
1159 "id": { "type": "integer" },
1160 "name": { "type": "string" }
1161 },
1162 "required": ["id", "name", "id", "name"]
1163 });
1164
1165 let result = json_value_to_schema(&schema_json);
1166 assert!(result.is_ok());
1167
1168 if let Ok(RefOr::T(Schema::Object(obj))) = result {
1169 assert!(obj.required.contains(&"id".to_string()));
1170 assert!(obj.required.contains(&"name".to_string()));
1171 }
1172 }
1173
1174 #[test]
1175 fn test_object_with_very_long_property_names() {
1176 let long_name = "very_long_property_name_that_is_256_characters_or_more_\
1177 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
1178 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
1179 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
1180 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
1181
1182 let schema_json = serde_json::json!({
1183 "type": "object",
1184 "properties": {
1185 long_name: { "type": "string" }
1186 }
1187 });
1188
1189 let result = json_value_to_schema(&schema_json);
1190 assert!(result.is_ok());
1191
1192 if let Ok(RefOr::T(Schema::Object(obj))) = result {
1193 assert!(obj.properties.contains_key(long_name));
1194 }
1195 }
1196
1197 #[test]
1198 fn test_arrays_of_all_primitive_types() {
1199 let types = vec!["string", "integer", "number", "boolean"];
1200
1201 for type_name in types {
1202 let schema_json = serde_json::json!({
1203 "type": "array",
1204 "items": { "type": type_name }
1205 });
1206
1207 let result = json_value_to_schema(&schema_json);
1208 assert!(result.is_ok(), "Array of {} should be handled correctly", type_name);
1209 }
1210 }
1211
1212 #[test]
1213 fn test_large_object_with_mixed_required_optional() {
1214 let mut properties = serde_json::Map::new();
1215 let mut required = Vec::new();
1216
1217 for i in 0..30 {
1218 properties.insert(format!("field_{}", i), serde_json::json!({ "type": "string" }));
1219 if i % 3 == 0 {
1220 required.push(format!("field_{}", i));
1221 }
1222 }
1223
1224 let schema_json = serde_json::json!({
1225 "type": "object",
1226 "properties": properties,
1227 "required": required
1228 });
1229
1230 let result = json_value_to_schema(&schema_json);
1231 assert!(result.is_ok());
1232
1233 if let Ok(RefOr::T(Schema::Object(obj))) = result {
1234 assert!(obj.required.len() >= 9);
1235 assert!(obj.properties.len() == 30);
1236 }
1237 }
1238
1239 #[test]
1240 fn test_object_no_properties_with_required() {
1241 let schema_json = serde_json::json!({
1242 "type": "object",
1243 "required": ["name", "age"]
1244 });
1245
1246 let result = json_value_to_schema(&schema_json);
1247 assert!(result.is_ok());
1248
1249 if let Ok(RefOr::T(Schema::Object(obj))) = result {
1250 assert!(obj.required.contains(&"name".to_string()));
1251 assert!(obj.required.contains(&"age".to_string()));
1252 }
1253 }
1254
1255 #[test]
1256 fn test_request_body_all_optional_fields() {
1257 let schema_json = serde_json::json!({
1258 "type": "object",
1259 "properties": {
1260 "name": { "type": "string" },
1261 "email": { "type": "string" },
1262 "age": { "type": "integer" }
1263 }
1264 });
1265
1266 let result = json_schema_to_request_body(&schema_json);
1267 assert!(result.is_ok());
1268
1269 let request_body = result.unwrap();
1270 assert!(request_body.content.contains_key("application/json"));
1271 }
1272
1273 #[test]
1274 fn test_integer_with_min_max_values() {
1275 let schema_json = serde_json::json!({
1276 "type": "integer",
1277 "minimum": 0,
1278 "maximum": 100
1279 });
1280
1281 let result = json_value_to_schema(&schema_json);
1282 assert!(result.is_ok());
1283 }
1284
1285 #[test]
1286 fn test_string_with_length_constraints() {
1287 let schema_json = serde_json::json!({
1288 "type": "string",
1289 "minLength": 1,
1290 "maxLength": 255
1291 });
1292
1293 let result = json_value_to_schema(&schema_json);
1294 assert!(result.is_ok());
1295 }
1296
1297 #[test]
1298 fn test_array_with_item_count_constraints() {
1299 let schema_json = serde_json::json!({
1300 "type": "array",
1301 "items": { "type": "string" },
1302 "minItems": 1,
1303 "maxItems": 10
1304 });
1305
1306 let result = json_value_to_schema(&schema_json);
1307 assert!(result.is_ok());
1308 }
1309
1310 #[test]
1311 fn test_object_with_pattern_properties() {
1312 let schema_json = serde_json::json!({
1313 "type": "object",
1314 "properties": {
1315 "id": { "type": "integer" }
1316 },
1317 "patternProperties": {
1318 "^S_": { "type": "string" }
1319 }
1320 });
1321
1322 let result = json_value_to_schema(&schema_json);
1323 assert!(result.is_ok());
1324 }
1325
1326 #[test]
1327 fn test_deeply_nested_object_15_levels() {
1328 let mut schema = serde_json::json!({ "type": "string" });
1329
1330 for i in 0..15 {
1331 schema = serde_json::json!({
1332 "type": "object",
1333 "properties": {
1334 format!("level_{}", i): schema
1335 }
1336 });
1337 }
1338
1339 let result = json_value_to_schema(&schema);
1340 assert!(result.is_ok(), "15-level deep nesting should not cause stack overflow");
1341 }
1342
1343 #[test]
1344 fn test_object_with_unicode_property_names() {
1345 let schema_json = serde_json::json!({
1346 "type": "object",
1347 "properties": {
1348 "名前": { "type": "string" },
1349 "年齢": { "type": "integer" },
1350 "🚀": { "type": "string" }
1351 }
1352 });
1353
1354 let result = json_value_to_schema(&schema_json);
1355 assert!(result.is_ok());
1356
1357 if let Ok(RefOr::T(Schema::Object(obj))) = result {
1358 assert!(obj.properties.contains_key("名前"));
1359 assert!(obj.properties.contains_key("年齢"));
1360 assert!(obj.properties.contains_key("🚀"));
1361 }
1362 }
1363}