Skip to main content

json_schema_rs/validator/
mod.rs

1//! JSON Schema validation: schema + instance → validation result with all errors.
2//!
3//! Collects every validation error (no fail-fast) and returns them in a single result.
4
5mod 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
14/// Returns the JSON type name of the value for use in "got" error messages.
15fn 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
26/// Serializes a JSON value to a string for error display. Never truncates.
27fn value_to_display_string(v: &Value) -> String {
28    serde_json::to_string(v).unwrap_or_else(|_| "?".to_string())
29}
30
31/// Validates a JSON instance against a schema. Collects **all** validation errors
32/// and returns them in a single result (no fail-fast).
33///
34/// Validates using the `type` (object, string, integer, number), `required`, and `properties`
35/// keywords. Resolves fragment-only `$ref` against the root schema; additional properties are allowed.
36///
37/// # Errors
38///
39/// Returns `Err(errors)` when the instance does not conform to the schema, with
40/// one or more [`ValidationError`] values describing each failure.
41///
42/// # Example
43///
44/// ```
45/// use json_schema_rs::{JsonSchema, validate};
46/// use serde_json::json;
47///
48/// let schema: JsonSchema = serde_json::from_str(r#"{"type":"object","properties":{"name":{"type":"string"}}}"#).unwrap();
49/// let instance = json!({"name": "Alice"});
50/// let result = validate(&schema, &instance);
51/// assert!(result.is_ok());
52/// ```
53pub 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                // Push in reverse order so we pop in schema properties order (first key first).
193                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                // additionalProperties: keys not in properties are "additional"
204                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                // minLength / maxLength: count Unicode code points (chars), not bytes.
239                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                // Type absent or not enforced: validate required/properties when instance is object
419                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        // "日本語" has 3 Unicode code points but 9 UTF-8 bytes.
4569        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}