1mod error;
6pub use error::{OrderedF64, ValidationError, ValidationResult};
7
8use crate::json_pointer::JsonPointer;
9use crate::json_schema::JsonSchema;
10use crate::json_schema::json_schema::AdditionalProperties;
11use crate::json_schema::ref_resolver;
12use serde_json::Value;
13
14fn json_type_name(v: &Value) -> &'static str {
16 match v {
17 Value::Null => "null",
18 Value::Bool(_) => "boolean",
19 Value::Number(_) => "number",
20 Value::String(_) => "string",
21 Value::Array(_) => "array",
22 Value::Object(_) => "object",
23 }
24}
25
26fn value_to_display_string(v: &Value) -> String {
28 serde_json::to_string(v).unwrap_or_else(|_| "?".to_string())
29}
30
31pub fn validate(schema: &JsonSchema, instance: &Value) -> ValidationResult {
54 validate_with_root(schema, schema, instance)
55}
56
57#[expect(clippy::too_many_lines)]
58fn validate_with_root(
59 root: &JsonSchema,
60 schema: &JsonSchema,
61 instance: &Value,
62) -> ValidationResult {
63 let mut errors: Vec<ValidationError> = Vec::new();
64 let mut stack: Vec<(&JsonSchema, &Value, JsonPointer)> = Vec::new();
65 stack.push((schema, instance, JsonPointer::root()));
66
67 while let Some((schema, instance, instance_path)) = stack.pop() {
68 let schema: &JsonSchema = match ref_resolver::resolve_schema_ref_transitive(root, schema) {
69 Ok(s) => s,
70 Err(e) => {
71 let ref_str: String = schema
72 .ref_
73 .clone()
74 .unwrap_or_else(|| "<missing>".to_string());
75 errors.push(ValidationError::InvalidRef {
76 instance_path: instance_path.clone(),
77 ref_str,
78 reason: format!("{e:?}"),
79 });
80 continue;
81 }
82 };
83
84 if let Some(ref expected) = schema.const_value
85 && instance != expected
86 {
87 let expected_str: String = value_to_display_string(expected);
88 let actual_str: String = value_to_display_string(instance);
89 errors.push(ValidationError::NotConst {
90 instance_path: instance_path.clone(),
91 expected: expected_str,
92 actual: actual_str,
93 });
94 continue;
95 }
96 if let Some(ref allowed) = schema.enum_values
97 && !allowed.is_empty()
98 && !allowed.iter().any(|a| a == instance)
99 {
100 let invalid_value: String = value_to_display_string(instance);
101 let allowed_strs: Vec<String> = allowed.iter().map(value_to_display_string).collect();
102 errors.push(ValidationError::NotInEnum {
103 instance_path: instance_path.clone(),
104 invalid_value,
105 allowed: allowed_strs,
106 });
107 continue;
108 }
109 if let Some(ref any_of) = schema.any_of {
110 if any_of.is_empty() {
111 errors.push(ValidationError::NoSubschemaMatched {
112 instance_path: instance_path.clone(),
113 subschema_count: 0,
114 });
115 } else {
116 let mut at_least_one_passed: bool = false;
117 for subschema in any_of {
118 let sub_result: ValidationResult =
119 validate_with_root(root, subschema, instance);
120 if sub_result.is_ok() {
121 at_least_one_passed = true;
122 break;
123 }
124 }
125 if !at_least_one_passed {
126 errors.push(ValidationError::NoSubschemaMatched {
127 instance_path: instance_path.clone(),
128 subschema_count: any_of.len(),
129 });
130 }
131 }
132 continue;
133 }
134 if let Some(ref one_of) = schema.one_of {
135 if one_of.is_empty() {
136 errors.push(ValidationError::NoSubschemaMatched {
137 instance_path: instance_path.clone(),
138 subschema_count: 0,
139 });
140 } else {
141 let mut pass_count: usize = 0;
142 for subschema in one_of {
143 let sub_result: ValidationResult =
144 validate_with_root(root, subschema, instance);
145 if sub_result.is_ok() {
146 pass_count += 1;
147 }
148 }
149 if pass_count == 0 {
150 errors.push(ValidationError::NoSubschemaMatched {
151 instance_path: instance_path.clone(),
152 subschema_count: one_of.len(),
153 });
154 } else if pass_count > 1 {
155 errors.push(ValidationError::MultipleSubschemasMatched {
156 instance_path: instance_path.clone(),
157 subschema_count: one_of.len(),
158 match_count: pass_count,
159 });
160 }
161 }
162 continue;
163 }
164 if let Some(ref all_of) = schema.all_of
165 && !all_of.is_empty()
166 {
167 for subschema in all_of.iter().rev() {
168 stack.push((subschema, instance, instance_path.clone()));
169 }
170 continue;
171 }
172 let expected_type: Option<&str> = schema.type_.as_deref();
173 match expected_type {
174 Some("object") => {
175 let Some(obj) = instance.as_object() else {
176 errors.push(ValidationError::ExpectedObject {
177 instance_path: instance_path.clone(),
178 got: json_type_name(instance).to_string(),
179 });
180 continue;
181 };
182 if let Some(ref required) = schema.required {
183 for name in required {
184 if !obj.contains_key(name) {
185 errors.push(ValidationError::MissingRequired {
186 instance_path: instance_path.push(name),
187 property: name.clone(),
188 });
189 }
190 }
191 }
192 let mut pending: Vec<(&JsonSchema, &Value, JsonPointer)> = Vec::new();
194 for (key, sub_schema) in &schema.properties {
195 if let Some(value) = obj.get(key) {
196 let path = instance_path.push(key);
197 pending.push((sub_schema, value, path));
198 }
199 }
200 for item in pending.into_iter().rev() {
201 stack.push(item);
202 }
203 let additional_keys: Vec<&str> = obj
205 .keys()
206 .filter(|k| !schema.properties.contains_key(*k))
207 .map(String::as_str)
208 .collect();
209 if !additional_keys.is_empty() {
210 match schema.additional_properties.as_ref() {
211 None | Some(AdditionalProperties::Allow) => {}
212 Some(AdditionalProperties::Forbid) => {
213 for key in additional_keys {
214 errors.push(ValidationError::DisallowedAdditionalProperty {
215 instance_path: instance_path.push(key),
216 property: key.to_string(),
217 });
218 }
219 }
220 Some(AdditionalProperties::Schema(sub_schema)) => {
221 for key in additional_keys {
222 if let Some(value) = obj.get(key) {
223 let path = instance_path.push(key);
224 stack.push((sub_schema, value, path));
225 }
226 }
227 }
228 }
229 }
230 }
231 Some("string") => {
232 if !instance.is_string() {
233 errors.push(ValidationError::ExpectedString {
234 instance_path: instance_path.clone(),
235 got: json_type_name(instance).to_string(),
236 });
237 }
238 if let Some(s) = instance.as_str() {
240 let char_count: u64 = s.chars().count() as u64;
241 if let Some(min_length) = schema.min_length
242 && char_count < min_length
243 {
244 errors.push(ValidationError::TooShort {
245 instance_path: instance_path.clone(),
246 min_length,
247 actual_length: char_count,
248 });
249 }
250 if let Some(max_length) = schema.max_length
251 && char_count > max_length
252 {
253 errors.push(ValidationError::TooLong {
254 instance_path: instance_path.clone(),
255 max_length,
256 actual_length: char_count,
257 });
258 }
259 if let Some(ref pattern) = schema.pattern {
260 match regress::Regex::new(pattern) {
261 Ok(re) => {
262 if re.find(s).is_none() {
263 errors.push(ValidationError::PatternMismatch {
264 instance_path: instance_path.clone(),
265 pattern: pattern.clone(),
266 value: s.to_string(),
267 });
268 }
269 }
270 Err(_) => {
271 errors.push(ValidationError::InvalidPatternInSchema {
272 instance_path: instance_path.clone(),
273 pattern: pattern.clone(),
274 });
275 }
276 }
277 }
278 }
279 #[cfg(feature = "uuid")]
280 if schema.format.as_deref() == Some("uuid") {
281 if let Some(s) = instance.as_str() {
282 if uuid::Uuid::parse_str(s).is_err() {
283 errors.push(ValidationError::InvalidUuidFormat {
284 instance_path: instance_path.clone(),
285 value: s.to_string(),
286 });
287 }
288 }
289 }
290 }
291 Some("integer") => {
292 let valid = instance.as_number().is_some_and(|n| n.as_i64().is_some());
293 if !valid {
294 errors.push(ValidationError::ExpectedInteger {
295 instance_path: instance_path.clone(),
296 got: json_type_name(instance).to_string(),
297 });
298 } else if let Some(instance_f64) =
299 instance.as_number().and_then(serde_json::Number::as_f64)
300 {
301 if let Some(min) = schema.minimum
302 && instance_f64 < min
303 {
304 errors.push(ValidationError::BelowMinimum {
305 instance_path: instance_path.clone(),
306 minimum: crate::validator::error::OrderedF64(min),
307 actual: crate::validator::error::OrderedF64(instance_f64),
308 });
309 }
310 if let Some(max) = schema.maximum
311 && instance_f64 > max
312 {
313 errors.push(ValidationError::AboveMaximum {
314 instance_path: instance_path.clone(),
315 maximum: crate::validator::error::OrderedF64(max),
316 actual: crate::validator::error::OrderedF64(instance_f64),
317 });
318 }
319 }
320 }
321 Some("number") => {
322 let valid = instance.as_number().is_some();
323 if !valid {
324 errors.push(ValidationError::ExpectedNumber {
325 instance_path: instance_path.clone(),
326 got: json_type_name(instance).to_string(),
327 });
328 } else if let Some(instance_f64) =
329 instance.as_number().and_then(serde_json::Number::as_f64)
330 {
331 if let Some(min) = schema.minimum
332 && instance_f64 < min
333 {
334 errors.push(ValidationError::BelowMinimum {
335 instance_path: instance_path.clone(),
336 minimum: crate::validator::error::OrderedF64(min),
337 actual: crate::validator::error::OrderedF64(instance_f64),
338 });
339 }
340 if let Some(max) = schema.maximum
341 && instance_f64 > max
342 {
343 errors.push(ValidationError::AboveMaximum {
344 instance_path: instance_path.clone(),
345 maximum: crate::validator::error::OrderedF64(max),
346 actual: crate::validator::error::OrderedF64(instance_f64),
347 });
348 }
349 }
350 }
351 Some("array") => {
352 let Some(arr) = instance.as_array() else {
353 errors.push(ValidationError::ExpectedArray {
354 instance_path: instance_path.clone(),
355 got: json_type_name(instance).to_string(),
356 });
357 continue;
358 };
359 let actual_count: u64 = arr.len() as u64;
360 if let Some(min_items) = schema.min_items
361 && arr.len() < min_items.try_into().unwrap_or(usize::MAX)
362 {
363 errors.push(ValidationError::TooFewItems {
364 instance_path: instance_path.clone(),
365 min_items,
366 actual_count,
367 });
368 }
369 if let Some(max_items) = schema.max_items
370 && arr.len() > max_items.try_into().unwrap_or(0)
371 {
372 errors.push(ValidationError::TooManyItems {
373 instance_path: instance_path.clone(),
374 max_items,
375 actual_count,
376 });
377 }
378 if schema.unique_items == Some(true) {
379 let mut duplicate_value_opt: Option<String> = None;
380 for i in 0..arr.len() {
381 for j in (i + 1)..arr.len() {
382 if arr[i] == arr[j] {
383 duplicate_value_opt = Some(value_to_display_string(&arr[i]));
384 break;
385 }
386 }
387 if duplicate_value_opt.is_some() {
388 break;
389 }
390 }
391 if let Some(duplicate_value) = duplicate_value_opt {
392 errors.push(ValidationError::DuplicateArrayItems {
393 instance_path: instance_path.clone(),
394 duplicate_value,
395 });
396 }
397 }
398 if let Some(ref item_schema) = schema.items {
399 let mut pending: Vec<(&JsonSchema, &Value, JsonPointer)> = Vec::new();
400 for (i, elem) in arr.iter().enumerate() {
401 let path = instance_path.push(&i.to_string());
402 pending.push((item_schema, elem, path));
403 }
404 for item in pending.into_iter().rev() {
405 stack.push(item);
406 }
407 }
408 }
409 Some("boolean") => {
410 if !instance.is_boolean() {
411 errors.push(ValidationError::ExpectedBoolean {
412 instance_path: instance_path.clone(),
413 got: json_type_name(instance).to_string(),
414 });
415 }
416 }
417 None | Some(_) => {
418 if let Some(obj) = instance.as_object() {
420 if let Some(ref required) = schema.required {
421 for name in required {
422 if !obj.contains_key(name) {
423 errors.push(ValidationError::MissingRequired {
424 instance_path: instance_path.push(name),
425 property: name.clone(),
426 });
427 }
428 }
429 }
430 let mut pending: Vec<(&JsonSchema, &Value, JsonPointer)> = Vec::new();
431 for (key, sub_schema) in &schema.properties {
432 if let Some(value) = obj.get(key) {
433 let path = instance_path.push(key);
434 pending.push((sub_schema, value, path));
435 }
436 }
437 for item in pending.into_iter().rev() {
438 stack.push(item);
439 }
440 }
441 }
442 }
443 }
444
445 if errors.is_empty() {
446 Ok(())
447 } else {
448 Err(errors)
449 }
450}
451
452#[cfg(test)]
453mod tests {
454 use super::{OrderedF64, ValidationError, ValidationResult, validate};
455 use crate::json_pointer::JsonPointer;
456 use crate::json_schema::JsonSchema;
457 use crate::json_schema::json_schema::AdditionalProperties;
458 use crate::json_schema::ref_resolver::RefResolutionError;
459 use serde_json::json;
460 use std::collections::BTreeMap;
461
462 fn schema_object_with_required(
463 required: Vec<&str>,
464 properties: BTreeMap<String, JsonSchema>,
465 ) -> JsonSchema {
466 JsonSchema {
467 type_: Some("object".to_string()),
468 properties,
469 required: Some(required.into_iter().map(String::from).collect()),
470 ..Default::default()
471 }
472 }
473
474 #[test]
475 fn validate_ref_to_defs_valid() {
476 let schema: JsonSchema = serde_json::from_str(
477 r##"{
478 "$defs": {
479 "Address": {
480 "type": "object",
481 "properties": { "city": { "type": "string" } },
482 "required": ["city"]
483 }
484 },
485 "type": "object",
486 "properties": { "address": { "$ref": "#/$defs/Address" } },
487 "required": ["address"]
488}"##,
489 )
490 .expect("parse schema");
491 let instance = json!({"address": {"city": "NYC"}});
492 let actual: ValidationResult = validate(&schema, &instance);
493 let expected: ValidationResult = Ok(());
494 assert_eq!(expected, actual);
495 }
496
497 #[test]
498 fn validate_ref_to_missing_defs_emits_invalid_ref() {
499 let schema: JsonSchema = serde_json::from_str(
500 r##"{
501 "type": "object",
502 "properties": { "x": { "$ref": "#/$defs/Missing" } }
503}"##,
504 )
505 .expect("parse schema");
506 let instance = json!({"x": "hi"});
507 let actual: ValidationResult = validate(&schema, &instance);
508 let expected_reason: String = format!(
509 "{:?}",
510 RefResolutionError::DefsMissing {
511 ref_str: "#/$defs/Missing".to_string()
512 }
513 );
514 let expected: ValidationResult = Err(vec![ValidationError::InvalidRef {
515 instance_path: JsonPointer::root().push("x"),
516 ref_str: "#/$defs/Missing".to_string(),
517 reason: expected_reason,
518 }]);
519 assert_eq!(expected, actual);
520 }
521
522 #[test]
523 fn validate_ref_cycle_in_defs_emits_invalid_ref() {
524 let schema: JsonSchema = serde_json::from_str(
525 r##"{
526 "$defs": {
527 "A": { "$ref": "#/$defs/B" },
528 "B": { "$ref": "#/$defs/A" }
529 },
530 "type": "object",
531 "properties": { "x": { "$ref": "#/$defs/A" } }
532}"##,
533 )
534 .expect("parse schema");
535 let instance = json!({"x": 42});
536 let actual: ValidationResult = validate(&schema, &instance);
537 let expected_reason: String = format!(
538 "{:?}",
539 RefResolutionError::RefCycle {
540 ref_str: "#/$defs/A".to_string()
541 }
542 );
543 let expected: ValidationResult = Err(vec![ValidationError::InvalidRef {
544 instance_path: JsonPointer::root().push("x"),
545 ref_str: "#/$defs/A".to_string(),
546 reason: expected_reason,
547 }]);
548 assert_eq!(expected, actual);
549 }
550
551 #[test]
552 fn valid_object_with_required_and_properties() {
553 let schema = schema_object_with_required(vec!["a"], {
554 let mut m = BTreeMap::new();
555 m.insert(
556 "a".to_string(),
557 JsonSchema {
558 type_: Some("string".to_string()),
559 ..Default::default()
560 },
561 );
562 m.insert(
563 "b".to_string(),
564 JsonSchema {
565 type_: Some("string".to_string()),
566 ..Default::default()
567 },
568 );
569 m
570 });
571 let instance = json!({"a": "x", "b": "y"});
572 let actual: ValidationResult = validate(&schema, &instance);
573 let expected: ValidationResult = Ok(());
574 assert_eq!(expected, actual);
575 }
576
577 #[test]
578 fn missing_required_property() {
579 let schema = schema_object_with_required(vec!["name"], {
580 let mut m = BTreeMap::new();
581 m.insert(
582 "name".to_string(),
583 JsonSchema {
584 type_: Some("string".to_string()),
585 ..Default::default()
586 },
587 );
588 m
589 });
590 let instance = json!({});
591 let actual: ValidationResult = validate(&schema, &instance);
592 let expected: ValidationResult = Err(vec![ValidationError::MissingRequired {
593 instance_path: JsonPointer::root().push("name"),
594 property: "name".to_string(),
595 }]);
596 assert_eq!(expected, actual);
597 }
598
599 #[test]
600 fn additional_properties_absent_allows_extra_key() {
601 let schema = schema_object_with_required(vec!["a"], {
602 let mut m = BTreeMap::new();
603 m.insert(
604 "a".to_string(),
605 JsonSchema {
606 type_: Some("string".to_string()),
607 ..Default::default()
608 },
609 );
610 m
611 });
612 let instance = json!({"a": "x", "extra": 1});
613 let actual: ValidationResult = validate(&schema, &instance);
614 let expected: ValidationResult = Ok(());
615 assert_eq!(expected, actual);
616 }
617
618 #[test]
619 fn additional_properties_false_rejects_one_extra_key() {
620 let mut schema = schema_object_with_required(vec!["a"], {
621 let mut m = BTreeMap::new();
622 m.insert(
623 "a".to_string(),
624 JsonSchema {
625 type_: Some("string".to_string()),
626 ..Default::default()
627 },
628 );
629 m
630 });
631 schema.additional_properties = Some(AdditionalProperties::Forbid);
632 let instance = json!({"a": "x", "extra": 1});
633 let actual: ValidationResult = validate(&schema, &instance);
634 let expected: ValidationResult = Err(vec![ValidationError::DisallowedAdditionalProperty {
635 instance_path: JsonPointer::root().push("extra"),
636 property: "extra".to_string(),
637 }]);
638 assert_eq!(expected, actual);
639 }
640
641 #[test]
642 fn additional_properties_false_rejects_multiple_extra_keys() {
643 let mut schema = schema_object_with_required(vec!["a"], {
644 let mut m = BTreeMap::new();
645 m.insert(
646 "a".to_string(),
647 JsonSchema {
648 type_: Some("string".to_string()),
649 ..Default::default()
650 },
651 );
652 m
653 });
654 schema.additional_properties = Some(AdditionalProperties::Forbid);
655 let instance = json!({"a": "x", "extra1": 1, "extra2": 2});
656 let actual_result: ValidationResult = validate(&schema, &instance);
657 let expected_errors: Vec<ValidationError> = vec![
658 ValidationError::DisallowedAdditionalProperty {
659 instance_path: JsonPointer::root().push("extra1"),
660 property: "extra1".to_string(),
661 },
662 ValidationError::DisallowedAdditionalProperty {
663 instance_path: JsonPointer::root().push("extra2"),
664 property: "extra2".to_string(),
665 },
666 ];
667 let mut expected_sorted = expected_errors;
668 expected_sorted.sort_by(|a, b| {
669 a.instance_path()
670 .to_string()
671 .cmp(&b.instance_path().to_string())
672 });
673 let mut actual_errors = actual_result.expect_err("expected validation errors");
674 actual_errors.sort_by(|a, b| {
675 a.instance_path()
676 .to_string()
677 .cmp(&b.instance_path().to_string())
678 });
679 let expected: ValidationResult = Err(expected_sorted);
680 let actual: ValidationResult = Err(actual_errors);
681 assert_eq!(expected, actual);
682 }
683
684 #[test]
685 fn additional_properties_false_empty_object_valid() {
686 let mut schema = schema_object_with_required(vec![], {
687 let mut m = BTreeMap::new();
688 m.insert(
689 "a".to_string(),
690 JsonSchema {
691 type_: Some("string".to_string()),
692 ..Default::default()
693 },
694 );
695 m
696 });
697 schema.additional_properties = Some(AdditionalProperties::Forbid);
698 let instance = json!({});
699 let actual: ValidationResult = validate(&schema, &instance);
700 let expected: ValidationResult = Ok(());
701 assert_eq!(expected, actual);
702 }
703
704 #[test]
705 fn additional_properties_false_only_known_keys_valid() {
706 let mut schema = schema_object_with_required(vec!["a"], {
707 let mut m = BTreeMap::new();
708 m.insert(
709 "a".to_string(),
710 JsonSchema {
711 type_: Some("string".to_string()),
712 ..Default::default()
713 },
714 );
715 m
716 });
717 schema.additional_properties = Some(AdditionalProperties::Forbid);
718 let instance = json!({"a": "x"});
719 let actual: ValidationResult = validate(&schema, &instance);
720 let expected: ValidationResult = Ok(());
721 assert_eq!(expected, actual);
722 }
723
724 #[test]
725 fn additional_properties_schema_valid_when_value_passes() {
726 let sub = JsonSchema {
727 type_: Some("string".to_string()),
728 ..Default::default()
729 };
730 let schema = JsonSchema {
731 type_: Some("object".to_string()),
732 properties: {
733 let mut m = BTreeMap::new();
734 m.insert(
735 "a".to_string(),
736 JsonSchema {
737 type_: Some("string".to_string()),
738 ..Default::default()
739 },
740 );
741 m
742 },
743 additional_properties: Some(AdditionalProperties::Schema(Box::new(sub))),
744 ..Default::default()
745 };
746 let instance = json!({"a": "x", "extra": "allowed"});
747 let actual: ValidationResult = validate(&schema, &instance);
748 let expected: ValidationResult = Ok(());
749 assert_eq!(expected, actual);
750 }
751
752 #[test]
753 fn additional_properties_schema_invalid_when_value_fails() {
754 let sub = JsonSchema {
755 type_: Some("integer".to_string()),
756 ..Default::default()
757 };
758 let schema = JsonSchema {
759 type_: Some("object".to_string()),
760 properties: {
761 let mut m = BTreeMap::new();
762 m.insert(
763 "a".to_string(),
764 JsonSchema {
765 type_: Some("string".to_string()),
766 ..Default::default()
767 },
768 );
769 m
770 },
771 additional_properties: Some(AdditionalProperties::Schema(Box::new(sub))),
772 ..Default::default()
773 };
774 let instance = json!({"a": "x", "extra": "not_an_integer"});
775 let actual: ValidationResult = validate(&schema, &instance);
776 let expected: ValidationResult = Err(vec![ValidationError::ExpectedInteger {
777 instance_path: JsonPointer::root().push("extra"),
778 got: "string".to_string(),
779 }]);
780 assert_eq!(expected, actual);
781 }
782
783 #[test]
784 fn all_of_all_subschemas_pass() {
785 let schema = JsonSchema {
786 all_of: Some(vec![
787 JsonSchema {
788 type_: Some("object".to_string()),
789 properties: {
790 let mut m = BTreeMap::new();
791 m.insert(
792 "a".to_string(),
793 JsonSchema {
794 type_: Some("string".to_string()),
795 ..Default::default()
796 },
797 );
798 m
799 },
800 ..Default::default()
801 },
802 JsonSchema {
803 type_: Some("object".to_string()),
804 properties: {
805 let mut m = BTreeMap::new();
806 m.insert(
807 "b".to_string(),
808 JsonSchema {
809 type_: Some("integer".to_string()),
810 ..Default::default()
811 },
812 );
813 m
814 },
815 ..Default::default()
816 },
817 ]),
818 ..Default::default()
819 };
820 let instance = json!({"a": "ok", "b": 1});
821 let actual: ValidationResult = validate(&schema, &instance);
822 let expected: ValidationResult = Ok(());
823 assert_eq!(expected, actual);
824 }
825
826 #[test]
827 fn all_of_one_subschema_fails_required() {
828 let schema = JsonSchema {
829 all_of: Some(vec![
830 JsonSchema {
831 type_: Some("object".to_string()),
832 properties: BTreeMap::new(),
833 ..Default::default()
834 },
835 JsonSchema {
836 type_: Some("object".to_string()),
837 required: Some(vec!["b".to_string()]),
838 properties: {
839 let mut m = BTreeMap::new();
840 m.insert(
841 "b".to_string(),
842 JsonSchema {
843 type_: Some("integer".to_string()),
844 ..Default::default()
845 },
846 );
847 m
848 },
849 ..Default::default()
850 },
851 ]),
852 ..Default::default()
853 };
854 let instance = json!({"a": "x"});
855 let actual: ValidationResult = validate(&schema, &instance);
856 assert!(matches!(
857 actual,
858 Err(ref e) if e.iter().any(|err| matches!(err, ValidationError::MissingRequired { property, .. } if property == "b"))
859 ));
860 }
861
862 #[test]
863 fn all_of_multiple_subschemas_fail() {
864 let schema = JsonSchema {
865 all_of: Some(vec![
866 JsonSchema {
867 type_: Some("object".to_string()),
868 required: Some(vec!["a".to_string()]),
869 properties: {
870 let mut m = BTreeMap::new();
871 m.insert(
872 "a".to_string(),
873 JsonSchema {
874 type_: Some("string".to_string()),
875 ..Default::default()
876 },
877 );
878 m
879 },
880 ..Default::default()
881 },
882 JsonSchema {
883 type_: Some("object".to_string()),
884 required: Some(vec!["b".to_string()]),
885 properties: {
886 let mut m = BTreeMap::new();
887 m.insert(
888 "b".to_string(),
889 JsonSchema {
890 type_: Some("integer".to_string()),
891 ..Default::default()
892 },
893 );
894 m
895 },
896 ..Default::default()
897 },
898 ]),
899 ..Default::default()
900 };
901 let instance = json!({"a": 1, "b": "x"});
902 let result: ValidationResult = validate(&schema, &instance);
903 let errs = result.unwrap_err();
904 let expected: usize = 2;
905 let actual: usize = errs.len();
906 assert_eq!(expected, actual);
907 }
908
909 #[test]
910 fn all_of_empty_valid() {
911 let schema = JsonSchema {
912 all_of: Some(vec![]),
913 ..Default::default()
914 };
915 let instance = json!({});
916 let actual: ValidationResult = validate(&schema, &instance);
917 let expected: ValidationResult = Ok(());
918 assert_eq!(expected, actual);
919 }
920
921 #[test]
922 fn all_of_enum_in_one_subschema_fails_when_not_in_enum() {
923 let schema = JsonSchema {
924 all_of: Some(vec![
925 JsonSchema {
926 type_: Some("object".to_string()),
927 properties: BTreeMap::new(),
928 ..Default::default()
929 },
930 JsonSchema {
931 type_: Some("object".to_string()),
932 properties: {
933 let mut m = BTreeMap::new();
934 m.insert(
935 "s".to_string(),
936 JsonSchema {
937 enum_values: Some(vec![
938 serde_json::Value::String("a".to_string()),
939 serde_json::Value::String("b".to_string()),
940 ]),
941 ..Default::default()
942 },
943 );
944 m
945 },
946 ..Default::default()
947 },
948 ]),
949 ..Default::default()
950 };
951 let instance = json!({"s": "c"});
952 let actual: ValidationResult = validate(&schema, &instance);
953 assert!(matches!(actual, Err(ref e) if !e.is_empty()));
954 let errs = actual.unwrap_err();
955 assert!(
956 errs.iter()
957 .any(|e| matches!(e, ValidationError::NotInEnum { .. }))
958 );
959 }
960
961 #[test]
962 fn any_of_at_least_one_subschema_passes() {
963 let schema = JsonSchema {
964 any_of: Some(vec![
965 JsonSchema {
966 type_: Some("string".to_string()),
967 ..Default::default()
968 },
969 JsonSchema {
970 type_: Some("integer".to_string()),
971 ..Default::default()
972 },
973 ]),
974 ..Default::default()
975 };
976 let instance_str = json!("hello");
977 let actual_str: ValidationResult = validate(&schema, &instance_str);
978 let expected_str: ValidationResult = Ok(());
979 assert_eq!(expected_str, actual_str);
980 let instance_int = json!(42);
981 let actual_int: ValidationResult = validate(&schema, &instance_int);
982 let expected_int: ValidationResult = Ok(());
983 assert_eq!(expected_int, actual_int);
984 }
985
986 #[test]
987 fn any_of_no_subschema_passes() {
988 let schema = JsonSchema {
989 any_of: Some(vec![
990 JsonSchema {
991 type_: Some("string".to_string()),
992 ..Default::default()
993 },
994 JsonSchema {
995 type_: Some("integer".to_string()),
996 ..Default::default()
997 },
998 ]),
999 ..Default::default()
1000 };
1001 let instance = json!(true);
1002 let actual: ValidationResult = validate(&schema, &instance);
1003 let errs = actual.unwrap_err();
1004 let expected: usize = 1;
1005 let actual_count: usize = errs.len();
1006 assert_eq!(expected, actual_count);
1007 assert!(errs.iter().any(|e| matches!(
1008 e,
1009 ValidationError::NoSubschemaMatched {
1010 subschema_count: 2,
1011 ..
1012 }
1013 )));
1014 }
1015
1016 #[test]
1017 fn any_of_empty_no_subschema_matches() {
1018 let schema = JsonSchema {
1019 any_of: Some(vec![]),
1020 ..Default::default()
1021 };
1022 let instance = json!(1);
1023 let actual: ValidationResult = validate(&schema, &instance);
1024 let errs = actual.unwrap_err();
1025 let expected: usize = 1;
1026 let actual_count: usize = errs.len();
1027 assert_eq!(expected, actual_count);
1028 assert!(errs.iter().any(|e| matches!(
1029 e,
1030 ValidationError::NoSubschemaMatched {
1031 subschema_count: 0,
1032 ..
1033 }
1034 )));
1035 }
1036
1037 #[test]
1038 fn any_of_single_subschema_passes() {
1039 let schema = JsonSchema {
1040 any_of: Some(vec![JsonSchema {
1041 type_: Some("number".to_string()),
1042 ..Default::default()
1043 }]),
1044 ..Default::default()
1045 };
1046 let instance = json!(std::f64::consts::PI);
1047 let actual: ValidationResult = validate(&schema, &instance);
1048 let expected: ValidationResult = Ok(());
1049 assert_eq!(expected, actual);
1050 }
1051
1052 #[test]
1053 fn any_of_single_subschema_fails() {
1054 let schema = JsonSchema {
1055 any_of: Some(vec![JsonSchema {
1056 type_: Some("string".to_string()),
1057 ..Default::default()
1058 }]),
1059 ..Default::default()
1060 };
1061 let instance = json!(42);
1062 let actual: ValidationResult = validate(&schema, &instance);
1063 let errs = actual.unwrap_err();
1064 assert_eq!(1, errs.len());
1065 assert!(errs.iter().any(|e| matches!(
1066 e,
1067 ValidationError::NoSubschemaMatched {
1068 subschema_count: 1,
1069 ..
1070 }
1071 )));
1072 }
1073
1074 #[test]
1075 fn one_of_exactly_one_subschema_passes() {
1076 let schema = JsonSchema {
1077 one_of: Some(vec![
1078 JsonSchema {
1079 type_: Some("string".to_string()),
1080 ..Default::default()
1081 },
1082 JsonSchema {
1083 type_: Some("integer".to_string()),
1084 ..Default::default()
1085 },
1086 ]),
1087 ..Default::default()
1088 };
1089 let instance = json!("hello");
1090 let actual: ValidationResult = validate(&schema, &instance);
1091 let expected: ValidationResult = Ok(());
1092 assert_eq!(expected, actual);
1093 }
1094
1095 #[test]
1096 fn one_of_no_subschema_passes() {
1097 let schema = JsonSchema {
1098 one_of: Some(vec![
1099 JsonSchema {
1100 type_: Some("string".to_string()),
1101 ..Default::default()
1102 },
1103 JsonSchema {
1104 type_: Some("integer".to_string()),
1105 ..Default::default()
1106 },
1107 ]),
1108 ..Default::default()
1109 };
1110 let instance = json!(1.5);
1111 let actual: ValidationResult = validate(&schema, &instance);
1112 let errs = actual.unwrap_err();
1113 assert_eq!(1, errs.len());
1114 assert!(errs.iter().any(|e| matches!(
1115 e,
1116 ValidationError::NoSubschemaMatched {
1117 subschema_count: 2,
1118 ..
1119 }
1120 )));
1121 }
1122
1123 #[test]
1124 fn one_of_multiple_subschemas_pass() {
1125 let schema = JsonSchema {
1126 one_of: Some(vec![
1127 JsonSchema::default(),
1128 JsonSchema {
1129 type_: Some("string".to_string()),
1130 ..Default::default()
1131 },
1132 ]),
1133 ..Default::default()
1134 };
1135 let instance = json!("x");
1136 let actual: ValidationResult = validate(&schema, &instance);
1137 let errs = actual.unwrap_err();
1138 assert_eq!(1, errs.len());
1139 assert!(errs.iter().any(|e| matches!(
1140 e,
1141 ValidationError::MultipleSubschemasMatched {
1142 subschema_count: 2,
1143 match_count: 2,
1144 ..
1145 }
1146 )));
1147 }
1148
1149 #[test]
1150 fn one_of_empty_no_subschema_matches() {
1151 let schema = JsonSchema {
1152 one_of: Some(vec![]),
1153 ..Default::default()
1154 };
1155 let instance = json!(1);
1156 let actual: ValidationResult = validate(&schema, &instance);
1157 let errs = actual.unwrap_err();
1158 assert_eq!(1, errs.len());
1159 assert!(errs.iter().any(|e| matches!(
1160 e,
1161 ValidationError::NoSubschemaMatched {
1162 subschema_count: 0,
1163 ..
1164 }
1165 )));
1166 }
1167
1168 #[test]
1169 fn one_of_single_subschema_passes() {
1170 let schema = JsonSchema {
1171 one_of: Some(vec![JsonSchema {
1172 type_: Some("number".to_string()),
1173 ..Default::default()
1174 }]),
1175 ..Default::default()
1176 };
1177 let instance = json!(std::f64::consts::PI);
1178 let actual: ValidationResult = validate(&schema, &instance);
1179 let expected: ValidationResult = Ok(());
1180 assert_eq!(expected, actual);
1181 }
1182
1183 #[test]
1184 fn one_of_single_subschema_fails() {
1185 let schema = JsonSchema {
1186 one_of: Some(vec![JsonSchema {
1187 type_: Some("string".to_string()),
1188 ..Default::default()
1189 }]),
1190 ..Default::default()
1191 };
1192 let instance = json!(42);
1193 let actual: ValidationResult = validate(&schema, &instance);
1194 let errs = actual.unwrap_err();
1195 assert_eq!(1, errs.len());
1196 assert!(errs.iter().any(|e| matches!(
1197 e,
1198 ValidationError::NoSubschemaMatched {
1199 subschema_count: 1,
1200 ..
1201 }
1202 )));
1203 }
1204
1205 #[test]
1206 fn wrong_type_string_instead_of_object() {
1207 let schema: JsonSchema = JsonSchema {
1208 type_: Some("object".to_string()),
1209 ..Default::default()
1210 };
1211 let instance = json!("not an object");
1212 let actual: ValidationResult = validate(&schema, &instance);
1213 let expected: ValidationResult = Err(vec![ValidationError::ExpectedObject {
1214 instance_path: JsonPointer::root(),
1215 got: "string".to_string(),
1216 }]);
1217 assert_eq!(expected, actual);
1218 }
1219
1220 #[test]
1221 fn schema_with_description_validates_as_before() {
1222 let schema: JsonSchema = JsonSchema {
1223 type_: Some("object".to_string()),
1224 properties: {
1225 let mut m = BTreeMap::new();
1226 m.insert(
1227 "name".to_string(),
1228 JsonSchema {
1229 type_: Some("string".to_string()),
1230 properties: BTreeMap::new(),
1231 additional_properties: None,
1232 required: None,
1233 title: None,
1234 description: Some("User name".to_string()),
1235 comment: None,
1236 enum_values: None,
1237 const_value: None,
1238 items: None,
1239 unique_items: None,
1240 min_items: None,
1241 max_items: None,
1242 minimum: None,
1243 maximum: None,
1244 min_length: None,
1245 max_length: None,
1246 pattern: None,
1247 format: None,
1248 default_value: None,
1249 all_of: None,
1250 any_of: None,
1251 one_of: None,
1252 ..Default::default()
1253 },
1254 );
1255 m
1256 },
1257 additional_properties: None,
1258 required: None,
1259 title: None,
1260 description: Some("Root type".to_string()),
1261 comment: None,
1262 enum_values: None,
1263 const_value: None,
1264 items: None,
1265 unique_items: None,
1266 min_items: None,
1267 max_items: None,
1268 minimum: None,
1269 maximum: None,
1270 min_length: None,
1271 max_length: None,
1272 pattern: None,
1273 format: None,
1274 default_value: None,
1275 all_of: None,
1276 any_of: None,
1277 one_of: None,
1278 ..Default::default()
1279 };
1280 let valid_instance = json!({"name": "Alice"});
1281 let actual_valid: ValidationResult = validate(&schema, &valid_instance);
1282 assert_eq!(Ok(()), actual_valid);
1283 let invalid_instance = json!({"name": 42});
1284 let actual_invalid: ValidationResult = validate(&schema, &invalid_instance);
1285 assert!(actual_invalid.is_err());
1286 }
1287
1288 #[test]
1289 fn schema_with_examples_valid_instance_passes() {
1290 let schema: JsonSchema = serde_json::from_str(
1291 r#"{"type":"object","properties":{"x":{"type":"string"}},"required":["x"],"examples":[{"x":"foo"}]}"#,
1292 )
1293 .expect("parse schema");
1294 let instance = json!({"x": "valid"});
1295 let actual: ValidationResult = validate(&schema, &instance);
1296 let expected: ValidationResult = Ok(());
1297 assert_eq!(expected, actual);
1298 }
1299
1300 #[test]
1301 fn schema_with_deprecated_property_valid_instance_passes() {
1302 let schema: JsonSchema = serde_json::from_str(
1303 r#"{"type":"object","properties":{"legacy":{"type":"string","deprecated":true}}}"#,
1304 )
1305 .expect("parse schema");
1306 let instance = json!({"legacy": "still-valid"});
1307 let actual: ValidationResult = validate(&schema, &instance);
1308 let expected: ValidationResult = Ok(());
1309 assert_eq!(expected, actual);
1310 }
1311
1312 #[test]
1313 fn schema_with_examples_invalid_instance_same_errors_as_without() {
1314 let schema: JsonSchema = serde_json::from_str(
1315 r#"{"type":"object","properties":{"x":{"type":"string"}},"required":["x"],"examples":[{"x":"foo"}]}"#,
1316 )
1317 .expect("parse schema");
1318 let instance = json!({});
1319 let actual: ValidationResult = validate(&schema, &instance);
1320 let expected: ValidationResult = Err(vec![ValidationError::MissingRequired {
1321 instance_path: JsonPointer::root().push("x"),
1322 property: "x".to_string(),
1323 }]);
1324 assert_eq!(expected, actual);
1325 }
1326
1327 #[test]
1328 fn schema_with_comment_validates_same_as_without() {
1329 let schema_without: JsonSchema = JsonSchema {
1330 type_: Some("string".to_string()),
1331 properties: BTreeMap::new(),
1332 additional_properties: None,
1333 required: None,
1334 title: None,
1335 description: None,
1336 comment: None,
1337 enum_values: None,
1338 const_value: None,
1339 items: None,
1340 unique_items: None,
1341 min_items: None,
1342 max_items: None,
1343 minimum: None,
1344 maximum: None,
1345 min_length: None,
1346 max_length: None,
1347 pattern: None,
1348 format: None,
1349 default_value: None,
1350 all_of: None,
1351 any_of: None,
1352 one_of: None,
1353 ..Default::default()
1354 };
1355 let schema_with_comment: JsonSchema = JsonSchema {
1356 type_: Some("string".to_string()),
1357 properties: BTreeMap::new(),
1358 additional_properties: None,
1359 required: None,
1360 title: None,
1361 description: None,
1362 comment: Some("Editor note".to_string()),
1363 enum_values: None,
1364 const_value: None,
1365 items: None,
1366 unique_items: None,
1367 min_items: None,
1368 max_items: None,
1369 minimum: None,
1370 maximum: None,
1371 min_length: None,
1372 max_length: None,
1373 pattern: None,
1374 format: None,
1375 default_value: None,
1376 all_of: None,
1377 any_of: None,
1378 one_of: None,
1379 ..Default::default()
1380 };
1381 let valid_instance = json!("hello");
1382 let invalid_instance = json!(42);
1383 let expected_valid: ValidationResult = validate(&schema_without, &valid_instance);
1384 let actual_valid: ValidationResult = validate(&schema_with_comment, &valid_instance);
1385 assert_eq!(expected_valid, actual_valid);
1386 let expected_invalid: ValidationResult = validate(&schema_without, &invalid_instance);
1387 let actual_invalid: ValidationResult = validate(&schema_with_comment, &invalid_instance);
1388 assert_eq!(expected_invalid, actual_invalid);
1389 }
1390
1391 #[test]
1392 #[expect(clippy::too_many_lines)]
1393 fn schema_with_default_validates_same_as_without() {
1394 let mut properties_without: BTreeMap<String, JsonSchema> = BTreeMap::new();
1395 properties_without.insert(
1396 "opt".to_string(),
1397 JsonSchema {
1398 type_: Some("string".to_string()),
1399 properties: BTreeMap::new(),
1400 additional_properties: None,
1401 required: None,
1402 title: None,
1403 description: None,
1404 comment: None,
1405 enum_values: None,
1406 const_value: None,
1407 items: None,
1408 unique_items: None,
1409 min_items: None,
1410 max_items: None,
1411 minimum: None,
1412 maximum: None,
1413 min_length: None,
1414 max_length: None,
1415 pattern: None,
1416 format: None,
1417 default_value: None,
1418 all_of: None,
1419 any_of: None,
1420 one_of: None,
1421 ..Default::default()
1422 },
1423 );
1424 let mut properties_with_default: BTreeMap<String, JsonSchema> = BTreeMap::new();
1425 properties_with_default.insert(
1426 "opt".to_string(),
1427 JsonSchema {
1428 type_: Some("string".to_string()),
1429 properties: BTreeMap::new(),
1430 additional_properties: None,
1431 required: None,
1432 title: None,
1433 description: None,
1434 comment: None,
1435 enum_values: None,
1436 const_value: None,
1437 items: None,
1438 unique_items: None,
1439 min_items: None,
1440 max_items: None,
1441 minimum: None,
1442 maximum: None,
1443 min_length: None,
1444 max_length: None,
1445 pattern: None,
1446 format: None,
1447 default_value: Some(serde_json::Value::String("defaulted".to_string())),
1448 all_of: None,
1449 any_of: None,
1450 one_of: None,
1451 ..Default::default()
1452 },
1453 );
1454 let schema_without: JsonSchema = JsonSchema {
1455 type_: Some("object".to_string()),
1456 properties: properties_without,
1457 additional_properties: None,
1458 required: None,
1459 title: None,
1460 description: None,
1461 comment: None,
1462 enum_values: None,
1463 const_value: None,
1464 items: None,
1465 unique_items: None,
1466 min_items: None,
1467 max_items: None,
1468 minimum: None,
1469 maximum: None,
1470 min_length: None,
1471 max_length: None,
1472 pattern: None,
1473 format: None,
1474 default_value: None,
1475 all_of: None,
1476 any_of: None,
1477 one_of: None,
1478 ..Default::default()
1479 };
1480 let schema_with_default: JsonSchema = JsonSchema {
1481 type_: Some("object".to_string()),
1482 properties: properties_with_default,
1483 additional_properties: None,
1484 required: None,
1485 title: None,
1486 description: None,
1487 comment: None,
1488 enum_values: None,
1489 const_value: None,
1490 items: None,
1491 unique_items: None,
1492 min_items: None,
1493 max_items: None,
1494 minimum: None,
1495 maximum: None,
1496 min_length: None,
1497 max_length: None,
1498 pattern: None,
1499 format: None,
1500 default_value: None,
1501 all_of: None,
1502 any_of: None,
1503 one_of: None,
1504 ..Default::default()
1505 };
1506 let empty_instance = json!({});
1507 let expected: ValidationResult = validate(&schema_without, &empty_instance);
1508 let actual: ValidationResult = validate(&schema_with_default, &empty_instance);
1509 assert_eq!(expected, actual);
1510 }
1511
1512 #[test]
1513 fn root_type_string_valid_nonempty() {
1514 let schema: JsonSchema = JsonSchema {
1515 type_: Some("string".to_string()),
1516 properties: BTreeMap::new(),
1517 additional_properties: None,
1518 required: None,
1519 title: None,
1520 description: None,
1521 comment: None,
1522 enum_values: None,
1523 const_value: None,
1524 items: None,
1525 unique_items: None,
1526 min_items: None,
1527 max_items: None,
1528 minimum: None,
1529 maximum: None,
1530 min_length: None,
1531 max_length: None,
1532 pattern: None,
1533 format: None,
1534 default_value: None,
1535 all_of: None,
1536 any_of: None,
1537 one_of: None,
1538 ..Default::default()
1539 };
1540 let instance = json!("ok");
1541 let actual: ValidationResult = validate(&schema, &instance);
1542 let expected: ValidationResult = Ok(());
1543 assert_eq!(expected, actual);
1544 }
1545
1546 #[test]
1547 fn root_type_string_valid_empty() {
1548 let schema: JsonSchema = JsonSchema {
1549 type_: Some("string".to_string()),
1550 properties: BTreeMap::new(),
1551 additional_properties: None,
1552 required: None,
1553 title: None,
1554 description: None,
1555 comment: None,
1556 enum_values: None,
1557 const_value: None,
1558 items: None,
1559 unique_items: None,
1560 min_items: None,
1561 max_items: None,
1562 minimum: None,
1563 maximum: None,
1564 min_length: None,
1565 max_length: None,
1566 pattern: None,
1567 format: None,
1568 default_value: None,
1569 all_of: None,
1570 any_of: None,
1571 one_of: None,
1572 ..Default::default()
1573 };
1574 let instance = json!("");
1575 let actual: ValidationResult = validate(&schema, &instance);
1576 let expected: ValidationResult = Ok(());
1577 assert_eq!(expected, actual);
1578 }
1579
1580 #[test]
1581 fn wrong_type_object_instead_of_string() {
1582 let schema: JsonSchema = JsonSchema {
1583 type_: Some("string".to_string()),
1584 properties: BTreeMap::new(),
1585 additional_properties: None,
1586 required: None,
1587 title: None,
1588 description: None,
1589 comment: None,
1590 enum_values: None,
1591 const_value: None,
1592 items: None,
1593 unique_items: None,
1594 min_items: None,
1595 max_items: None,
1596 minimum: None,
1597 maximum: None,
1598 min_length: None,
1599 max_length: None,
1600 pattern: None,
1601 format: None,
1602 default_value: None,
1603 all_of: None,
1604 any_of: None,
1605 one_of: None,
1606 ..Default::default()
1607 };
1608 let instance = json!({"x": 1});
1609 let actual: ValidationResult = validate(&schema, &instance);
1610 let expected: ValidationResult = Err(vec![ValidationError::ExpectedString {
1611 instance_path: JsonPointer::root(),
1612 got: "object".to_string(),
1613 }]);
1614 assert_eq!(expected, actual);
1615 }
1616
1617 #[test]
1618 fn wrong_type_number_instead_of_string() {
1619 let schema: JsonSchema = JsonSchema {
1620 type_: Some("string".to_string()),
1621 properties: BTreeMap::new(),
1622 additional_properties: None,
1623 required: None,
1624 title: None,
1625 description: None,
1626 comment: None,
1627 enum_values: None,
1628 const_value: None,
1629 items: None,
1630 unique_items: None,
1631 min_items: None,
1632 max_items: None,
1633 minimum: None,
1634 maximum: None,
1635 min_length: None,
1636 max_length: None,
1637 pattern: None,
1638 format: None,
1639 default_value: None,
1640 all_of: None,
1641 any_of: None,
1642 one_of: None,
1643 ..Default::default()
1644 };
1645 let instance = json!(42);
1646 let actual: ValidationResult = validate(&schema, &instance);
1647 let expected: ValidationResult = Err(vec![ValidationError::ExpectedString {
1648 instance_path: JsonPointer::root(),
1649 got: "number".to_string(),
1650 }]);
1651 assert_eq!(expected, actual);
1652 }
1653
1654 #[test]
1655 fn wrong_type_null_instead_of_string() {
1656 let schema: JsonSchema = JsonSchema {
1657 type_: Some("string".to_string()),
1658 properties: BTreeMap::new(),
1659 additional_properties: None,
1660 required: None,
1661 title: None,
1662 description: None,
1663 comment: None,
1664 enum_values: None,
1665 const_value: None,
1666 items: None,
1667 unique_items: None,
1668 min_items: None,
1669 max_items: None,
1670 minimum: None,
1671 maximum: None,
1672 min_length: None,
1673 max_length: None,
1674 pattern: None,
1675 format: None,
1676 default_value: None,
1677 all_of: None,
1678 any_of: None,
1679 one_of: None,
1680 ..Default::default()
1681 };
1682 let instance = json!(null);
1683 let actual: ValidationResult = validate(&schema, &instance);
1684 let expected: ValidationResult = Err(vec![ValidationError::ExpectedString {
1685 instance_path: JsonPointer::root(),
1686 got: "null".to_string(),
1687 }]);
1688 assert_eq!(expected, actual);
1689 }
1690
1691 #[test]
1692 fn wrong_type_boolean_instead_of_string() {
1693 let schema: JsonSchema = JsonSchema {
1694 type_: Some("string".to_string()),
1695 properties: BTreeMap::new(),
1696 additional_properties: None,
1697 required: None,
1698 title: None,
1699 description: None,
1700 comment: None,
1701 enum_values: None,
1702 const_value: None,
1703 items: None,
1704 unique_items: None,
1705 min_items: None,
1706 max_items: None,
1707 minimum: None,
1708 maximum: None,
1709 min_length: None,
1710 max_length: None,
1711 pattern: None,
1712 format: None,
1713 default_value: None,
1714 all_of: None,
1715 any_of: None,
1716 one_of: None,
1717 ..Default::default()
1718 };
1719 let instance = json!(true);
1720 let actual: ValidationResult = validate(&schema, &instance);
1721 let expected: ValidationResult = Err(vec![ValidationError::ExpectedString {
1722 instance_path: JsonPointer::root(),
1723 got: "boolean".to_string(),
1724 }]);
1725 assert_eq!(expected, actual);
1726 }
1727
1728 #[test]
1729 fn root_type_boolean_valid_true() {
1730 let schema: JsonSchema = JsonSchema {
1731 type_: Some("boolean".to_string()),
1732 ..Default::default()
1733 };
1734 let instance = json!(true);
1735 let actual: ValidationResult = validate(&schema, &instance);
1736 let expected: ValidationResult = Ok(());
1737 assert_eq!(expected, actual);
1738 }
1739
1740 #[test]
1741 fn root_type_boolean_valid_false() {
1742 let schema: JsonSchema = JsonSchema {
1743 type_: Some("boolean".to_string()),
1744 ..Default::default()
1745 };
1746 let instance = json!(false);
1747 let actual: ValidationResult = validate(&schema, &instance);
1748 let expected: ValidationResult = Ok(());
1749 assert_eq!(expected, actual);
1750 }
1751
1752 #[test]
1753 fn wrong_type_string_instead_of_boolean() {
1754 let schema: JsonSchema = JsonSchema {
1755 type_: Some("boolean".to_string()),
1756 ..Default::default()
1757 };
1758 let instance = json!("hello");
1759 let actual: ValidationResult = validate(&schema, &instance);
1760 let expected: ValidationResult = Err(vec![ValidationError::ExpectedBoolean {
1761 instance_path: JsonPointer::root(),
1762 got: "string".to_string(),
1763 }]);
1764 assert_eq!(expected, actual);
1765 }
1766
1767 #[test]
1768 fn wrong_type_array_instead_of_string() {
1769 let schema: JsonSchema = JsonSchema {
1770 type_: Some("string".to_string()),
1771 properties: BTreeMap::new(),
1772 additional_properties: None,
1773 required: None,
1774 title: None,
1775 description: None,
1776 comment: None,
1777 enum_values: None,
1778 const_value: None,
1779 items: None,
1780 unique_items: None,
1781 min_items: None,
1782 max_items: None,
1783 minimum: None,
1784 maximum: None,
1785 min_length: None,
1786 max_length: None,
1787 pattern: None,
1788 format: None,
1789 default_value: None,
1790 all_of: None,
1791 any_of: None,
1792 one_of: None,
1793 ..Default::default()
1794 };
1795 let instance = json!([1, 2]);
1796 let actual: ValidationResult = validate(&schema, &instance);
1797 let expected: ValidationResult = Err(vec![ValidationError::ExpectedString {
1798 instance_path: JsonPointer::root(),
1799 got: "array".to_string(),
1800 }]);
1801 assert_eq!(expected, actual);
1802 }
1803
1804 #[test]
1805 fn enum_valid_instance_in_allowed() {
1806 let schema: JsonSchema = JsonSchema {
1807 enum_values: Some(vec![
1808 serde_json::Value::String("open".to_string()),
1809 serde_json::Value::String("closed".to_string()),
1810 ]),
1811 ..Default::default()
1812 };
1813 let instance = json!("open");
1814 let actual: ValidationResult = validate(&schema, &instance);
1815 let expected: ValidationResult = Ok(());
1816 assert_eq!(expected, actual);
1817 }
1818
1819 #[test]
1820 fn enum_invalid_instance_not_in_allowed() {
1821 let schema: JsonSchema = JsonSchema {
1822 enum_values: Some(vec![
1823 serde_json::Value::String("open".to_string()),
1824 serde_json::Value::String("closed".to_string()),
1825 ]),
1826 ..Default::default()
1827 };
1828 let instance = json!("pending");
1829 let actual: ValidationResult = validate(&schema, &instance);
1830 let expected: ValidationResult = Err(vec![ValidationError::NotInEnum {
1831 instance_path: JsonPointer::root(),
1832 invalid_value: "\"pending\"".to_string(),
1833 allowed: vec!["\"open\"".to_string(), "\"closed\"".to_string()],
1834 }]);
1835 assert_eq!(expected, actual);
1836 }
1837
1838 #[test]
1839 fn enum_with_type_string_valid() {
1840 let schema: JsonSchema = JsonSchema {
1841 type_: Some("string".to_string()),
1842 enum_values: Some(vec![
1843 serde_json::Value::String("a".to_string()),
1844 serde_json::Value::String("b".to_string()),
1845 ]),
1846 ..Default::default()
1847 };
1848 let instance = json!("a");
1849 let actual: ValidationResult = validate(&schema, &instance);
1850 let expected: ValidationResult = Ok(());
1851 assert_eq!(expected, actual);
1852 }
1853
1854 #[test]
1855 fn enum_with_type_string_invalid_not_in_enum() {
1856 let schema: JsonSchema = JsonSchema {
1857 type_: Some("string".to_string()),
1858 enum_values: Some(vec![
1859 serde_json::Value::String("a".to_string()),
1860 serde_json::Value::String("b".to_string()),
1861 ]),
1862 ..Default::default()
1863 };
1864 let instance = json!("c");
1865 let actual: ValidationResult = validate(&schema, &instance);
1866 let expected: ValidationResult = Err(vec![ValidationError::NotInEnum {
1867 instance_path: JsonPointer::root(),
1868 invalid_value: "\"c\"".to_string(),
1869 allowed: vec!["\"a\"".to_string(), "\"b\"".to_string()],
1870 }]);
1871 assert_eq!(expected, actual);
1872 }
1873
1874 #[test]
1875 fn const_valid_string() {
1876 let schema: JsonSchema = JsonSchema {
1877 const_value: Some(serde_json::Value::String("ok".to_string())),
1878 ..Default::default()
1879 };
1880 let instance = json!("ok");
1881 let actual: ValidationResult = validate(&schema, &instance);
1882 let expected: ValidationResult = Ok(());
1883 assert_eq!(expected, actual);
1884 }
1885
1886 #[test]
1887 fn const_invalid_string() {
1888 let schema: JsonSchema = JsonSchema {
1889 const_value: Some(serde_json::Value::String("expected".to_string())),
1890 ..Default::default()
1891 };
1892 let instance = json!("actual");
1893 let actual: ValidationResult = validate(&schema, &instance);
1894 let errs = actual.as_ref().expect_err("expected NotConst error");
1895 assert_eq!(errs.len(), 1);
1896 assert!(matches!(
1897 &errs[0],
1898 ValidationError::NotConst {
1899 expected: e,
1900 actual: a,
1901 ..
1902 } if e == "\"expected\"" && a == "\"actual\""
1903 ));
1904 }
1905
1906 #[test]
1907 fn const_valid_number() {
1908 let schema: JsonSchema = JsonSchema {
1909 const_value: Some(serde_json::Value::Number(42_i64.into())),
1910 ..Default::default()
1911 };
1912 let instance = json!(42);
1913 let actual: ValidationResult = validate(&schema, &instance);
1914 let expected: ValidationResult = Ok(());
1915 assert_eq!(expected, actual);
1916 }
1917
1918 #[test]
1919 fn const_with_enum_instance_equals_const_valid() {
1920 let schema: JsonSchema = JsonSchema {
1921 type_: None,
1922 properties: BTreeMap::new(),
1923 additional_properties: None,
1924 required: None,
1925 title: None,
1926 description: None,
1927 comment: None,
1928 enum_values: Some(vec![
1929 serde_json::Value::String("a".to_string()),
1930 serde_json::Value::String("b".to_string()),
1931 ]),
1932 const_value: Some(serde_json::Value::String("a".to_string())),
1933 items: None,
1934 unique_items: None,
1935 min_items: None,
1936 max_items: None,
1937 minimum: None,
1938 maximum: None,
1939 min_length: None,
1940 max_length: None,
1941 pattern: None,
1942 format: None,
1943 default_value: None,
1944 all_of: None,
1945 any_of: None,
1946 one_of: None,
1947 ..Default::default()
1948 };
1949 let instance = json!("a");
1950 let actual: ValidationResult = validate(&schema, &instance);
1951 let expected: ValidationResult = Ok(());
1952 assert_eq!(expected, actual);
1953 }
1954
1955 #[test]
1956 fn const_with_enum_instance_not_const_not_const_error() {
1957 let schema: JsonSchema = JsonSchema {
1958 type_: None,
1959 properties: BTreeMap::new(),
1960 additional_properties: None,
1961 required: None,
1962 title: None,
1963 description: None,
1964 comment: None,
1965 enum_values: Some(vec![
1966 serde_json::Value::String("a".to_string()),
1967 serde_json::Value::String("b".to_string()),
1968 ]),
1969 const_value: Some(serde_json::Value::String("a".to_string())),
1970 items: None,
1971 unique_items: None,
1972 min_items: None,
1973 max_items: None,
1974 minimum: None,
1975 maximum: None,
1976 min_length: None,
1977 max_length: None,
1978 pattern: None,
1979 format: None,
1980 default_value: None,
1981 all_of: None,
1982 any_of: None,
1983 one_of: None,
1984 ..Default::default()
1985 };
1986 let instance = json!("b");
1987 let actual: ValidationResult = validate(&schema, &instance);
1988 let errs = actual.as_ref().expect_err("expected NotConst");
1989 assert_eq!(errs.len(), 1);
1990 assert!(matches!(&errs[0], ValidationError::NotConst { .. }));
1991 }
1992
1993 #[test]
1994 fn validation_error_not_const_display() {
1995 let err: ValidationError = ValidationError::NotConst {
1996 instance_path: JsonPointer::root(),
1997 expected: "\"foo\"".to_string(),
1998 actual: "\"bar\"".to_string(),
1999 };
2000 let expected: String =
2001 "root: value \"bar\" does not match const (expected: \"foo\")".to_string();
2002 let actual: String = err.to_string();
2003 assert_eq!(expected, actual);
2004 }
2005
2006 #[test]
2007 fn root_type_integer_valid_42() {
2008 let schema: JsonSchema = JsonSchema {
2009 type_: Some("integer".to_string()),
2010 properties: BTreeMap::new(),
2011 additional_properties: None,
2012 required: None,
2013 title: None,
2014 description: None,
2015 comment: None,
2016 enum_values: None,
2017 const_value: None,
2018 items: None,
2019 unique_items: None,
2020 min_items: None,
2021 max_items: None,
2022 minimum: None,
2023 maximum: None,
2024 min_length: None,
2025 max_length: None,
2026 pattern: None,
2027 format: None,
2028 default_value: None,
2029 all_of: None,
2030 any_of: None,
2031 one_of: None,
2032 ..Default::default()
2033 };
2034 let instance = json!(42);
2035 let actual: ValidationResult = validate(&schema, &instance);
2036 let expected: ValidationResult = Ok(());
2037 assert_eq!(expected, actual);
2038 }
2039
2040 #[test]
2041 fn root_type_integer_valid_0() {
2042 let schema: JsonSchema = JsonSchema {
2043 type_: Some("integer".to_string()),
2044 properties: BTreeMap::new(),
2045 additional_properties: None,
2046 required: None,
2047 title: None,
2048 description: None,
2049 comment: None,
2050 enum_values: None,
2051 const_value: None,
2052 items: None,
2053 unique_items: None,
2054 min_items: None,
2055 max_items: None,
2056 minimum: None,
2057 maximum: None,
2058 min_length: None,
2059 max_length: None,
2060 pattern: None,
2061 format: None,
2062 default_value: None,
2063 all_of: None,
2064 any_of: None,
2065 one_of: None,
2066 ..Default::default()
2067 };
2068 let instance = json!(0);
2069 let actual: ValidationResult = validate(&schema, &instance);
2070 let expected: ValidationResult = Ok(());
2071 assert_eq!(expected, actual);
2072 }
2073
2074 #[test]
2075 fn root_type_integer_valid_negative() {
2076 let schema: JsonSchema = JsonSchema {
2077 type_: Some("integer".to_string()),
2078 properties: BTreeMap::new(),
2079 additional_properties: None,
2080 required: None,
2081 title: None,
2082 description: None,
2083 comment: None,
2084 enum_values: None,
2085 const_value: None,
2086 items: None,
2087 unique_items: None,
2088 min_items: None,
2089 max_items: None,
2090 minimum: None,
2091 maximum: None,
2092 min_length: None,
2093 max_length: None,
2094 pattern: None,
2095 format: None,
2096 default_value: None,
2097 all_of: None,
2098 any_of: None,
2099 one_of: None,
2100 ..Default::default()
2101 };
2102 let instance = json!(-1);
2103 let actual: ValidationResult = validate(&schema, &instance);
2104 let expected: ValidationResult = Ok(());
2105 assert_eq!(expected, actual);
2106 }
2107
2108 #[test]
2109 fn root_type_integer_invalid_float() {
2110 let schema: JsonSchema = JsonSchema {
2111 type_: Some("integer".to_string()),
2112 properties: BTreeMap::new(),
2113 additional_properties: None,
2114 required: None,
2115 title: None,
2116 description: None,
2117 comment: None,
2118 enum_values: None,
2119 const_value: None,
2120 items: None,
2121 unique_items: None,
2122 min_items: None,
2123 max_items: None,
2124 minimum: None,
2125 maximum: None,
2126 min_length: None,
2127 max_length: None,
2128 pattern: None,
2129 format: None,
2130 default_value: None,
2131 all_of: None,
2132 any_of: None,
2133 one_of: None,
2134 ..Default::default()
2135 };
2136 let instance = json!(2.5);
2137 let actual: ValidationResult = validate(&schema, &instance);
2138 let expected: ValidationResult = Err(vec![ValidationError::ExpectedInteger {
2139 instance_path: JsonPointer::root(),
2140 got: "number".to_string(),
2141 }]);
2142 assert_eq!(expected, actual);
2143 }
2144
2145 #[test]
2146 fn root_type_integer_invalid_string() {
2147 let schema: JsonSchema = JsonSchema {
2148 type_: Some("integer".to_string()),
2149 properties: BTreeMap::new(),
2150 additional_properties: None,
2151 required: None,
2152 title: None,
2153 description: None,
2154 comment: None,
2155 enum_values: None,
2156 const_value: None,
2157 items: None,
2158 unique_items: None,
2159 min_items: None,
2160 max_items: None,
2161 minimum: None,
2162 maximum: None,
2163 min_length: None,
2164 max_length: None,
2165 pattern: None,
2166 format: None,
2167 default_value: None,
2168 all_of: None,
2169 any_of: None,
2170 one_of: None,
2171 ..Default::default()
2172 };
2173 let instance = json!("42");
2174 let actual: ValidationResult = validate(&schema, &instance);
2175 let expected: ValidationResult = Err(vec![ValidationError::ExpectedInteger {
2176 instance_path: JsonPointer::root(),
2177 got: "string".to_string(),
2178 }]);
2179 assert_eq!(expected, actual);
2180 }
2181
2182 #[test]
2183 fn root_type_integer_invalid_null() {
2184 let schema: JsonSchema = JsonSchema {
2185 type_: Some("integer".to_string()),
2186 properties: BTreeMap::new(),
2187 additional_properties: None,
2188 required: None,
2189 title: None,
2190 description: None,
2191 comment: None,
2192 enum_values: None,
2193 const_value: None,
2194 items: None,
2195 unique_items: None,
2196 min_items: None,
2197 max_items: None,
2198 minimum: None,
2199 maximum: None,
2200 min_length: None,
2201 max_length: None,
2202 pattern: None,
2203 format: None,
2204 default_value: None,
2205 all_of: None,
2206 any_of: None,
2207 one_of: None,
2208 ..Default::default()
2209 };
2210 let instance = json!(null);
2211 let actual: ValidationResult = validate(&schema, &instance);
2212 let expected: ValidationResult = Err(vec![ValidationError::ExpectedInteger {
2213 instance_path: JsonPointer::root(),
2214 got: "null".to_string(),
2215 }]);
2216 assert_eq!(expected, actual);
2217 }
2218
2219 #[test]
2220 fn root_type_integer_invalid_object() {
2221 let schema: JsonSchema = JsonSchema {
2222 type_: Some("integer".to_string()),
2223 properties: BTreeMap::new(),
2224 additional_properties: None,
2225 required: None,
2226 title: None,
2227 description: None,
2228 comment: None,
2229 enum_values: None,
2230 const_value: None,
2231 items: None,
2232 unique_items: None,
2233 min_items: None,
2234 max_items: None,
2235 minimum: None,
2236 maximum: None,
2237 min_length: None,
2238 max_length: None,
2239 pattern: None,
2240 format: None,
2241 default_value: None,
2242 all_of: None,
2243 any_of: None,
2244 one_of: None,
2245 ..Default::default()
2246 };
2247 let instance = json!({"x": 1});
2248 let actual: ValidationResult = validate(&schema, &instance);
2249 let expected: ValidationResult = Err(vec![ValidationError::ExpectedInteger {
2250 instance_path: JsonPointer::root(),
2251 got: "object".to_string(),
2252 }]);
2253 assert_eq!(expected, actual);
2254 }
2255
2256 #[test]
2257 fn root_type_integer_invalid_array() {
2258 let schema: JsonSchema = JsonSchema {
2259 type_: Some("integer".to_string()),
2260 properties: BTreeMap::new(),
2261 additional_properties: None,
2262 required: None,
2263 title: None,
2264 description: None,
2265 comment: None,
2266 enum_values: None,
2267 const_value: None,
2268 items: None,
2269 unique_items: None,
2270 min_items: None,
2271 max_items: None,
2272 minimum: None,
2273 maximum: None,
2274 min_length: None,
2275 max_length: None,
2276 pattern: None,
2277 format: None,
2278 default_value: None,
2279 all_of: None,
2280 any_of: None,
2281 one_of: None,
2282 ..Default::default()
2283 };
2284 let instance = json!([1, 2]);
2285 let actual: ValidationResult = validate(&schema, &instance);
2286 let expected: ValidationResult = Err(vec![ValidationError::ExpectedInteger {
2287 instance_path: JsonPointer::root(),
2288 got: "array".to_string(),
2289 }]);
2290 assert_eq!(expected, actual);
2291 }
2292
2293 #[test]
2294 fn root_type_integer_invalid_bool() {
2295 let schema: JsonSchema = JsonSchema {
2296 type_: Some("integer".to_string()),
2297 properties: BTreeMap::new(),
2298 additional_properties: None,
2299 required: None,
2300 title: None,
2301 description: None,
2302 comment: None,
2303 enum_values: None,
2304 const_value: None,
2305 items: None,
2306 unique_items: None,
2307 min_items: None,
2308 max_items: None,
2309 minimum: None,
2310 maximum: None,
2311 min_length: None,
2312 max_length: None,
2313 pattern: None,
2314 format: None,
2315 default_value: None,
2316 all_of: None,
2317 any_of: None,
2318 one_of: None,
2319 ..Default::default()
2320 };
2321 let instance = json!(true);
2322 let actual: ValidationResult = validate(&schema, &instance);
2323 let expected: ValidationResult = Err(vec![ValidationError::ExpectedInteger {
2324 instance_path: JsonPointer::root(),
2325 got: "boolean".to_string(),
2326 }]);
2327 assert_eq!(expected, actual);
2328 }
2329
2330 #[test]
2331 fn nested_property_type_integer_valid() {
2332 let schema = schema_object_with_required(vec!["count"], {
2333 let mut m = BTreeMap::new();
2334 m.insert(
2335 "count".to_string(),
2336 JsonSchema {
2337 type_: Some("integer".to_string()),
2338 ..Default::default()
2339 },
2340 );
2341 m
2342 });
2343 let instance = json!({"count": 10});
2344 let actual: ValidationResult = validate(&schema, &instance);
2345 let expected: ValidationResult = Ok(());
2346 assert_eq!(expected, actual);
2347 }
2348
2349 #[test]
2350 fn nested_property_type_integer_invalid_float() {
2351 let schema = schema_object_with_required(vec!["count"], {
2352 let mut m = BTreeMap::new();
2353 m.insert(
2354 "count".to_string(),
2355 JsonSchema {
2356 type_: Some("integer".to_string()),
2357 ..Default::default()
2358 },
2359 );
2360 m
2361 });
2362 let instance = json!({"count": 2.5});
2363 let actual: ValidationResult = validate(&schema, &instance);
2364 let expected: ValidationResult = Err(vec![ValidationError::ExpectedInteger {
2365 instance_path: JsonPointer::root().push("count"),
2366 got: "number".to_string(),
2367 }]);
2368 assert_eq!(expected, actual);
2369 }
2370
2371 #[test]
2372 fn nested_required_integer_missing() {
2373 let schema = schema_object_with_required(vec!["count"], {
2374 let mut m = BTreeMap::new();
2375 m.insert(
2376 "count".to_string(),
2377 JsonSchema {
2378 type_: Some("integer".to_string()),
2379 ..Default::default()
2380 },
2381 );
2382 m
2383 });
2384 let instance = json!({});
2385 let actual: ValidationResult = validate(&schema, &instance);
2386 let expected: ValidationResult = Err(vec![ValidationError::MissingRequired {
2387 instance_path: JsonPointer::root().push("count"),
2388 property: "count".to_string(),
2389 }]);
2390 assert_eq!(expected, actual);
2391 }
2392
2393 #[test]
2394 fn root_type_number_valid_float() {
2395 let schema: JsonSchema = JsonSchema {
2396 type_: Some("number".to_string()),
2397 properties: BTreeMap::new(),
2398 additional_properties: None,
2399 required: None,
2400 title: None,
2401 description: None,
2402 comment: None,
2403 enum_values: None,
2404 const_value: None,
2405 items: None,
2406 unique_items: None,
2407 min_items: None,
2408 max_items: None,
2409 minimum: None,
2410 maximum: None,
2411 min_length: None,
2412 max_length: None,
2413 pattern: None,
2414 format: None,
2415 default_value: None,
2416 all_of: None,
2417 any_of: None,
2418 one_of: None,
2419 ..Default::default()
2420 };
2421 let instance = json!(2.5);
2422 let actual: ValidationResult = validate(&schema, &instance);
2423 let expected: ValidationResult = Ok(());
2424 assert_eq!(expected, actual);
2425 }
2426
2427 #[test]
2428 fn root_type_number_valid_integer() {
2429 let schema: JsonSchema = JsonSchema {
2430 type_: Some("number".to_string()),
2431 properties: BTreeMap::new(),
2432 additional_properties: None,
2433 required: None,
2434 title: None,
2435 description: None,
2436 comment: None,
2437 enum_values: None,
2438 const_value: None,
2439 items: None,
2440 unique_items: None,
2441 min_items: None,
2442 max_items: None,
2443 minimum: None,
2444 maximum: None,
2445 min_length: None,
2446 max_length: None,
2447 pattern: None,
2448 format: None,
2449 default_value: None,
2450 all_of: None,
2451 any_of: None,
2452 one_of: None,
2453 ..Default::default()
2454 };
2455 let instance = json!(42);
2456 let actual: ValidationResult = validate(&schema, &instance);
2457 let expected: ValidationResult = Ok(());
2458 assert_eq!(expected, actual);
2459 }
2460
2461 #[test]
2462 fn root_type_number_invalid_string() {
2463 let schema: JsonSchema = JsonSchema {
2464 type_: Some("number".to_string()),
2465 properties: BTreeMap::new(),
2466 additional_properties: None,
2467 required: None,
2468 title: None,
2469 description: None,
2470 comment: None,
2471 enum_values: None,
2472 const_value: None,
2473 items: None,
2474 unique_items: None,
2475 min_items: None,
2476 max_items: None,
2477 minimum: None,
2478 maximum: None,
2479 min_length: None,
2480 max_length: None,
2481 pattern: None,
2482 format: None,
2483 default_value: None,
2484 all_of: None,
2485 any_of: None,
2486 one_of: None,
2487 ..Default::default()
2488 };
2489 let instance = json!("3.14");
2490 let actual: ValidationResult = validate(&schema, &instance);
2491 let expected: ValidationResult = Err(vec![ValidationError::ExpectedNumber {
2492 instance_path: JsonPointer::root(),
2493 got: "string".to_string(),
2494 }]);
2495 assert_eq!(expected, actual);
2496 }
2497
2498 #[test]
2499 fn root_type_number_invalid_null() {
2500 let schema: JsonSchema = JsonSchema {
2501 type_: Some("number".to_string()),
2502 properties: BTreeMap::new(),
2503 additional_properties: None,
2504 required: None,
2505 title: None,
2506 description: None,
2507 comment: None,
2508 enum_values: None,
2509 const_value: None,
2510 items: None,
2511 unique_items: None,
2512 min_items: None,
2513 max_items: None,
2514 minimum: None,
2515 maximum: None,
2516 min_length: None,
2517 max_length: None,
2518 pattern: None,
2519 format: None,
2520 default_value: None,
2521 all_of: None,
2522 any_of: None,
2523 one_of: None,
2524 ..Default::default()
2525 };
2526 let instance = json!(null);
2527 let actual: ValidationResult = validate(&schema, &instance);
2528 let expected: ValidationResult = Err(vec![ValidationError::ExpectedNumber {
2529 instance_path: JsonPointer::root(),
2530 got: "null".to_string(),
2531 }]);
2532 assert_eq!(expected, actual);
2533 }
2534
2535 #[test]
2536 fn root_type_number_invalid_object() {
2537 let schema: JsonSchema = JsonSchema {
2538 type_: Some("number".to_string()),
2539 properties: BTreeMap::new(),
2540 additional_properties: None,
2541 required: None,
2542 title: None,
2543 description: None,
2544 comment: None,
2545 enum_values: None,
2546 const_value: None,
2547 items: None,
2548 unique_items: None,
2549 min_items: None,
2550 max_items: None,
2551 minimum: None,
2552 maximum: None,
2553 min_length: None,
2554 max_length: None,
2555 pattern: None,
2556 format: None,
2557 default_value: None,
2558 all_of: None,
2559 any_of: None,
2560 one_of: None,
2561 ..Default::default()
2562 };
2563 let instance = json!({});
2564 let actual: ValidationResult = validate(&schema, &instance);
2565 let expected: ValidationResult = Err(vec![ValidationError::ExpectedNumber {
2566 instance_path: JsonPointer::root(),
2567 got: "object".to_string(),
2568 }]);
2569 assert_eq!(expected, actual);
2570 }
2571
2572 #[test]
2573 fn root_type_number_invalid_array() {
2574 let schema: JsonSchema = JsonSchema {
2575 type_: Some("number".to_string()),
2576 properties: BTreeMap::new(),
2577 additional_properties: None,
2578 required: None,
2579 title: None,
2580 description: None,
2581 comment: None,
2582 enum_values: None,
2583 const_value: None,
2584 items: None,
2585 unique_items: None,
2586 min_items: None,
2587 max_items: None,
2588 minimum: None,
2589 maximum: None,
2590 min_length: None,
2591 max_length: None,
2592 pattern: None,
2593 format: None,
2594 default_value: None,
2595 all_of: None,
2596 any_of: None,
2597 one_of: None,
2598 ..Default::default()
2599 };
2600 let instance = json!([1.0]);
2601 let actual: ValidationResult = validate(&schema, &instance);
2602 let expected: ValidationResult = Err(vec![ValidationError::ExpectedNumber {
2603 instance_path: JsonPointer::root(),
2604 got: "array".to_string(),
2605 }]);
2606 assert_eq!(expected, actual);
2607 }
2608
2609 #[test]
2610 fn root_type_number_invalid_bool() {
2611 let schema: JsonSchema = JsonSchema {
2612 type_: Some("number".to_string()),
2613 properties: BTreeMap::new(),
2614 additional_properties: None,
2615 required: None,
2616 title: None,
2617 description: None,
2618 comment: None,
2619 enum_values: None,
2620 const_value: None,
2621 items: None,
2622 unique_items: None,
2623 min_items: None,
2624 max_items: None,
2625 minimum: None,
2626 maximum: None,
2627 min_length: None,
2628 max_length: None,
2629 pattern: None,
2630 format: None,
2631 default_value: None,
2632 all_of: None,
2633 any_of: None,
2634 one_of: None,
2635 ..Default::default()
2636 };
2637 let instance = json!(true);
2638 let actual: ValidationResult = validate(&schema, &instance);
2639 let expected: ValidationResult = Err(vec![ValidationError::ExpectedNumber {
2640 instance_path: JsonPointer::root(),
2641 got: "boolean".to_string(),
2642 }]);
2643 assert_eq!(expected, actual);
2644 }
2645
2646 #[test]
2647 fn integer_with_minimum_maximum_valid_in_range() {
2648 let schema: JsonSchema = JsonSchema {
2649 type_: Some("integer".to_string()),
2650 properties: BTreeMap::new(),
2651 additional_properties: None,
2652 required: None,
2653 title: None,
2654 description: None,
2655 comment: None,
2656 enum_values: None,
2657 const_value: None,
2658 items: None,
2659 unique_items: None,
2660 min_items: None,
2661 max_items: None,
2662 minimum: Some(0.0),
2663 maximum: Some(255.0),
2664 min_length: None,
2665 max_length: None,
2666 pattern: None,
2667 format: None,
2668 default_value: None,
2669 all_of: None,
2670 any_of: None,
2671 one_of: None,
2672 ..Default::default()
2673 };
2674 let instance = json!(100);
2675 let actual: ValidationResult = validate(&schema, &instance);
2676 let expected: ValidationResult = Ok(());
2677 assert_eq!(expected, actual);
2678 }
2679
2680 #[test]
2681 fn integer_below_minimum() {
2682 let schema: JsonSchema = JsonSchema {
2683 type_: Some("integer".to_string()),
2684 properties: BTreeMap::new(),
2685 additional_properties: None,
2686 required: None,
2687 title: None,
2688 description: None,
2689 comment: None,
2690 enum_values: None,
2691 const_value: None,
2692 items: None,
2693 unique_items: None,
2694 min_items: None,
2695 max_items: None,
2696 minimum: Some(10.0),
2697 maximum: Some(100.0),
2698 min_length: None,
2699 max_length: None,
2700 pattern: None,
2701 format: None,
2702 default_value: None,
2703 all_of: None,
2704 any_of: None,
2705 one_of: None,
2706 ..Default::default()
2707 };
2708 let instance = json!(5);
2709 let actual: ValidationResult = validate(&schema, &instance);
2710 let expected: ValidationResult = Err(vec![ValidationError::BelowMinimum {
2711 instance_path: JsonPointer::root(),
2712 minimum: OrderedF64(10.0),
2713 actual: OrderedF64(5.0),
2714 }]);
2715 assert_eq!(expected, actual);
2716 }
2717
2718 #[test]
2719 fn integer_above_maximum() {
2720 let schema: JsonSchema = JsonSchema {
2721 type_: Some("integer".to_string()),
2722 properties: BTreeMap::new(),
2723 additional_properties: None,
2724 required: None,
2725 title: None,
2726 description: None,
2727 comment: None,
2728 enum_values: None,
2729 const_value: None,
2730 items: None,
2731 unique_items: None,
2732 min_items: None,
2733 max_items: None,
2734 minimum: Some(0.0),
2735 maximum: Some(10.0),
2736 min_length: None,
2737 max_length: None,
2738 pattern: None,
2739 format: None,
2740 default_value: None,
2741 all_of: None,
2742 any_of: None,
2743 one_of: None,
2744 ..Default::default()
2745 };
2746 let instance = json!(20);
2747 let actual: ValidationResult = validate(&schema, &instance);
2748 let expected: ValidationResult = Err(vec![ValidationError::AboveMaximum {
2749 instance_path: JsonPointer::root(),
2750 maximum: OrderedF64(10.0),
2751 actual: OrderedF64(20.0),
2752 }]);
2753 assert_eq!(expected, actual);
2754 }
2755
2756 #[test]
2757 fn integer_no_minimum_maximum_no_extra_errors() {
2758 let schema: JsonSchema = JsonSchema {
2759 type_: Some("integer".to_string()),
2760 properties: BTreeMap::new(),
2761 additional_properties: None,
2762 required: None,
2763 title: None,
2764 description: None,
2765 comment: None,
2766 enum_values: None,
2767 const_value: None,
2768 items: None,
2769 unique_items: None,
2770 min_items: None,
2771 max_items: None,
2772 minimum: None,
2773 maximum: None,
2774 min_length: None,
2775 max_length: None,
2776 pattern: None,
2777 format: None,
2778 default_value: None,
2779 all_of: None,
2780 any_of: None,
2781 one_of: None,
2782 ..Default::default()
2783 };
2784 let instance = json!(42);
2785 let actual: ValidationResult = validate(&schema, &instance);
2786 let expected: ValidationResult = Ok(());
2787 assert_eq!(expected, actual);
2788 }
2789
2790 #[test]
2791 fn number_with_minimum_maximum_valid_in_range() {
2792 let schema: JsonSchema = JsonSchema {
2793 type_: Some("number".to_string()),
2794 properties: BTreeMap::new(),
2795 additional_properties: None,
2796 required: None,
2797 title: None,
2798 description: None,
2799 comment: None,
2800 enum_values: None,
2801 const_value: None,
2802 items: None,
2803 unique_items: None,
2804 min_items: None,
2805 max_items: None,
2806 minimum: Some(0.5),
2807 maximum: Some(99.5),
2808 min_length: None,
2809 max_length: None,
2810 pattern: None,
2811 format: None,
2812 default_value: None,
2813 all_of: None,
2814 any_of: None,
2815 one_of: None,
2816 ..Default::default()
2817 };
2818 let instance = json!(50.0);
2819 let actual: ValidationResult = validate(&schema, &instance);
2820 let expected: ValidationResult = Ok(());
2821 assert_eq!(expected, actual);
2822 }
2823
2824 #[test]
2825 fn number_below_minimum() {
2826 let schema: JsonSchema = JsonSchema {
2827 type_: Some("number".to_string()),
2828 properties: BTreeMap::new(),
2829 additional_properties: None,
2830 required: None,
2831 title: None,
2832 description: None,
2833 comment: None,
2834 enum_values: None,
2835 const_value: None,
2836 items: None,
2837 unique_items: None,
2838 min_items: None,
2839 max_items: None,
2840 minimum: Some(1.0),
2841 maximum: Some(10.0),
2842 min_length: None,
2843 max_length: None,
2844 pattern: None,
2845 format: None,
2846 default_value: None,
2847 all_of: None,
2848 any_of: None,
2849 one_of: None,
2850 ..Default::default()
2851 };
2852 let instance = json!(0.5);
2853 let actual: ValidationResult = validate(&schema, &instance);
2854 let expected: ValidationResult = Err(vec![ValidationError::BelowMinimum {
2855 instance_path: JsonPointer::root(),
2856 minimum: OrderedF64(1.0),
2857 actual: OrderedF64(0.5),
2858 }]);
2859 assert_eq!(expected, actual);
2860 }
2861
2862 #[test]
2863 fn number_above_maximum() {
2864 let schema: JsonSchema = JsonSchema {
2865 type_: Some("number".to_string()),
2866 properties: BTreeMap::new(),
2867 additional_properties: None,
2868 required: None,
2869 title: None,
2870 description: None,
2871 comment: None,
2872 enum_values: None,
2873 const_value: None,
2874 items: None,
2875 unique_items: None,
2876 min_items: None,
2877 max_items: None,
2878 minimum: Some(0.0),
2879 maximum: Some(1.0),
2880 min_length: None,
2881 max_length: None,
2882 pattern: None,
2883 format: None,
2884 default_value: None,
2885 all_of: None,
2886 any_of: None,
2887 one_of: None,
2888 ..Default::default()
2889 };
2890 let instance = json!(2.5);
2891 let actual: ValidationResult = validate(&schema, &instance);
2892 let expected: ValidationResult = Err(vec![ValidationError::AboveMaximum {
2893 instance_path: JsonPointer::root(),
2894 maximum: OrderedF64(1.0),
2895 actual: OrderedF64(2.5),
2896 }]);
2897 assert_eq!(expected, actual);
2898 }
2899
2900 #[test]
2901 #[expect(clippy::too_many_lines)]
2902 fn integer_and_number_min_max_violations_collected_from_multiple_properties() {
2903 let schema: JsonSchema = JsonSchema {
2904 type_: Some("object".to_string()),
2905 properties: {
2906 let mut m = BTreeMap::new();
2907 m.insert(
2908 "low".to_string(),
2909 JsonSchema {
2910 type_: Some("integer".to_string()),
2911 properties: BTreeMap::new(),
2912 additional_properties: None,
2913 required: None,
2914 title: None,
2915 description: None,
2916 comment: None,
2917 enum_values: None,
2918 const_value: None,
2919 items: None,
2920 unique_items: None,
2921 min_items: None,
2922 max_items: None,
2923 minimum: Some(10.0),
2924 maximum: Some(100.0),
2925 min_length: None,
2926 max_length: None,
2927 pattern: None,
2928 format: None,
2929 default_value: None,
2930 all_of: None,
2931 any_of: None,
2932 one_of: None,
2933 ..Default::default()
2934 },
2935 );
2936 m.insert(
2937 "high".to_string(),
2938 JsonSchema {
2939 type_: Some("integer".to_string()),
2940 properties: BTreeMap::new(),
2941 additional_properties: None,
2942 required: None,
2943 title: None,
2944 description: None,
2945 comment: None,
2946 enum_values: None,
2947 const_value: None,
2948 items: None,
2949 unique_items: None,
2950 min_items: None,
2951 max_items: None,
2952 minimum: Some(10.0),
2953 maximum: Some(100.0),
2954 min_length: None,
2955 max_length: None,
2956 pattern: None,
2957 format: None,
2958 default_value: None,
2959 all_of: None,
2960 any_of: None,
2961 one_of: None,
2962 ..Default::default()
2963 },
2964 );
2965 m
2966 },
2967 additional_properties: None,
2968 required: None,
2969 title: None,
2970 description: None,
2971 comment: None,
2972 enum_values: None,
2973 const_value: None,
2974 items: None,
2975 unique_items: None,
2976 min_items: None,
2977 max_items: None,
2978 minimum: None,
2979 maximum: None,
2980 min_length: None,
2981 max_length: None,
2982 pattern: None,
2983 format: None,
2984 default_value: None,
2985 all_of: None,
2986 any_of: None,
2987 one_of: None,
2988 ..Default::default()
2989 };
2990 let instance = json!({"low": 5, "high": 200});
2991 let actual: ValidationResult = validate(&schema, &instance);
2992 let expected: ValidationResult = Err(vec![
2993 ValidationError::AboveMaximum {
2994 instance_path: JsonPointer::root().push("high"),
2995 maximum: OrderedF64(100.0),
2996 actual: OrderedF64(200.0),
2997 },
2998 ValidationError::BelowMinimum {
2999 instance_path: JsonPointer::root().push("low"),
3000 minimum: OrderedF64(10.0),
3001 actual: OrderedF64(5.0),
3002 },
3003 ]);
3004 assert_eq!(expected, actual);
3005 }
3006
3007 #[test]
3008 fn root_type_array_valid_empty() {
3009 let schema: JsonSchema = JsonSchema {
3010 type_: Some("array".to_string()),
3011 properties: BTreeMap::new(),
3012 additional_properties: None,
3013 required: None,
3014 title: None,
3015 description: None,
3016 comment: None,
3017 enum_values: None,
3018 const_value: None,
3019 items: None,
3020 unique_items: None,
3021 min_items: None,
3022 max_items: None,
3023 minimum: None,
3024 maximum: None,
3025 min_length: None,
3026 max_length: None,
3027 pattern: None,
3028 format: None,
3029 default_value: None,
3030 all_of: None,
3031 any_of: None,
3032 one_of: None,
3033 ..Default::default()
3034 };
3035 let instance = json!([]);
3036 let actual: ValidationResult = validate(&schema, &instance);
3037 let expected: ValidationResult = Ok(());
3038 assert_eq!(expected, actual);
3039 }
3040
3041 #[test]
3042 fn root_type_array_valid_non_empty() {
3043 let schema: JsonSchema = JsonSchema {
3044 type_: Some("array".to_string()),
3045 properties: BTreeMap::new(),
3046 additional_properties: None,
3047 required: None,
3048 title: None,
3049 description: None,
3050 comment: None,
3051 enum_values: None,
3052 const_value: None,
3053 items: None,
3054 unique_items: None,
3055 min_items: None,
3056 max_items: None,
3057 minimum: None,
3058 maximum: None,
3059 min_length: None,
3060 max_length: None,
3061 pattern: None,
3062 format: None,
3063 default_value: None,
3064 all_of: None,
3065 any_of: None,
3066 one_of: None,
3067 ..Default::default()
3068 };
3069 let instance = json!([1, 2, 3]);
3070 let actual: ValidationResult = validate(&schema, &instance);
3071 let expected: ValidationResult = Ok(());
3072 assert_eq!(expected, actual);
3073 }
3074
3075 #[test]
3076 fn root_type_array_invalid_not_array() {
3077 let schema: JsonSchema = JsonSchema {
3078 type_: Some("array".to_string()),
3079 properties: BTreeMap::new(),
3080 additional_properties: None,
3081 required: None,
3082 title: None,
3083 description: None,
3084 comment: None,
3085 enum_values: None,
3086 const_value: None,
3087 items: None,
3088 unique_items: None,
3089 min_items: None,
3090 max_items: None,
3091 minimum: None,
3092 maximum: None,
3093 min_length: None,
3094 max_length: None,
3095 pattern: None,
3096 format: None,
3097 default_value: None,
3098 all_of: None,
3099 any_of: None,
3100 one_of: None,
3101 ..Default::default()
3102 };
3103 let instance = json!("not an array");
3104 let actual: ValidationResult = validate(&schema, &instance);
3105 let expected: ValidationResult = Err(vec![ValidationError::ExpectedArray {
3106 instance_path: JsonPointer::root(),
3107 got: "string".to_string(),
3108 }]);
3109 assert_eq!(expected, actual);
3110 }
3111
3112 #[test]
3113 fn root_type_array_with_items_valid() {
3114 let item_schema: JsonSchema = JsonSchema {
3115 type_: Some("string".to_string()),
3116 properties: BTreeMap::new(),
3117 additional_properties: None,
3118 required: None,
3119 title: None,
3120 description: None,
3121 comment: None,
3122 enum_values: None,
3123 const_value: None,
3124 items: None,
3125 unique_items: None,
3126 min_items: None,
3127 max_items: None,
3128 minimum: None,
3129 maximum: None,
3130 min_length: None,
3131 max_length: None,
3132 pattern: None,
3133 format: None,
3134 default_value: None,
3135 all_of: None,
3136 any_of: None,
3137 one_of: None,
3138 ..Default::default()
3139 };
3140 let schema: JsonSchema = JsonSchema {
3141 type_: Some("array".to_string()),
3142 properties: BTreeMap::new(),
3143 additional_properties: None,
3144 required: None,
3145 title: None,
3146 description: None,
3147 comment: None,
3148 enum_values: None,
3149 const_value: None,
3150 items: Some(Box::new(item_schema)),
3151 unique_items: None,
3152 min_items: None,
3153 max_items: None,
3154 minimum: None,
3155 maximum: None,
3156 min_length: None,
3157 max_length: None,
3158 pattern: None,
3159 format: None,
3160 default_value: None,
3161 all_of: None,
3162 any_of: None,
3163 one_of: None,
3164 ..Default::default()
3165 };
3166 let instance = json!(["a", "b", "c"]);
3167 let actual: ValidationResult = validate(&schema, &instance);
3168 let expected: ValidationResult = Ok(());
3169 assert_eq!(expected, actual);
3170 }
3171
3172 #[test]
3173 fn root_type_array_with_items_invalid_element() {
3174 let item_schema: JsonSchema = JsonSchema {
3175 type_: Some("string".to_string()),
3176 properties: BTreeMap::new(),
3177 additional_properties: None,
3178 required: None,
3179 title: None,
3180 description: None,
3181 comment: None,
3182 enum_values: None,
3183 const_value: None,
3184 items: None,
3185 unique_items: None,
3186 min_items: None,
3187 max_items: None,
3188 minimum: None,
3189 maximum: None,
3190 min_length: None,
3191 max_length: None,
3192 pattern: None,
3193 format: None,
3194 default_value: None,
3195 all_of: None,
3196 any_of: None,
3197 one_of: None,
3198 ..Default::default()
3199 };
3200 let schema: JsonSchema = JsonSchema {
3201 type_: Some("array".to_string()),
3202 properties: BTreeMap::new(),
3203 additional_properties: None,
3204 required: None,
3205 title: None,
3206 description: None,
3207 comment: None,
3208 enum_values: None,
3209 const_value: None,
3210 items: Some(Box::new(item_schema)),
3211 unique_items: None,
3212 min_items: None,
3213 max_items: None,
3214 minimum: None,
3215 maximum: None,
3216 min_length: None,
3217 max_length: None,
3218 pattern: None,
3219 format: None,
3220 default_value: None,
3221 all_of: None,
3222 any_of: None,
3223 one_of: None,
3224 ..Default::default()
3225 };
3226 let instance = json!(["a", 42, "c"]);
3227 let actual: ValidationResult = validate(&schema, &instance);
3228 let expected: ValidationResult = Err(vec![ValidationError::ExpectedString {
3229 instance_path: JsonPointer::root().push("1"),
3230 got: "number".to_string(),
3231 }]);
3232 assert_eq!(expected, actual);
3233 }
3234
3235 #[test]
3236 fn unique_items_true_no_duplicates_valid() {
3237 let item_schema: JsonSchema = JsonSchema {
3238 type_: Some("string".to_string()),
3239 properties: BTreeMap::new(),
3240 additional_properties: None,
3241 required: None,
3242 title: None,
3243 description: None,
3244 comment: None,
3245 enum_values: None,
3246 const_value: None,
3247 items: None,
3248 unique_items: None,
3249 min_items: None,
3250 max_items: None,
3251 minimum: None,
3252 maximum: None,
3253 min_length: None,
3254 max_length: None,
3255 pattern: None,
3256 format: None,
3257 default_value: None,
3258 all_of: None,
3259 any_of: None,
3260 one_of: None,
3261 ..Default::default()
3262 };
3263 let schema: JsonSchema = JsonSchema {
3264 type_: Some("array".to_string()),
3265 properties: BTreeMap::new(),
3266 additional_properties: None,
3267 required: None,
3268 title: None,
3269 description: None,
3270 comment: None,
3271 enum_values: None,
3272 const_value: None,
3273 items: Some(Box::new(item_schema)),
3274 unique_items: Some(true),
3275 min_items: None,
3276 max_items: None,
3277 minimum: None,
3278 maximum: None,
3279 min_length: None,
3280 max_length: None,
3281 pattern: None,
3282 format: None,
3283 default_value: None,
3284 all_of: None,
3285 any_of: None,
3286 one_of: None,
3287 ..Default::default()
3288 };
3289 let instance = json!(["a", "b", "c"]);
3290 let actual: ValidationResult = validate(&schema, &instance);
3291 let expected: ValidationResult = Ok(());
3292 assert_eq!(expected, actual);
3293 }
3294
3295 #[test]
3296 fn unique_items_true_duplicates_invalid() {
3297 let item_schema: JsonSchema = JsonSchema {
3298 type_: Some("string".to_string()),
3299 properties: BTreeMap::new(),
3300 additional_properties: None,
3301 required: None,
3302 title: None,
3303 description: None,
3304 comment: None,
3305 enum_values: None,
3306 const_value: None,
3307 items: None,
3308 unique_items: None,
3309 min_items: None,
3310 max_items: None,
3311 minimum: None,
3312 maximum: None,
3313 min_length: None,
3314 max_length: None,
3315 pattern: None,
3316 format: None,
3317 default_value: None,
3318 all_of: None,
3319 any_of: None,
3320 one_of: None,
3321 ..Default::default()
3322 };
3323 let schema: JsonSchema = JsonSchema {
3324 type_: Some("array".to_string()),
3325 properties: BTreeMap::new(),
3326 additional_properties: None,
3327 required: None,
3328 title: None,
3329 description: None,
3330 comment: None,
3331 enum_values: None,
3332 const_value: None,
3333 items: Some(Box::new(item_schema)),
3334 unique_items: Some(true),
3335 min_items: None,
3336 max_items: None,
3337 minimum: None,
3338 maximum: None,
3339 min_length: None,
3340 max_length: None,
3341 pattern: None,
3342 format: None,
3343 default_value: None,
3344 all_of: None,
3345 any_of: None,
3346 one_of: None,
3347 ..Default::default()
3348 };
3349 let instance = json!(["a", "b", "a"]);
3350 let actual: ValidationResult = validate(&schema, &instance);
3351 let expected_has_duplicate_error: bool = actual.as_ref().err().is_some_and(|e| {
3352 e.iter()
3353 .any(|err| matches!(err, ValidationError::DuplicateArrayItems { .. }))
3354 });
3355 assert!(
3356 expected_has_duplicate_error,
3357 "expected DuplicateArrayItems: {actual:?}"
3358 );
3359 }
3360
3361 #[test]
3362 fn unique_items_false_duplicates_valid() {
3363 let item_schema: JsonSchema = JsonSchema {
3364 type_: Some("string".to_string()),
3365 properties: BTreeMap::new(),
3366 additional_properties: None,
3367 required: None,
3368 title: None,
3369 description: None,
3370 comment: None,
3371 enum_values: None,
3372 const_value: None,
3373 items: None,
3374 unique_items: None,
3375 min_items: None,
3376 max_items: None,
3377 minimum: None,
3378 maximum: None,
3379 min_length: None,
3380 max_length: None,
3381 pattern: None,
3382 format: None,
3383 default_value: None,
3384 all_of: None,
3385 any_of: None,
3386 one_of: None,
3387 ..Default::default()
3388 };
3389 let schema: JsonSchema = JsonSchema {
3390 type_: Some("array".to_string()),
3391 properties: BTreeMap::new(),
3392 additional_properties: None,
3393 required: None,
3394 title: None,
3395 description: None,
3396 comment: None,
3397 enum_values: None,
3398 const_value: None,
3399 items: Some(Box::new(item_schema)),
3400 unique_items: Some(false),
3401 min_items: None,
3402 max_items: None,
3403 minimum: None,
3404 maximum: None,
3405 min_length: None,
3406 max_length: None,
3407 pattern: None,
3408 format: None,
3409 default_value: None,
3410 all_of: None,
3411 any_of: None,
3412 one_of: None,
3413 ..Default::default()
3414 };
3415 let instance = json!(["a", "a"]);
3416 let actual: ValidationResult = validate(&schema, &instance);
3417 let expected: ValidationResult = Ok(());
3418 assert_eq!(expected, actual);
3419 }
3420
3421 #[test]
3422 fn unique_items_absent_duplicates_valid() {
3423 let item_schema: JsonSchema = JsonSchema {
3424 type_: Some("string".to_string()),
3425 properties: BTreeMap::new(),
3426 additional_properties: None,
3427 required: None,
3428 title: None,
3429 description: None,
3430 comment: None,
3431 enum_values: None,
3432 const_value: None,
3433 items: None,
3434 unique_items: None,
3435 min_items: None,
3436 max_items: None,
3437 minimum: None,
3438 maximum: None,
3439 min_length: None,
3440 max_length: None,
3441 pattern: None,
3442 format: None,
3443 default_value: None,
3444 all_of: None,
3445 any_of: None,
3446 one_of: None,
3447 ..Default::default()
3448 };
3449 let schema: JsonSchema = JsonSchema {
3450 type_: Some("array".to_string()),
3451 properties: BTreeMap::new(),
3452 additional_properties: None,
3453 required: None,
3454 title: None,
3455 description: None,
3456 comment: None,
3457 enum_values: None,
3458 const_value: None,
3459 items: Some(Box::new(item_schema)),
3460 unique_items: None,
3461 min_items: None,
3462 max_items: None,
3463 minimum: None,
3464 maximum: None,
3465 min_length: None,
3466 max_length: None,
3467 pattern: None,
3468 format: None,
3469 default_value: None,
3470 all_of: None,
3471 any_of: None,
3472 one_of: None,
3473 ..Default::default()
3474 };
3475 let instance = json!(["a", "a"]);
3476 let actual: ValidationResult = validate(&schema, &instance);
3477 let expected: ValidationResult = Ok(());
3478 assert_eq!(expected, actual);
3479 }
3480
3481 #[test]
3482 fn unique_items_true_empty_array_valid() {
3483 let schema: JsonSchema = JsonSchema {
3484 type_: Some("array".to_string()),
3485 properties: BTreeMap::new(),
3486 additional_properties: None,
3487 required: None,
3488 title: None,
3489 description: None,
3490 comment: None,
3491 enum_values: None,
3492 const_value: None,
3493 items: None,
3494 unique_items: Some(true),
3495 min_items: None,
3496 max_items: None,
3497 minimum: None,
3498 maximum: None,
3499 min_length: None,
3500 max_length: None,
3501 pattern: None,
3502 format: None,
3503 default_value: None,
3504 all_of: None,
3505 any_of: None,
3506 one_of: None,
3507 ..Default::default()
3508 };
3509 let instance = json!([]);
3510 let actual: ValidationResult = validate(&schema, &instance);
3511 let expected: ValidationResult = Ok(());
3512 assert_eq!(expected, actual);
3513 }
3514
3515 #[test]
3516 fn min_items_only_pass() {
3517 let schema: JsonSchema = JsonSchema {
3518 type_: Some("array".to_string()),
3519 properties: BTreeMap::new(),
3520 additional_properties: None,
3521 required: None,
3522 title: None,
3523 description: None,
3524 comment: None,
3525 enum_values: None,
3526 const_value: None,
3527 items: None,
3528 unique_items: None,
3529 min_items: Some(2),
3530 max_items: None,
3531 minimum: None,
3532 maximum: None,
3533 min_length: None,
3534 max_length: None,
3535 pattern: None,
3536 format: None,
3537 default_value: None,
3538 all_of: None,
3539 any_of: None,
3540 one_of: None,
3541 ..Default::default()
3542 };
3543 let instance = json!([1, 2, 3]);
3544 let actual: ValidationResult = validate(&schema, &instance);
3545 let expected: ValidationResult = Ok(());
3546 assert_eq!(expected, actual);
3547 }
3548
3549 #[test]
3550 fn min_items_only_fail() {
3551 let schema: JsonSchema = JsonSchema {
3552 type_: Some("array".to_string()),
3553 properties: BTreeMap::new(),
3554 additional_properties: None,
3555 required: None,
3556 title: None,
3557 description: None,
3558 comment: None,
3559 enum_values: None,
3560 const_value: None,
3561 items: None,
3562 unique_items: None,
3563 min_items: Some(3),
3564 max_items: None,
3565 minimum: None,
3566 maximum: None,
3567 min_length: None,
3568 max_length: None,
3569 pattern: None,
3570 format: None,
3571 default_value: None,
3572 all_of: None,
3573 any_of: None,
3574 one_of: None,
3575 ..Default::default()
3576 };
3577 let instance = json!([1, 2]);
3578 let actual: ValidationResult = validate(&schema, &instance);
3579 let expected: ValidationResult = Err(vec![ValidationError::TooFewItems {
3580 instance_path: JsonPointer::root(),
3581 min_items: 3,
3582 actual_count: 2,
3583 }]);
3584 assert_eq!(expected, actual);
3585 }
3586
3587 #[test]
3588 fn min_items_only_edge_len_equals_min() {
3589 let schema: JsonSchema = JsonSchema {
3590 type_: Some("array".to_string()),
3591 properties: BTreeMap::new(),
3592 additional_properties: None,
3593 required: None,
3594 title: None,
3595 description: None,
3596 comment: None,
3597 enum_values: None,
3598 const_value: None,
3599 items: None,
3600 unique_items: None,
3601 min_items: Some(2),
3602 max_items: None,
3603 minimum: None,
3604 maximum: None,
3605 min_length: None,
3606 max_length: None,
3607 pattern: None,
3608 format: None,
3609 default_value: None,
3610 all_of: None,
3611 any_of: None,
3612 one_of: None,
3613 ..Default::default()
3614 };
3615 let instance = json!([1, 2]);
3616 let actual: ValidationResult = validate(&schema, &instance);
3617 let expected: ValidationResult = Ok(());
3618 assert_eq!(expected, actual);
3619 }
3620
3621 #[test]
3622 fn max_items_only_pass() {
3623 let schema: JsonSchema = JsonSchema {
3624 type_: Some("array".to_string()),
3625 properties: BTreeMap::new(),
3626 additional_properties: None,
3627 required: None,
3628 title: None,
3629 description: None,
3630 comment: None,
3631 enum_values: None,
3632 const_value: None,
3633 items: None,
3634 unique_items: None,
3635 min_items: None,
3636 max_items: Some(5),
3637 minimum: None,
3638 maximum: None,
3639 min_length: None,
3640 max_length: None,
3641 pattern: None,
3642 format: None,
3643 default_value: None,
3644 all_of: None,
3645 any_of: None,
3646 one_of: None,
3647 ..Default::default()
3648 };
3649 let instance = json!([1, 2]);
3650 let actual: ValidationResult = validate(&schema, &instance);
3651 let expected: ValidationResult = Ok(());
3652 assert_eq!(expected, actual);
3653 }
3654
3655 #[test]
3656 fn max_items_only_fail() {
3657 let schema: JsonSchema = JsonSchema {
3658 type_: Some("array".to_string()),
3659 properties: BTreeMap::new(),
3660 additional_properties: None,
3661 required: None,
3662 title: None,
3663 description: None,
3664 comment: None,
3665 enum_values: None,
3666 const_value: None,
3667 items: None,
3668 unique_items: None,
3669 min_items: None,
3670 max_items: Some(2),
3671 minimum: None,
3672 maximum: None,
3673 min_length: None,
3674 max_length: None,
3675 pattern: None,
3676 format: None,
3677 default_value: None,
3678 all_of: None,
3679 any_of: None,
3680 one_of: None,
3681 ..Default::default()
3682 };
3683 let instance = json!([1, 2, 3]);
3684 let actual: ValidationResult = validate(&schema, &instance);
3685 let expected: ValidationResult = Err(vec![ValidationError::TooManyItems {
3686 instance_path: JsonPointer::root(),
3687 max_items: 2,
3688 actual_count: 3,
3689 }]);
3690 assert_eq!(expected, actual);
3691 }
3692
3693 #[test]
3694 fn max_items_only_edge_len_equals_max() {
3695 let schema: JsonSchema = JsonSchema {
3696 type_: Some("array".to_string()),
3697 properties: BTreeMap::new(),
3698 additional_properties: None,
3699 required: None,
3700 title: None,
3701 description: None,
3702 comment: None,
3703 enum_values: None,
3704 const_value: None,
3705 items: None,
3706 unique_items: None,
3707 min_items: None,
3708 max_items: Some(2),
3709 minimum: None,
3710 maximum: None,
3711 min_length: None,
3712 max_length: None,
3713 pattern: None,
3714 format: None,
3715 default_value: None,
3716 all_of: None,
3717 any_of: None,
3718 one_of: None,
3719 ..Default::default()
3720 };
3721 let instance = json!([1, 2]);
3722 let actual: ValidationResult = validate(&schema, &instance);
3723 let expected: ValidationResult = Ok(());
3724 assert_eq!(expected, actual);
3725 }
3726
3727 #[test]
3728 fn min_items_max_items_both_pass() {
3729 let schema: JsonSchema = JsonSchema {
3730 type_: Some("array".to_string()),
3731 properties: BTreeMap::new(),
3732 additional_properties: None,
3733 required: None,
3734 title: None,
3735 description: None,
3736 comment: None,
3737 enum_values: None,
3738 const_value: None,
3739 items: None,
3740 unique_items: None,
3741 min_items: Some(2),
3742 max_items: Some(5),
3743 minimum: None,
3744 maximum: None,
3745 min_length: None,
3746 max_length: None,
3747 pattern: None,
3748 format: None,
3749 default_value: None,
3750 all_of: None,
3751 any_of: None,
3752 one_of: None,
3753 ..Default::default()
3754 };
3755 let instance = json!([1, 2, 3]);
3756 let actual: ValidationResult = validate(&schema, &instance);
3757 let expected: ValidationResult = Ok(());
3758 assert_eq!(expected, actual);
3759 }
3760
3761 #[test]
3762 fn min_items_max_items_fail_too_few() {
3763 let schema: JsonSchema = JsonSchema {
3764 type_: Some("array".to_string()),
3765 properties: BTreeMap::new(),
3766 additional_properties: None,
3767 required: None,
3768 title: None,
3769 description: None,
3770 comment: None,
3771 enum_values: None,
3772 const_value: None,
3773 items: None,
3774 unique_items: None,
3775 min_items: Some(2),
3776 max_items: Some(5),
3777 minimum: None,
3778 maximum: None,
3779 min_length: None,
3780 max_length: None,
3781 pattern: None,
3782 format: None,
3783 default_value: None,
3784 all_of: None,
3785 any_of: None,
3786 one_of: None,
3787 ..Default::default()
3788 };
3789 let instance = json!([1]);
3790 let actual: ValidationResult = validate(&schema, &instance);
3791 let expected: ValidationResult = Err(vec![ValidationError::TooFewItems {
3792 instance_path: JsonPointer::root(),
3793 min_items: 2,
3794 actual_count: 1,
3795 }]);
3796 assert_eq!(expected, actual);
3797 }
3798
3799 #[test]
3800 fn min_items_max_items_fail_too_many() {
3801 let schema: JsonSchema = JsonSchema {
3802 type_: Some("array".to_string()),
3803 properties: BTreeMap::new(),
3804 additional_properties: None,
3805 required: None,
3806 title: None,
3807 description: None,
3808 comment: None,
3809 enum_values: None,
3810 const_value: None,
3811 items: None,
3812 unique_items: None,
3813 min_items: Some(2),
3814 max_items: Some(5),
3815 minimum: None,
3816 maximum: None,
3817 min_length: None,
3818 max_length: None,
3819 pattern: None,
3820 format: None,
3821 default_value: None,
3822 all_of: None,
3823 any_of: None,
3824 one_of: None,
3825 ..Default::default()
3826 };
3827 let instance = json!([1, 2, 3, 4, 5, 6]);
3828 let actual: ValidationResult = validate(&schema, &instance);
3829 let expected: ValidationResult = Err(vec![ValidationError::TooManyItems {
3830 instance_path: JsonPointer::root(),
3831 max_items: 5,
3832 actual_count: 6,
3833 }]);
3834 assert_eq!(expected, actual);
3835 }
3836
3837 #[test]
3838 fn min_items_max_items_absent_unchanged() {
3839 let schema: JsonSchema = JsonSchema {
3840 type_: Some("array".to_string()),
3841 properties: BTreeMap::new(),
3842 additional_properties: None,
3843 required: None,
3844 title: None,
3845 description: None,
3846 comment: None,
3847 enum_values: None,
3848 const_value: None,
3849 items: None,
3850 unique_items: None,
3851 min_items: None,
3852 max_items: None,
3853 minimum: None,
3854 maximum: None,
3855 min_length: None,
3856 max_length: None,
3857 pattern: None,
3858 format: None,
3859 default_value: None,
3860 all_of: None,
3861 any_of: None,
3862 one_of: None,
3863 ..Default::default()
3864 };
3865 let instance = json!([1, 2, 3]);
3866 let actual: ValidationResult = validate(&schema, &instance);
3867 let expected: ValidationResult = Ok(());
3868 assert_eq!(expected, actual);
3869 }
3870
3871 #[test]
3872 fn min_items_max_items_not_array_expected_array_only() {
3873 let schema: JsonSchema = JsonSchema {
3874 type_: Some("array".to_string()),
3875 properties: BTreeMap::new(),
3876 additional_properties: None,
3877 required: None,
3878 title: None,
3879 description: None,
3880 comment: None,
3881 enum_values: None,
3882 const_value: None,
3883 items: None,
3884 unique_items: None,
3885 min_items: Some(2),
3886 max_items: Some(5),
3887 minimum: None,
3888 maximum: None,
3889 min_length: None,
3890 max_length: None,
3891 pattern: None,
3892 format: None,
3893 default_value: None,
3894 all_of: None,
3895 any_of: None,
3896 one_of: None,
3897 ..Default::default()
3898 };
3899 let instance = json!("not an array");
3900 let actual: ValidationResult = validate(&schema, &instance);
3901 let expected: ValidationResult = Err(vec![ValidationError::ExpectedArray {
3902 instance_path: JsonPointer::root(),
3903 got: "string".to_string(),
3904 }]);
3905 assert_eq!(expected, actual);
3906 }
3907
3908 #[test]
3909 fn nested_property_type_number_valid() {
3910 let schema = schema_object_with_required(vec!["value"], {
3911 let mut m = BTreeMap::new();
3912 m.insert(
3913 "value".to_string(),
3914 JsonSchema {
3915 type_: Some("number".to_string()),
3916 ..Default::default()
3917 },
3918 );
3919 m
3920 });
3921 let instance = json!({"value": 2.5});
3922 let actual: ValidationResult = validate(&schema, &instance);
3923 let expected: ValidationResult = Ok(());
3924 assert_eq!(expected, actual);
3925 }
3926
3927 #[test]
3928 fn nested_property_type_number_invalid_string() {
3929 let schema = schema_object_with_required(vec!["value"], {
3930 let mut m = BTreeMap::new();
3931 m.insert(
3932 "value".to_string(),
3933 JsonSchema {
3934 type_: Some("number".to_string()),
3935 ..Default::default()
3936 },
3937 );
3938 m
3939 });
3940 let instance = json!({"value": "3.14"});
3941 let actual: ValidationResult = validate(&schema, &instance);
3942 let expected: ValidationResult = Err(vec![ValidationError::ExpectedNumber {
3943 instance_path: JsonPointer::root().push("value"),
3944 got: "string".to_string(),
3945 }]);
3946 assert_eq!(expected, actual);
3947 }
3948
3949 #[test]
3950 fn nested_required_number_missing() {
3951 let schema = schema_object_with_required(vec!["value"], {
3952 let mut m = BTreeMap::new();
3953 m.insert(
3954 "value".to_string(),
3955 JsonSchema {
3956 type_: Some("number".to_string()),
3957 ..Default::default()
3958 },
3959 );
3960 m
3961 });
3962 let instance = json!({});
3963 let actual: ValidationResult = validate(&schema, &instance);
3964 let expected: ValidationResult = Err(vec![ValidationError::MissingRequired {
3965 instance_path: JsonPointer::root().push("value"),
3966 property: "value".to_string(),
3967 }]);
3968 assert_eq!(expected, actual);
3969 }
3970
3971 #[test]
3972 fn wrong_type_object_with_number() {
3973 let schema: JsonSchema = JsonSchema {
3974 type_: Some("object".to_string()),
3975 properties: BTreeMap::new(),
3976 additional_properties: None,
3977 required: None,
3978 title: None,
3979 description: None,
3980 comment: None,
3981 enum_values: None,
3982 const_value: None,
3983 items: None,
3984 unique_items: None,
3985 min_items: None,
3986 max_items: None,
3987 minimum: None,
3988 maximum: None,
3989 min_length: None,
3990 max_length: None,
3991 pattern: None,
3992 format: None,
3993 default_value: None,
3994 all_of: None,
3995 any_of: None,
3996 one_of: None,
3997 ..Default::default()
3998 };
3999 let instance = json!(42);
4000 let actual: ValidationResult = validate(&schema, &instance);
4001 let expected: ValidationResult = Err(vec![ValidationError::ExpectedObject {
4002 instance_path: JsonPointer::root(),
4003 got: "number".to_string(),
4004 }]);
4005 assert_eq!(expected, actual);
4006 }
4007
4008 #[test]
4009 fn wrong_type_object_with_null() {
4010 let schema: JsonSchema = JsonSchema {
4011 type_: Some("object".to_string()),
4012 properties: BTreeMap::new(),
4013 additional_properties: None,
4014 required: None,
4015 title: None,
4016 description: None,
4017 comment: None,
4018 enum_values: None,
4019 const_value: None,
4020 items: None,
4021 unique_items: None,
4022 min_items: None,
4023 max_items: None,
4024 minimum: None,
4025 maximum: None,
4026 min_length: None,
4027 max_length: None,
4028 pattern: None,
4029 format: None,
4030 default_value: None,
4031 all_of: None,
4032 any_of: None,
4033 one_of: None,
4034 ..Default::default()
4035 };
4036 let instance = json!(null);
4037 let actual: ValidationResult = validate(&schema, &instance);
4038 let expected: ValidationResult = Err(vec![ValidationError::ExpectedObject {
4039 instance_path: JsonPointer::root(),
4040 got: "null".to_string(),
4041 }]);
4042 assert_eq!(expected, actual);
4043 }
4044
4045 #[test]
4046 fn wrong_type_object_with_array() {
4047 let schema: JsonSchema = JsonSchema {
4048 type_: Some("object".to_string()),
4049 properties: BTreeMap::new(),
4050 additional_properties: None,
4051 required: None,
4052 title: None,
4053 description: None,
4054 comment: None,
4055 enum_values: None,
4056 const_value: None,
4057 items: None,
4058 unique_items: None,
4059 min_items: None,
4060 max_items: None,
4061 minimum: None,
4062 maximum: None,
4063 min_length: None,
4064 max_length: None,
4065 pattern: None,
4066 format: None,
4067 default_value: None,
4068 all_of: None,
4069 any_of: None,
4070 one_of: None,
4071 ..Default::default()
4072 };
4073 let instance = json!([1, 2]);
4074 let actual: ValidationResult = validate(&schema, &instance);
4075 let expected: ValidationResult = Err(vec![ValidationError::ExpectedObject {
4076 instance_path: JsonPointer::root(),
4077 got: "array".to_string(),
4078 }]);
4079 assert_eq!(expected, actual);
4080 }
4081
4082 #[test]
4083 fn no_type_but_properties_object_instance_valid() {
4084 let schema: JsonSchema = JsonSchema {
4085 type_: None,
4086 properties: {
4087 let mut m = BTreeMap::new();
4088 m.insert(
4089 "name".to_string(),
4090 JsonSchema {
4091 type_: Some("string".to_string()),
4092 ..Default::default()
4093 },
4094 );
4095 m
4096 },
4097 additional_properties: None,
4098 required: Some(vec!["name".to_string()]),
4099 title: None,
4100 description: None,
4101 comment: None,
4102 enum_values: None,
4103 const_value: None,
4104 items: None,
4105 unique_items: None,
4106 min_items: None,
4107 max_items: None,
4108 minimum: None,
4109 maximum: None,
4110 min_length: None,
4111 max_length: None,
4112 pattern: None,
4113 format: None,
4114 default_value: None,
4115 all_of: None,
4116 any_of: None,
4117 one_of: None,
4118 ..Default::default()
4119 };
4120 let instance = json!({"name": "Alice"});
4121 let actual: ValidationResult = validate(&schema, &instance);
4122 let expected: ValidationResult = Ok(());
4123 assert_eq!(expected, actual);
4124 }
4125
4126 #[test]
4127 fn nested_object_validation() {
4128 let schema = schema_object_with_required(vec!["address"], {
4129 let mut m = BTreeMap::new();
4130 m.insert(
4131 "address".to_string(),
4132 JsonSchema {
4133 type_: Some("object".to_string()),
4134 properties: {
4135 let mut inner = BTreeMap::new();
4136 inner.insert(
4137 "city".to_string(),
4138 JsonSchema {
4139 type_: Some("string".to_string()),
4140 ..Default::default()
4141 },
4142 );
4143 inner
4144 },
4145 additional_properties: None,
4146 required: Some(vec!["city".to_string()]),
4147 title: None,
4148 description: None,
4149 comment: None,
4150 enum_values: None,
4151 const_value: None,
4152 items: None,
4153 unique_items: None,
4154 min_items: None,
4155 max_items: None,
4156 minimum: None,
4157 maximum: None,
4158 min_length: None,
4159 max_length: None,
4160 pattern: None,
4161 format: None,
4162 default_value: None,
4163 all_of: None,
4164 any_of: None,
4165 one_of: None,
4166 ..Default::default()
4167 },
4168 );
4169 m
4170 });
4171 let instance = json!({"address": {}});
4172 let actual: ValidationResult = validate(&schema, &instance);
4173 let expected: ValidationResult = Err(vec![ValidationError::MissingRequired {
4174 instance_path: JsonPointer::root().push("address").push("city"),
4175 property: "city".to_string(),
4176 }]);
4177 assert_eq!(expected, actual);
4178 }
4179
4180 #[test]
4181 fn optional_property_absent_valid() {
4182 let schema = schema_object_with_required(vec![], {
4183 let mut m = BTreeMap::new();
4184 m.insert(
4185 "opt".to_string(),
4186 JsonSchema {
4187 type_: Some("string".to_string()),
4188 ..Default::default()
4189 },
4190 );
4191 m
4192 });
4193 let instance = json!({});
4194 let actual: ValidationResult = validate(&schema, &instance);
4195 let expected: ValidationResult = Ok(());
4196 assert_eq!(expected, actual);
4197 }
4198
4199 #[test]
4200 fn multiple_failures_all_errors_returned() {
4201 let schema = schema_object_with_required(vec!["a", "b", "c"], {
4202 let mut m = BTreeMap::new();
4203 m.insert(
4204 "a".to_string(),
4205 JsonSchema {
4206 type_: Some("string".to_string()),
4207 ..Default::default()
4208 },
4209 );
4210 m.insert(
4211 "b".to_string(),
4212 JsonSchema {
4213 type_: Some("string".to_string()),
4214 ..Default::default()
4215 },
4216 );
4217 m.insert(
4218 "c".to_string(),
4219 JsonSchema {
4220 type_: Some("string".to_string()),
4221 ..Default::default()
4222 },
4223 );
4224 m
4225 });
4226 let instance = json!({});
4227 let actual: ValidationResult = validate(&schema, &instance);
4228 let expected: ValidationResult = Err(vec![
4229 ValidationError::MissingRequired {
4230 instance_path: JsonPointer::root().push("a"),
4231 property: "a".to_string(),
4232 },
4233 ValidationError::MissingRequired {
4234 instance_path: JsonPointer::root().push("b"),
4235 property: "b".to_string(),
4236 },
4237 ValidationError::MissingRequired {
4238 instance_path: JsonPointer::root().push("c"),
4239 property: "c".to_string(),
4240 },
4241 ]);
4242 assert_eq!(expected, actual);
4243 }
4244
4245 #[test]
4246 fn multiple_failures_type_and_required_and_nested() {
4247 let schema = schema_object_with_required(vec!["x", "nested"], {
4248 let mut m = BTreeMap::new();
4249 m.insert(
4250 "x".to_string(),
4251 JsonSchema {
4252 type_: Some("string".to_string()),
4253 ..Default::default()
4254 },
4255 );
4256 m.insert(
4257 "nested".to_string(),
4258 JsonSchema {
4259 type_: Some("object".to_string()),
4260 properties: {
4261 let mut inner = BTreeMap::new();
4262 inner.insert(
4263 "y".to_string(),
4264 JsonSchema {
4265 type_: Some("string".to_string()),
4266 ..Default::default()
4267 },
4268 );
4269 inner
4270 },
4271 additional_properties: None,
4272 required: Some(vec!["y".to_string()]),
4273 title: None,
4274 description: None,
4275 comment: None,
4276 enum_values: None,
4277 const_value: None,
4278 items: None,
4279 unique_items: None,
4280 min_items: None,
4281 max_items: None,
4282 minimum: None,
4283 maximum: None,
4284 min_length: None,
4285 max_length: None,
4286 pattern: None,
4287 format: None,
4288 default_value: None,
4289 all_of: None,
4290 any_of: None,
4291 one_of: None,
4292 ..Default::default()
4293 },
4294 );
4295 m
4296 });
4297 let instance = json!({"nested": {}});
4298 let actual: ValidationResult = validate(&schema, &instance);
4299 let expected: ValidationResult = Err(vec![
4300 ValidationError::MissingRequired {
4301 instance_path: JsonPointer::root().push("x"),
4302 property: "x".to_string(),
4303 },
4304 ValidationError::MissingRequired {
4305 instance_path: JsonPointer::root().push("nested").push("y"),
4306 property: "y".to_string(),
4307 },
4308 ]);
4309 assert_eq!(expected, actual);
4310 }
4311
4312 #[test]
4313 fn deeply_nested_instance_does_not_stack_overflow() {
4314 const DEPTH: usize = 200;
4315 let mut inner: JsonSchema = JsonSchema {
4316 type_: Some("object".to_string()),
4317 properties: {
4318 let mut m = BTreeMap::new();
4319 m.insert(
4320 "value".to_string(),
4321 JsonSchema {
4322 type_: Some("string".to_string()),
4323 ..Default::default()
4324 },
4325 );
4326 m
4327 },
4328 required: Some(vec!["value".to_string()]),
4329 ..Default::default()
4330 };
4331 for _ in 0..DEPTH {
4332 let mut wrap: JsonSchema = JsonSchema {
4333 type_: Some("object".to_string()),
4334 properties: BTreeMap::new(),
4335 required: Some(vec!["child".to_string()]),
4336 ..Default::default()
4337 };
4338 wrap.properties.insert("child".to_string(), inner);
4339 inner = wrap;
4340 }
4341 let mut instance_value: serde_json::Value = {
4342 let mut leaf = serde_json::Map::new();
4343 leaf.insert(
4344 "value".to_string(),
4345 serde_json::Value::String("ok".to_string()),
4346 );
4347 serde_json::Value::Object(leaf)
4348 };
4349 for _ in 0..DEPTH {
4350 let mut obj = serde_json::Map::new();
4351 obj.insert("child".to_string(), instance_value);
4352 instance_value = serde_json::Value::Object(obj);
4353 }
4354 let actual: ValidationResult = validate(&inner, &instance_value);
4355 let expected: ValidationResult = Ok(());
4356 assert_eq!(expected, actual);
4357 }
4358
4359 #[test]
4360 fn pointer_escaping() {
4361 let schema = schema_object_with_required(vec!["a/b"], {
4362 let mut m = BTreeMap::new();
4363 m.insert(
4364 "a/b".to_string(),
4365 JsonSchema {
4366 type_: Some("string".to_string()),
4367 ..Default::default()
4368 },
4369 );
4370 m
4371 });
4372 let instance = json!({"a/b": 123});
4373 let actual: ValidationResult = validate(&schema, &instance);
4374 let expected: ValidationResult = Err(vec![ValidationError::ExpectedString {
4375 instance_path: JsonPointer::try_from("/a~1b").unwrap(),
4376 got: "number".to_string(),
4377 }]);
4378 assert_eq!(expected, actual);
4379 }
4380
4381 #[test]
4382 fn validate_string_min_length_exact_passes() {
4383 let schema = JsonSchema {
4384 type_: Some("string".to_string()),
4385 min_length: Some(3),
4386 ..Default::default()
4387 };
4388 let instance = json!("abc");
4389 assert_eq!(Ok(()), validate(&schema, &instance));
4390 }
4391
4392 #[test]
4393 fn validate_string_min_length_below_fails() {
4394 let schema = JsonSchema {
4395 type_: Some("string".to_string()),
4396 min_length: Some(5),
4397 ..Default::default()
4398 };
4399 let instance = json!("hi");
4400 let actual = validate(&schema, &instance);
4401 let expected: ValidationResult = Err(vec![ValidationError::TooShort {
4402 instance_path: JsonPointer::root(),
4403 min_length: 5,
4404 actual_length: 2,
4405 }]);
4406 assert_eq!(expected, actual);
4407 }
4408
4409 #[test]
4410 fn validate_string_min_length_above_passes() {
4411 let schema = JsonSchema {
4412 type_: Some("string".to_string()),
4413 min_length: Some(2),
4414 ..Default::default()
4415 };
4416 let instance = json!("hello");
4417 assert_eq!(Ok(()), validate(&schema, &instance));
4418 }
4419
4420 #[test]
4421 fn validate_string_min_length_absent_any_length_passes() {
4422 let schema = JsonSchema {
4423 type_: Some("string".to_string()),
4424 ..Default::default()
4425 };
4426 let instance = json!("");
4427 assert_eq!(Ok(()), validate(&schema, &instance));
4428 }
4429
4430 #[test]
4431 fn validate_string_max_length_exact_passes() {
4432 let schema = JsonSchema {
4433 type_: Some("string".to_string()),
4434 max_length: Some(5),
4435 ..Default::default()
4436 };
4437 let instance = json!("hello");
4438 assert_eq!(Ok(()), validate(&schema, &instance));
4439 }
4440
4441 #[test]
4442 fn validate_string_max_length_above_fails() {
4443 let schema = JsonSchema {
4444 type_: Some("string".to_string()),
4445 max_length: Some(3),
4446 ..Default::default()
4447 };
4448 let instance = json!("hello");
4449 let actual = validate(&schema, &instance);
4450 let expected: ValidationResult = Err(vec![ValidationError::TooLong {
4451 instance_path: JsonPointer::root(),
4452 max_length: 3,
4453 actual_length: 5,
4454 }]);
4455 assert_eq!(expected, actual);
4456 }
4457
4458 #[test]
4459 fn validate_string_max_length_below_passes() {
4460 let schema = JsonSchema {
4461 type_: Some("string".to_string()),
4462 max_length: Some(10),
4463 ..Default::default()
4464 };
4465 let instance = json!("hi");
4466 assert_eq!(Ok(()), validate(&schema, &instance));
4467 }
4468
4469 #[test]
4470 fn validate_string_max_length_absent_any_length_passes() {
4471 let schema = JsonSchema {
4472 type_: Some("string".to_string()),
4473 ..Default::default()
4474 };
4475 let instance = json!("this is a very long string with no max length constraint");
4476 assert_eq!(Ok(()), validate(&schema, &instance));
4477 }
4478
4479 #[test]
4480 fn validate_string_min_length_zero_allows_empty() {
4481 let schema = JsonSchema {
4482 type_: Some("string".to_string()),
4483 min_length: Some(0),
4484 ..Default::default()
4485 };
4486 let instance = json!("");
4487 assert_eq!(Ok(()), validate(&schema, &instance));
4488 }
4489
4490 #[test]
4491 fn validate_string_max_length_zero_requires_empty() {
4492 let schema = JsonSchema {
4493 type_: Some("string".to_string()),
4494 max_length: Some(0),
4495 ..Default::default()
4496 };
4497 let instance = json!("x");
4498 let actual = validate(&schema, &instance);
4499 let expected: ValidationResult = Err(vec![ValidationError::TooLong {
4500 instance_path: JsonPointer::root(),
4501 max_length: 0,
4502 actual_length: 1,
4503 }]);
4504 assert_eq!(expected, actual);
4505 }
4506
4507 #[test]
4508 fn validate_string_max_length_zero_empty_passes() {
4509 let schema = JsonSchema {
4510 type_: Some("string".to_string()),
4511 max_length: Some(0),
4512 ..Default::default()
4513 };
4514 let instance = json!("");
4515 assert_eq!(Ok(()), validate(&schema, &instance));
4516 }
4517
4518 #[test]
4519 fn validate_string_both_constraints_within_range_passes() {
4520 let schema = JsonSchema {
4521 type_: Some("string".to_string()),
4522 min_length: Some(2),
4523 max_length: Some(10),
4524 ..Default::default()
4525 };
4526 let instance = json!("hello");
4527 assert_eq!(Ok(()), validate(&schema, &instance));
4528 }
4529
4530 #[test]
4531 fn validate_string_below_min_length_with_max_also_set() {
4532 let schema = JsonSchema {
4533 type_: Some("string".to_string()),
4534 min_length: Some(5),
4535 max_length: Some(10),
4536 ..Default::default()
4537 };
4538 let instance = json!("hi");
4539 let actual = validate(&schema, &instance);
4540 let expected: ValidationResult = Err(vec![ValidationError::TooShort {
4541 instance_path: JsonPointer::root(),
4542 min_length: 5,
4543 actual_length: 2,
4544 }]);
4545 assert_eq!(expected, actual);
4546 }
4547
4548 #[test]
4549 fn validate_string_above_max_length_with_min_also_set() {
4550 let schema = JsonSchema {
4551 type_: Some("string".to_string()),
4552 min_length: Some(2),
4553 max_length: Some(4),
4554 ..Default::default()
4555 };
4556 let instance = json!("hello world");
4557 let actual = validate(&schema, &instance);
4558 let expected: ValidationResult = Err(vec![ValidationError::TooLong {
4559 instance_path: JsonPointer::root(),
4560 max_length: 4,
4561 actual_length: 11,
4562 }]);
4563 assert_eq!(expected, actual);
4564 }
4565
4566 #[test]
4567 fn validate_string_min_length_unicode_code_points() {
4568 let schema = JsonSchema {
4570 type_: Some("string".to_string()),
4571 min_length: Some(3),
4572 max_length: Some(3),
4573 ..Default::default()
4574 };
4575 let instance = json!("日本語");
4576 assert_eq!(Ok(()), validate(&schema, &instance));
4577 }
4578
4579 #[test]
4580 fn validate_string_pattern_partial_match_passes() {
4581 let schema = JsonSchema {
4582 type_: Some("string".to_string()),
4583 pattern: Some("a".to_string()),
4584 ..Default::default()
4585 };
4586 let instance = json!("cat");
4587 let expected: ValidationResult = Ok(());
4588 let actual: ValidationResult = validate(&schema, &instance);
4589 assert_eq!(expected, actual);
4590 }
4591
4592 #[test]
4593 fn validate_string_pattern_full_match_passes() {
4594 let schema = JsonSchema {
4595 type_: Some("string".to_string()),
4596 pattern: Some("^[0-9]+$".to_string()),
4597 ..Default::default()
4598 };
4599 let instance = json!("123");
4600 let expected: ValidationResult = Ok(());
4601 let actual: ValidationResult = validate(&schema, &instance);
4602 assert_eq!(expected, actual);
4603 }
4604
4605 #[test]
4606 fn validate_string_pattern_mismatch_fails() {
4607 let schema = JsonSchema {
4608 type_: Some("string".to_string()),
4609 pattern: Some("^[0-9]+$".to_string()),
4610 ..Default::default()
4611 };
4612 let instance = json!("12a3");
4613 let expected: ValidationResult = Err(vec![ValidationError::PatternMismatch {
4614 instance_path: JsonPointer::root(),
4615 pattern: "^[0-9]+$".to_string(),
4616 value: "12a3".to_string(),
4617 }]);
4618 let actual: ValidationResult = validate(&schema, &instance);
4619 assert_eq!(expected, actual);
4620 }
4621
4622 #[test]
4623 fn validate_string_pattern_non_string_instance_only_expected_string() {
4624 let schema = JsonSchema {
4625 type_: Some("string".to_string()),
4626 pattern: Some("^[0-9]+$".to_string()),
4627 ..Default::default()
4628 };
4629 let instance = json!(42);
4630 let expected: ValidationResult = Err(vec![ValidationError::ExpectedString {
4631 instance_path: JsonPointer::root(),
4632 got: "number".to_string(),
4633 }]);
4634 let actual: ValidationResult = validate(&schema, &instance);
4635 assert_eq!(expected, actual);
4636 }
4637
4638 #[test]
4639 fn validate_string_pattern_invalid_regex_in_schema() {
4640 let schema = JsonSchema {
4641 type_: Some("string".to_string()),
4642 pattern: Some("[".to_string()),
4643 ..Default::default()
4644 };
4645 let instance = json!("x");
4646 let expected: ValidationResult = Err(vec![ValidationError::InvalidPatternInSchema {
4647 instance_path: JsonPointer::root(),
4648 pattern: "[".to_string(),
4649 }]);
4650 let actual: ValidationResult = validate(&schema, &instance);
4651 assert_eq!(expected, actual);
4652 }
4653
4654 #[test]
4655 fn validate_string_pattern_and_max_length_multiple_errors() {
4656 let schema = JsonSchema {
4657 type_: Some("string".to_string()),
4658 pattern: Some("^[0-9]+$".to_string()),
4659 max_length: Some(2),
4660 ..Default::default()
4661 };
4662 let instance = json!("12a");
4663 let expected: ValidationResult = Err(vec![
4664 ValidationError::TooLong {
4665 instance_path: JsonPointer::root(),
4666 max_length: 2,
4667 actual_length: 3,
4668 },
4669 ValidationError::PatternMismatch {
4670 instance_path: JsonPointer::root(),
4671 pattern: "^[0-9]+$".to_string(),
4672 value: "12a".to_string(),
4673 },
4674 ]);
4675 let actual: ValidationResult = validate(&schema, &instance);
4676 assert_eq!(expected, actual);
4677 }
4678
4679 #[cfg(feature = "uuid")]
4680 #[test]
4681 fn validate_uuid_format_valid_uuid() {
4682 let schema: JsonSchema =
4683 serde_json::from_str(r#"{"type":"string","format":"uuid"}"#).unwrap();
4684 let instance = json!("550e8400-e29b-41d4-a716-446655440000");
4685 let expected: ValidationResult = Ok(());
4686 let actual: ValidationResult = validate(&schema, &instance);
4687 assert_eq!(expected, actual);
4688 }
4689
4690 #[cfg(feature = "uuid")]
4691 #[test]
4692 fn validate_uuid_format_invalid_string() {
4693 let schema: JsonSchema =
4694 serde_json::from_str(r#"{"type":"string","format":"uuid"}"#).unwrap();
4695 let instance = json!("not-a-uuid");
4696 let expected: ValidationResult = Err(vec![ValidationError::InvalidUuidFormat {
4697 instance_path: JsonPointer::root(),
4698 value: "not-a-uuid".to_string(),
4699 }]);
4700 let actual: ValidationResult = validate(&schema, &instance);
4701 assert_eq!(expected, actual);
4702 }
4703}