use super::AdditionalProperties;
use super::JsonSchema;
use super::JsonSchemaPrimitiveType;
use super::JsonSchemaType;
use super::parse_tool_input_schema;
use super::parse_tool_input_schema_without_compaction;
use pretty_assertions::assert_eq;
use std::collections::BTreeMap;

// Tests in this section exercise normalization transforms that mutate badly
// formed JSON for consumption by the Responses API.

#[test]
fn parse_tool_input_schema_coerces_boolean_schemas() {
    // Example schema shape:
    // true
    //
    // Expected normalization behavior:
    // - JSON Schema boolean forms are coerced to `{ "type": "string" }`
    //   because the baseline enum model cannot represent boolean-schema
    //   semantics directly.
    let schema = parse_tool_input_schema(&serde_json::json!(true)).expect("parse schema");

    assert_eq!(schema, JsonSchema::string(/*description*/ None));
}

#[test]
fn json_schema_serializes_encrypted_marker() {
    let schema = JsonSchema::string(Some("Secret value".to_string())).with_encrypted();

    assert_eq!(
        serde_json::to_value(schema).expect("serialize schema"),
        serde_json::json!({
            "type": "string",
            "description": "Secret value",
            "encrypted": true,
        })
    );
}

#[test]
fn parse_tool_input_schema_infers_object_shape_and_defaults_properties() {
    // Example schema shape:
    // {
    //   "properties": {
    //     "query": { "description": "search query" }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - `properties` implies an object schema when `type` is omitted.
    // - The child property has no recognized schema hints, so it is coerced to
    //   an empty permissive schema.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "properties": {
            "query": {"description": "search query"}
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(
            BTreeMap::from([("query".to_string(), JsonSchema::default())]),
            /*required*/ None,
            /*additional_properties*/ None
        )
    );
}

#[test]
fn parse_tool_input_schema_coerces_unrecognized_object_schema_to_empty_schema() {
    // Example schema shape:
    // {
    //   "description": "Ticket identifier",
    //   "title": "Ticket ID"
    // }
    //
    // Expected normalization behavior:
    // - Object schemas with no recognized schema hints are treated as
    //   malformed and coerced to the empty permissive schema.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "description": "Ticket identifier",
        "title": "Ticket ID"
    }))
    .expect("parse schema");

    assert_eq!(schema, JsonSchema::default());
}

#[test]
fn parse_tool_input_schema_preserves_integer_and_defaults_array_items() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": {
    //     "page": { "type": "integer" },
    //     "tags": { "type": "array" }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - `"integer"` is preserved distinctly from `"number"`.
    // - Arrays missing `items` receive a permissive string `items` schema.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "page": {"type": "integer"},
            "tags": {"type": "array"}
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(
            BTreeMap::from([
                (
                    "page".to_string(),
                    JsonSchema::integer(/*description*/ None),
                ),
                (
                    "tags".to_string(),
                    JsonSchema::array(
                        JsonSchema::string(/*description*/ None),
                        /*description*/ None,
                    )
                ),
            ]),
            /*required*/ None,
            /*additional_properties*/ None
        )
    );
}

#[test]
fn parse_tool_input_schema_sanitizes_additional_properties_schema() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "additionalProperties": {
    //     "required": ["value"],
    //     "properties": {
    //       "value": {
    //         "anyOf": [
    //           { "type": "string" },
    //           { "type": "number" }
    //         ]
    //       }
    //     }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - `additionalProperties` schema objects are recursively sanitized.
    // - The nested schema is normalized into the current object/anyOf form.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "additionalProperties": {
            "required": ["value"],
            "properties": {
                "value": {"anyOf": [{"type": "string"}, {"type": "number"}]}
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(
            BTreeMap::new(),
            /*required*/ None,
            Some(AdditionalProperties::Schema(Box::new(JsonSchema::object(
                BTreeMap::from([(
                    "value".to_string(),
                    JsonSchema::any_of(
                        vec![
                            JsonSchema::string(/*description*/ None),
                            JsonSchema::number(/*description*/ None),
                        ],
                        /*description*/ None,
                    ),
                )]),
                Some(vec!["value".to_string()]),
                /*additional_properties*/ None,
            ))))
        )
    );
}

#[test]
fn parse_tool_input_schema_infers_object_shape_from_boolean_additional_properties_only() {
    // Example schema shape:
    // {
    //   "additionalProperties": false
    // }
    //
    // Expected normalization behavior:
    // - `additionalProperties` implies an object schema when `type` is omitted.
    // - The boolean `additionalProperties` setting is preserved.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "additionalProperties": false
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(BTreeMap::new(), /*required*/ None, Some(false.into()))
    );
}

#[test]
fn parse_tool_input_schema_infers_number_from_numeric_keywords() {
    // Example schema shape:
    // {
    //   "minimum": 1
    // }
    //
    // Expected normalization behavior:
    // - Numeric constraint keywords imply a number schema when `type` is
    //   omitted.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "minimum": 1
    }))
    .expect("parse schema");

    assert_eq!(schema, JsonSchema::number(/*description*/ None));
}

#[test]
fn parse_tool_input_schema_infers_number_from_multiple_of() {
    // Example schema shape:
    // {
    //   "multipleOf": 5
    // }
    //
    // Expected normalization behavior:
    // - `multipleOf` follows the same numeric-keyword inference path as
    //   `minimum` / `maximum`.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "multipleOf": 5
    }))
    .expect("parse schema");

    assert_eq!(schema, JsonSchema::number(/*description*/ None));
}

#[test]
fn parse_tool_input_schema_infers_string_from_enum_const_and_format_keywords() {
    // Example schema shapes:
    // { "enum": ["fast", "safe"] }
    // { "const": "file" }
    // { "format": "date-time" }
    //
    // Expected normalization behavior:
    // - `enum` and `const` normalize into explicit string-enum schemas.
    // - `format` still falls back to a plain string schema.
    let enum_schema = parse_tool_input_schema(&serde_json::json!({
        "enum": ["fast", "safe"]
    }))
    .expect("parse enum schema");
    let const_schema = parse_tool_input_schema(&serde_json::json!({
        "const": "file"
    }))
    .expect("parse const schema");
    let format_schema = parse_tool_input_schema(&serde_json::json!({
        "format": "date-time"
    }))
    .expect("parse format schema");

    assert_eq!(
        enum_schema,
        JsonSchema::string_enum(
            vec![serde_json::json!("fast"), serde_json::json!("safe")],
            /*description*/ None,
        )
    );
    assert_eq!(
        const_schema,
        JsonSchema::string_enum(vec![serde_json::json!("file")], /*description*/ None)
    );
    assert_eq!(format_schema, JsonSchema::string(/*description*/ None));
}

#[test]
fn parse_tool_input_schema_preserves_empty_schema() {
    // Example schema shape:
    // {}
    //
    // Expected normalization behavior:
    // - An empty JSON Schema is already a valid permissive schema, so it stays
    //   empty rather than being rewritten as an object schema.
    let schema = parse_tool_input_schema(&serde_json::json!({})).expect("parse schema");

    assert_eq!(schema, JsonSchema::default());
}

#[test]
fn parse_tool_input_schema_preserves_nested_empty_schema() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": {
    //     "metadata": {
    //       "properties": {
    //         "extra": {}
    //       }
    //     }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - The sanitizer recurses through nested object properties.
    // - The innermost `extra` field is an empty JSON Schema and stays empty.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "metadata": {
                "properties": {
                    "extra": {}
                }
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(
            BTreeMap::from([(
                "metadata".to_string(),
                JsonSchema::object(
                    BTreeMap::from([("extra".to_string(), JsonSchema::default())]),
                    /*required*/ None,
                    /*additional_properties*/ None,
                )
            )]),
            /*required*/ None,
            /*additional_properties*/ None,
        )
    );
}

#[test]
fn parse_tool_input_schema_infers_array_from_prefix_items() {
    // Example schema shape:
    // {
    //   "prefixItems": [
    //     { "type": "string" }
    //   ]
    // }
    //
    // Expected normalization behavior:
    // - `prefixItems` implies an array schema when `type` is omitted.
    // - The normalized result is stored as a regular array schema with string
    //   items.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "prefixItems": [
            {"type": "string"}
        ]
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::array(
            JsonSchema::string(/*description*/ None),
            /*description*/ None,
        )
    );
}

#[test]
fn parse_tool_input_schema_preserves_boolean_additional_properties_on_inferred_object() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": {
    //     "metadata": {
    //       "additionalProperties": true
    //     }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - The nested `metadata` schema is inferred to be an object because it has
    //   `additionalProperties`.
    // - `additionalProperties: true` is preserved rather than rewritten.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "metadata": {
                "additionalProperties": true
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(
            BTreeMap::from([(
                "metadata".to_string(),
                JsonSchema::object(BTreeMap::new(), /*required*/ None, Some(true.into())),
            )]),
            /*required*/ None,
            /*additional_properties*/ None
        )
    );
}

#[test]
fn parse_tool_input_schema_infers_object_shape_from_schema_additional_properties_only() {
    // Example schema shape:
    // {
    //   "additionalProperties": {
    //     "type": "string"
    //   }
    // }
    //
    // Expected normalization behavior:
    // - A schema-valued `additionalProperties` also implies an object schema
    //   when `type` is omitted.
    // - The nested schema is preserved as the object's
    //   `additionalProperties` definition.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "additionalProperties": {
            "type": "string"
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(
            BTreeMap::new(),
            /*required*/ None,
            Some(JsonSchema::string(/*description*/ None).into())
        )
    );
}

#[test]
fn parse_tool_input_schema_rewrites_const_to_single_value_enum() {
    // Example schema shape:
    // {
    //   "const": "tagged"
    // }
    //
    // Expected normalization behavior:
    // - `const` is rewritten through the sanitizer's `map.remove("const")`
    //   path into an equivalent single-value string enum schema.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "const": "tagged"
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::string_enum(vec![serde_json::json!("tagged")], /*description*/ None)
    );
}

#[test]
fn parse_tool_input_schema_rejects_singleton_null_type() {
    let err = parse_tool_input_schema(&serde_json::json!({
        "type": "null"
    }))
    .expect_err("singleton null should be rejected");

    assert!(
        err.to_string()
            .contains("tool input schema must not be a singleton null type"),
        "unexpected error: {err}"
    );
}

#[test]
fn parse_tool_input_schema_fills_default_properties_for_nullable_object_union() {
    // Example schema shape:
    // {
    //   "type": ["object", "null"]
    // }
    //
    // Expected normalization behavior:
    // - The full union is preserved.
    // - Object members of the union still receive default `properties`.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": ["object", "null"]
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema {
            schema_type: Some(JsonSchemaType::Multiple(vec![
                JsonSchemaPrimitiveType::Object,
                JsonSchemaPrimitiveType::Null,
            ])),
            properties: Some(BTreeMap::new()),
            ..Default::default()
        }
    );
}

#[test]
fn parse_tool_input_schema_fills_default_items_for_nullable_array_union() {
    // Example schema shape:
    // {
    //   "type": ["array", "null"]
    // }
    //
    // Expected normalization behavior:
    // - The full union is preserved.
    // - Array members of the union still receive default `items`.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": ["array", "null"]
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema {
            schema_type: Some(JsonSchemaType::Multiple(vec![
                JsonSchemaPrimitiveType::Array,
                JsonSchemaPrimitiveType::Null,
            ])),
            items: Some(Box::new(JsonSchema::string(/*description*/ None))),
            ..Default::default()
        }
    );
}

// Schemas that should be preserved for Responses API compatibility rather than
// being rewritten into a different shape.

#[test]
fn parse_tool_input_schema_preserves_nested_nullable_any_of_shape() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": {
    //     "open": {
    //       "anyOf": [
    //         {
    //           "type": "array",
    //           "items": {
    //             "type": "object",
    //             "properties": {
    //               "ref_id": { "type": "string" },
    //               "lineno": { "anyOf": [{ "type": "integer" }, { "type": "null" }] }
    //             },
    //             "required": ["ref_id"],
    //             "additionalProperties": false
    //           }
    //         },
    //         { "type": "null" }
    //       ]
    //     }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - Nested nullable `anyOf` shapes are preserved all the way down.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "open": {
                "anyOf": [
                    {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "ref_id": {"type": "string"},
                                "lineno": {"anyOf": [{"type": "integer"}, {"type": "null"}]}
                            },
                            "required": ["ref_id"],
                            "additionalProperties": false
                        }
                    },
                    {"type": "null"}
                ]
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(
            BTreeMap::from([(
                "open".to_string(),
                JsonSchema::any_of(
                    vec![
                        JsonSchema::array(
                            JsonSchema::object(
                                BTreeMap::from([
                                    (
                                        "lineno".to_string(),
                                        JsonSchema::any_of(
                                            vec![
                                                JsonSchema::integer(/*description*/ None),
                                                JsonSchema::null(/*description*/ None),
                                            ],
                                            /*description*/ None,
                                        ),
                                    ),
                                    (
                                        "ref_id".to_string(),
                                        JsonSchema::string(/*description*/ None),
                                    ),
                                ]),
                                Some(vec!["ref_id".to_string()]),
                                Some(false.into()),
                            ),
                            /*description*/ None,
                        ),
                        JsonSchema::null(/*description*/ None),
                    ],
                    /*description*/ None,
                ),
            ),]),
            /*required*/ None,
            /*additional_properties*/ None
        )
    );
}

#[test]
fn parse_tool_input_schema_preserves_nested_nullable_type_union() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": {
    //     "nickname": {
    //       "type": ["string", "null"],
    //       "description": "Optional nickname"
    //     }
    //   },
    //   "required": ["nickname"],
    //   "additionalProperties": false
    // }
    //
    // Expected normalization behavior:
    // - The nested property keeps the explicit `["string", "null"]` union.
    // - The object-level `required` and `additionalProperties: false` stay intact.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "nickname": {
                "type": ["string", "null"],
                "description": "Optional nickname"
            }
        },
        "required": ["nickname"],
        "additionalProperties": false
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(
            BTreeMap::from([(
                "nickname".to_string(),
                JsonSchema {
                    schema_type: Some(JsonSchemaType::Multiple(vec![
                        JsonSchemaPrimitiveType::String,
                        JsonSchemaPrimitiveType::Null,
                    ])),
                    description: Some("Optional nickname".to_string()),
                    ..Default::default()
                },
            )]),
            Some(vec!["nickname".to_string()]),
            Some(false.into()),
        )
    );
}

#[test]
fn parse_tool_input_schema_preserves_nested_any_of_property() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": {
    //     "query": {
    //       "anyOf": [
    //         { "type": "string" },
    //         { "type": "number" }
    //       ]
    //     }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - The nested `anyOf` is preserved rather than flattened into a single
    //   fallback type.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "query": {
                "anyOf": [
                    { "type": "string" },
                    { "type": "number" }
                ]
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(
            BTreeMap::from([(
                "query".to_string(),
                JsonSchema::any_of(
                    vec![
                        JsonSchema::string(/*description*/ None),
                        JsonSchema::number(/*description*/ None),
                    ],
                    /*description*/ None,
                ),
            )]),
            /*required*/ None,
            /*additional_properties*/ None
        )
    );
}

#[test]
fn parse_tool_input_schema_preserves_nested_one_of_property() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": {
    //     "query": {
    //       "oneOf": [
    //         { "const": "exact" },
    //         { "type": "number" }
    //       ]
    //     }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - The nested `oneOf` is preserved.
    // - Child variants are recursively sanitized, including `const` to `enum`.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "query": {
                "oneOf": [
                    { "const": "exact" },
                    { "type": "number" }
                ]
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(
            BTreeMap::from([(
                "query".to_string(),
                JsonSchema::one_of(
                    vec![
                        JsonSchema::string_enum(
                            vec![serde_json::json!("exact")],
                            /*description*/ None,
                        ),
                        JsonSchema::number(/*description*/ None),
                    ],
                    /*description*/ None,
                ),
            )]),
            /*required*/ None,
            /*additional_properties*/ None
        )
    );
}

#[test]
fn parse_tool_input_schema_preserves_nested_all_of_property() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": {
    //     "query": {
    //       "allOf": [
    //         { "type": "string" },
    //         { "description": "unrecognized by itself" }
    //       ]
    //     }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - The nested `allOf` is preserved structurally rather than flattened.
    // - Child variants are recursively sanitized.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "query": {
                "allOf": [
                    { "type": "string" },
                    { "description": "unrecognized by itself" }
                ]
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(
            BTreeMap::from([(
                "query".to_string(),
                JsonSchema::all_of(
                    vec![
                        JsonSchema::string(/*description*/ None),
                        JsonSchema::default(),
                    ],
                    /*description*/ None,
                ),
            )]),
            /*required*/ None,
            /*additional_properties*/ None
        )
    );
}

#[test]
fn parse_tool_input_schema_preserves_type_unions_without_rewriting_to_any_of() {
    // Example schema shape:
    // {
    //   "type": ["string", "null"],
    //   "description": "optional string"
    // }
    //
    // Expected normalization behavior:
    // - Explicit type unions are preserved as unions rather than rewritten to
    //   `anyOf`.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": ["string", "null"],
        "description": "optional string"
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema {
            schema_type: Some(JsonSchemaType::Multiple(vec![
                JsonSchemaPrimitiveType::String,
                JsonSchemaPrimitiveType::Null,
            ])),
            description: Some("optional string".to_string()),
            ..Default::default()
        }
    );
}

#[test]
fn parse_tool_input_schema_preserves_explicit_enum_type_union() {
    // Example schema shape:
    // {
    //   "type": ["string", "null"],
    //   "enum": ["short", "medium", "long"],
    //   "description": "optional response length"
    // }
    //
    // Expected normalization behavior:
    // - The explicit string/null union is preserved alongside the enum values.
    let schema = super::parse_tool_input_schema(&serde_json::json!({
        "type": ["string", "null"],
        "enum": ["short", "medium", "long"],
        "description": "optional response length"
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema {
            schema_type: Some(JsonSchemaType::Multiple(vec![
                JsonSchemaPrimitiveType::String,
                JsonSchemaPrimitiveType::Null,
            ])),
            description: Some("optional response length".to_string()),
            enum_values: Some(vec![
                serde_json::json!("short"),
                serde_json::json!("medium"),
                serde_json::json!("long"),
            ]),
            ..Default::default()
        }
    );
}

fn many_string_properties(count: usize) -> serde_json::Map<String, serde_json::Value> {
    (0..count)
        .map(|index| {
            (
                format!("field_{index:03}"),
                serde_json::json!({ "type": "string" }),
            )
        })
        .collect()
}

#[test]
fn parse_large_tool_input_schema_compacts_descriptions_only_on_default_path() {
    let input_schema = serde_json::json!({
        "type": "object",
        "description": "x".repeat(4_500),
        "properties": {
            "metadata": {
                "$ref": "#/$defs/metadata"
            }
        },
        "$defs": {
            "metadata": {
                "type": "string",
                "description": "Metadata value"
            }
        }
    });
    let schema = parse_tool_input_schema(&input_schema).expect("parse schema");

    assert_eq!(
        serde_json::to_value(schema).expect("serialize schema"),
        serde_json::json!({
            "type": "object",
            "properties": {
                "metadata": {
                    "$ref": "#/$defs/metadata"
                }
            },
            "$defs": {
                "metadata": {
                    "type": "string"
                }
            }
        })
    );

    let schema = parse_tool_input_schema_without_compaction(&input_schema).expect("parse schema");
    assert_eq!(
        serde_json::to_value(schema).expect("serialize schema"),
        serde_json::json!({
            "type": "object",
            "description": "x".repeat(4_500),
            "properties": {
                "metadata": {
                    "$ref": "#/$defs/metadata"
                }
            },
            "$defs": {
                "metadata": {
                    "type": "string",
                    "description": "Metadata value"
                }
            }
        })
    );
}

#[test]
fn parse_large_tool_input_schema_ignores_dropped_metadata_for_budget() {
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "event": {
                "type": "object",
                "title": "Calendar event",
                "properties": {
                    "recurrence": {
                        "type": "object",
                        "examples": [
                            {
                                "payload": "x".repeat(4_500)
                            }
                        ],
                        "properties": {
                            "pattern": {
                                "type": "string",
                                "title": "Recurrence pattern"
                            }
                        }
                    }
                }
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        serde_json::to_value(schema).expect("serialize schema"),
        serde_json::json!({
            "type": "object",
            "properties": {
                "event": {
                    "type": "object",
                    "properties": {
                        "recurrence": {
                            "type": "object",
                            "properties": {
                                "pattern": {
                                    "type": "string"
                                }
                            }
                        }
                    }
                }
            }
        })
    );
}

#[test]
fn parse_large_tool_input_schema_stops_after_dropping_root_definitions_when_under_budget() {
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "description": "x".repeat(4_500),
        "properties": {
            "event": {
                "type": "object",
                "description": "Calendar event",
                "properties": {
                    "recurrence": {
                        "type": "object",
                        "description": "Recurrence settings",
                        "properties": {
                            "pattern": {
                                "type": "string",
                                "description": "Recurrence pattern"
                            }
                        }
                    }
                }
            },
            "metadata": {
                "$ref": "#/$defs/metadata"
            }
        },
        "$defs": {
            "metadata": {
                "type": "object",
                "description": "metadata object",
                "properties": many_string_properties(/*count*/ 300)
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        serde_json::to_value(schema).expect("serialize schema"),
        serde_json::json!({
            "type": "object",
            "properties": {
                "event": {
                    "type": "object",
                    "properties": {
                        "recurrence": {
                            "type": "object",
                            "properties": {
                                "pattern": {
                                    "type": "string"
                                }
                            }
                        }
                    }
                },
                "metadata": {}
            }
        })
    );
}

#[test]
fn parse_large_tool_input_schema_strips_descriptions_without_removing_description_property() {
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "description": "x".repeat(4_500),
        "properties": {
            "description": {
                "type": "string",
                "description": "User-facing description value"
            },
            "metadata": {
                "type": "object",
                "description": "Metadata object",
                "properties": {
                    "label": {
                        "type": "string",
                        "description": "Metadata label"
                    }
                }
            },
            "tags": {
                "type": "array",
                "description": "Tag list",
                "items": {
                    "type": "string",
                    "description": "Tag value"
                }
            },
            "extras": {
                "type": "object",
                "additionalProperties": {
                    "type": "string",
                    "description": "Extra value"
                }
            },
            "choice": {
                "description": "Choice value",
                "anyOf": [
                    {
                        "type": "string",
                        "description": "String choice"
                    },
                    {
                        "type": "number",
                        "description": "Number choice"
                    }
                ]
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        serde_json::to_value(schema).expect("serialize schema"),
        serde_json::json!({
            "type": "object",
            "properties": {
                "choice": {
                    "anyOf": [
                        {
                            "type": "string"
                        },
                        {
                            "type": "number"
                        }
                    ]
                },
                "description": {
                    "type": "string"
                },
                "extras": {
                    "type": "object",
                    "properties": {},
                    "additionalProperties": {
                        "type": "string"
                    }
                },
                "metadata": {
                    "type": "object",
                    "properties": {
                        "label": {
                            "type": "string"
                        }
                    }
                },
                "tags": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                }
            }
        })
    );
}

#[test]
fn parse_large_tool_input_schema_prunes_compositions_as_last_resort() {
    for composition_key in super::COMPOSITION_SCHEMA_KEYS {
        let variants = vec![
            serde_json::json!({
                "type": "string",
                "enum": ["first ".repeat(400)]
            }),
            serde_json::json!({
                "type": "string",
                "enum": ["second ".repeat(400)]
            }),
            serde_json::json!({
                "type": "string",
                "enum": ["third ".repeat(400)]
            }),
        ];

        let mut choice = serde_json::Map::new();
        choice.insert(
            composition_key.to_string(),
            serde_json::Value::Array(variants),
        );
        let schema = parse_tool_input_schema(&serde_json::json!({
            "type": "object",
            "properties": {
                "choice": choice
            }
        }))
        .expect("parse schema");

        assert_eq!(
            serde_json::to_value(schema).expect("serialize schema"),
            serde_json::json!({
                "type": "object",
                "properties": {
                    "choice": {}
                }
            })
        );
    }
}

#[test]
fn parse_large_tool_input_schema_prunes_single_composition_variant_if_still_over_budget() {
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "choice": {
                "anyOf": [
                    {
                        "type": "string",
                        "enum": ["x".repeat(4_500)]
                    }
                ]
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        serde_json::to_value(schema).expect("serialize schema"),
        serde_json::json!({
            "type": "object",
            "properties": {
                "choice": {}
            }
        })
    );
}

#[test]
fn parse_large_tool_input_schema_preserves_object_enum_literal_descriptions() {
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "description": "x".repeat(4_500),
        "properties": {
            "choice": {
                "enum": [
                    {
                        "description": "first literal",
                        "id": 1
                    },
                    {
                        "description": "second literal",
                        "id": 2
                    }
                ]
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        serde_json::to_value(schema).expect("serialize schema"),
        serde_json::json!({
            "type": "object",
            "properties": {
                "choice": {
                    "type": "string",
                    "enum": [
                        {
                            "description": "first literal",
                            "id": 1
                        },
                        {
                            "description": "second literal",
                            "id": 2
                        }
                    ]
                }
            }
        })
    );
}

#[test]
fn collapse_deep_schema_objects_traverses_schema_children() {
    let mut schema = serde_json::json!({
        "type": "object",
        "properties": {
            "object_parent": {
                "type": "object",
                "properties": {
                    "complex": {
                        "type": "object",
                        "properties": {
                            "nested": {
                                "type": "object",
                                "properties": {
                                    "leaf": { "type": "string" }
                                }
                            }
                        }
                    },
                    "scalar": {
                        "type": "string"
                    }
                }
            },
            "array_parent": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "nested": {
                            "type": "object",
                            "properties": {
                                "leaf": { "type": "string" }
                            }
                        }
                    }
                }
            },
            "map_parent": {
                "type": "object",
                "additionalProperties": {
                    "type": "object",
                    "properties": {
                        "nested": {
                            "type": "object",
                            "properties": {
                                "leaf": { "type": "string" }
                            }
                        }
                    }
                }
            },
            "union_parent": {
                "anyOf": [
                    {
                        "type": "object",
                        "properties": {
                            "nested": {
                                "type": "object",
                                "properties": {
                                    "leaf": { "type": "string" }
                                }
                            }
                        }
                    },
                    { "type": "string" }
                ]
            }
        }
    });

    super::collapse_deep_schema_objects(&mut schema, /*depth*/ 0);

    assert_eq!(
        schema,
        serde_json::json!({
            "type": "object",
            "properties": {
                "object_parent": {
                    "type": "object",
                    "properties": {
                        "complex": {
                            "type": "object",
                            "properties": {
                                "nested": {}
                            }
                        },
                        "scalar": {
                            "type": "string"
                        }
                    }
                },
                "array_parent": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "nested": {}
                        }
                    }
                },
                "map_parent": {
                    "type": "object",
                    "additionalProperties": {
                        "type": "object",
                        "properties": {
                            "nested": {}
                        }
                    }
                },
                "union_parent": {
                    "anyOf": [
                        {
                            "type": "object",
                            "properties": {
                                "nested": {}
                            }
                        },
                        { "type": "string" }
                    ]
                }
            }
        })
    );
}

#[test]
fn parse_tool_input_schema_preserves_string_enum_constraints() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": {
    //     "response_length": { "type": "enum", "enum": ["short", "medium", "long"] },
    //     "kind": { "type": "const", "const": "tagged" },
    //     "scope": { "type": "enum", "enum": ["one", "two"] }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - Legacy `type: "enum"` and `type: "const"` inputs are normalized into
    //   the current string-enum representation.
    let schema = super::parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "response_length": {
                "type": "enum",
                "enum": ["short", "medium", "long"]
            },
            "kind": {
                "type": "const",
                "const": "tagged"
            },
            "scope": {
                "type": "enum",
                "enum": ["one", "two"]
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema::object(
            BTreeMap::from([
                (
                    "kind".to_string(),
                    JsonSchema::string_enum(
                        vec![serde_json::json!("tagged")],
                        /*description*/ None,
                    ),
                ),
                (
                    "response_length".to_string(),
                    JsonSchema::string_enum(
                        vec![
                            serde_json::json!("short"),
                            serde_json::json!("medium"),
                            serde_json::json!("long"),
                        ],
                        /*description*/ None,
                    ),
                ),
                (
                    "scope".to_string(),
                    JsonSchema::string_enum(
                        vec![serde_json::json!("one"), serde_json::json!("two")],
                        /*description*/ None,
                    ),
                ),
            ]),
            /*required*/ None,
            /*additional_properties*/ None
        )
    );
}

#[test]
fn parse_tool_input_schema_preserves_refs_and_prunes_unreachable_defs() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": { "user": { "$ref": "#/$defs/User" } },
    //   "$defs": {
    //     "User": { "type": "object", "properties": { "name": { "type": "string" } } },
    //     "Unused": { "type": "string" }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - Local `$ref` is preserved as a schema hint.
    // - Reachable `$defs` entries stay attached to the root schema.
    // - Unreachable `$defs` entries are pruned.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "user": {"$ref": "#/$defs/User"}
        },
        "$defs": {
            "User": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"}
                }
            },
            "Unused": {"type": "string"}
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema {
            schema_type: Some(JsonSchemaType::Single(JsonSchemaPrimitiveType::Object)),
            properties: Some(BTreeMap::from([(
                "user".to_string(),
                JsonSchema {
                    schema_ref: Some("#/$defs/User".to_string()),
                    ..Default::default()
                },
            )])),
            defs: Some(BTreeMap::from([(
                "User".to_string(),
                JsonSchema::object(
                    BTreeMap::from([(
                        "name".to_string(),
                        JsonSchema::string(/*description*/ None),
                    )]),
                    /*required*/ None,
                    /*additional_properties*/ None,
                ),
            )])),
            ..Default::default()
        }
    );
}

#[test]
fn parse_tool_input_schema_preserves_refs_from_properties_named_def_tables() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": {
    //     "$defs": { "$ref": "#/$defs/User" }
    //   },
    //   "$defs": { "User": { "type": "string" }, "Unused": { "type": "boolean" } }
    // }
    //
    // Expected normalization behavior:
    // - A property named like the `$defs` keyword is treated as a user field
    //   while traversing `properties`.
    // - Refs from that property schema still mark root definitions reachable.
    // - Unreferenced root definitions are still pruned.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "$defs": {"$ref": "#/$defs/User"}
        },
        "$defs": {
            "User": {"type": "string"},
            "Unused": {"type": "boolean"}
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema {
            schema_type: Some(JsonSchemaType::Single(JsonSchemaPrimitiveType::Object)),
            properties: Some(BTreeMap::from([(
                "$defs".to_string(),
                JsonSchema {
                    schema_ref: Some("#/$defs/User".to_string()),
                    ..Default::default()
                },
            )])),
            defs: Some(BTreeMap::from([(
                "User".to_string(),
                JsonSchema::string(/*description*/ None),
            )])),
            ..Default::default()
        }
    );
}

#[test]
fn parse_tool_input_schema_collects_refs_from_schema_child_keywords() {
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "items_holder": {
                "type": "array",
                "items": {"$ref": "#/$defs/Item"}
            },
            "map_holder": {
                "type": "object",
                "additionalProperties": {"$ref": "#/$defs/Extra"}
            },
            "choice": {
                "anyOf": [
                    {"$ref": "#/$defs/Choice"},
                    {"type": "string"}
                ]
            },
            "exclusive_choice": {
                "oneOf": [
                    {"$ref": "#/$defs/ExclusiveChoice"},
                    {"type": "integer"}
                ]
            },
            "combined": {
                "allOf": [
                    {"$ref": "#/$defs/Combined"},
                    {"type": "object"}
                ]
            }
        },
        "$defs": {
            "Combined": {"type": "object"},
            "Choice": {"type": "boolean"},
            "ExclusiveChoice": {"type": "null"},
            "Extra": {"type": "number"},
            "Item": {"type": "string"},
            "Unused": {"type": "null"}
        }
    }))
    .expect("parse schema");

    assert_eq!(
        serde_json::to_value(schema).expect("serialize schema"),
        serde_json::json!({
            "type": "object",
            "properties": {
                "choice": {
                    "anyOf": [
                        {"$ref": "#/$defs/Choice"},
                        {"type": "string"}
                    ]
                },
                "combined": {
                    "allOf": [
                        {"$ref": "#/$defs/Combined"},
                        {"type": "object", "properties": {}}
                    ]
                },
                "exclusive_choice": {
                    "oneOf": [
                        {"$ref": "#/$defs/ExclusiveChoice"},
                        {"type": "integer"}
                    ]
                },
                "items_holder": {
                    "type": "array",
                    "items": {"$ref": "#/$defs/Item"}
                },
                "map_holder": {
                    "type": "object",
                    "properties": {},
                    "additionalProperties": {"$ref": "#/$defs/Extra"}
                }
            },
            "$defs": {
                "Combined": {"type": "object", "properties": {}},
                "Choice": {"type": "boolean"},
                "ExclusiveChoice": {"type": "null"},
                "Extra": {"type": "number"},
                "Item": {"type": "string"}
            }
        })
    );
}

#[test]
fn parse_tool_input_schema_handles_cyclic_local_refs() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": { "node": { "$ref": "#/$defs/Node" } },
    //   "$defs": {
    //     "Node": {
    //       "type": "object",
    //       "properties": { "next": { "$ref": "#/$defs/Node" } }
    //     }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - Recursive refs are preserved.
    // - Pruning traversal terminates after visiting each local target once.
    // - Responses API handles this recursive local-ref shape correctly.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "node": {"$ref": "#/$defs/Node"}
        },
        "$defs": {
            "Node": {
                "type": "object",
                "properties": {
                    "next": {"$ref": "#/$defs/Node"}
                }
            }
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema {
            schema_type: Some(JsonSchemaType::Single(JsonSchemaPrimitiveType::Object)),
            properties: Some(BTreeMap::from([(
                "node".to_string(),
                JsonSchema {
                    schema_ref: Some("#/$defs/Node".to_string()),
                    ..Default::default()
                },
            )])),
            defs: Some(BTreeMap::from([(
                "Node".to_string(),
                JsonSchema::object(
                    BTreeMap::from([(
                        "next".to_string(),
                        JsonSchema {
                            schema_ref: Some("#/$defs/Node".to_string()),
                            ..Default::default()
                        },
                    )]),
                    /*required*/ None,
                    /*additional_properties*/ None,
                ),
            )])),
            ..Default::default()
        }
    );
}

#[test]
fn parse_tool_input_schema_preserves_legacy_definitions() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": { "user": { "$ref": "#/definitions/User" } },
    //   "definitions": {
    //     "User": { "type": "object", "properties": { "profile": { "$ref": "#/definitions/Profile" } } },
    //     "Profile": { "type": "object", "properties": { "name": { "type": "string" } } }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - Codex preserves legacy `definitions`.
    // - Reachability follows refs through the legacy definition table.
    // - Unreachable legacy definition entries are pruned.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "user": {"$ref": "#/definitions/User"}
        },
        "definitions": {
            "User": {
                "type": "object",
                "properties": {
                    "profile": {"$ref": "#/definitions/Profile"}
                }
            },
            "Profile": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"}
                }
            },
            "Unused": {"type": "string"}
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema {
            schema_type: Some(JsonSchemaType::Single(JsonSchemaPrimitiveType::Object)),
            properties: Some(BTreeMap::from([(
                "user".to_string(),
                JsonSchema {
                    schema_ref: Some("#/definitions/User".to_string()),
                    ..Default::default()
                },
            )])),
            definitions: Some(BTreeMap::from([
                (
                    "Profile".to_string(),
                    JsonSchema::object(
                        BTreeMap::from([(
                            "name".to_string(),
                            JsonSchema::string(/*description*/ None),
                        )]),
                        /*required*/ None,
                        /*additional_properties*/ None,
                    ),
                ),
                (
                    "User".to_string(),
                    JsonSchema::object(
                        BTreeMap::from([(
                            "profile".to_string(),
                            JsonSchema {
                                schema_ref: Some("#/definitions/Profile".to_string()),
                                ..Default::default()
                            },
                        )]),
                        /*required*/ None,
                        /*additional_properties*/ None,
                    ),
                ),
            ])),
            ..Default::default()
        }
    );
}

#[test]
fn parse_tool_input_schema_preserves_unresolved_and_external_refs() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": {
    //     "missing": { "$ref": "#/$defs/Missing" },
    //     "remote": { "$ref": "https://example.com/schema.json" }
    //   },
    //   "$defs": { "Unused": { "type": "string" } }
    // }
    //
    // Expected normalization behavior:
    // - Unresolved local refs and external refs are preserved.
    // - Unreachable local definitions are still pruned.
    // - Responses API handles these refs correctly during downstream validation.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "missing": {"$ref": "#/$defs/Missing"},
            "remote": {"$ref": "https://example.com/schema.json"}
        },
        "$defs": {
            "Unused": {"type": "string"}
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema {
            schema_type: Some(JsonSchemaType::Single(JsonSchemaPrimitiveType::Object)),
            properties: Some(BTreeMap::from([
                (
                    "missing".to_string(),
                    JsonSchema {
                        schema_ref: Some("#/$defs/Missing".to_string()),
                        ..Default::default()
                    },
                ),
                (
                    "remote".to_string(),
                    JsonSchema {
                        schema_ref: Some("https://example.com/schema.json".to_string()),
                        ..Default::default()
                    },
                ),
            ])),
            ..Default::default()
        }
    );
}

#[test]
fn parse_tool_input_schema_preserves_nested_defs_ref_parent() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": { "name": { "$ref": "#/$defs/User/properties/name" } },
    //   "$defs": {
    //     "User": { "type": "object", "properties": { "name": { "type": "string" } } },
    //     "name": { "type": "string" },
    //     "Unused": { "type": "boolean" }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - The nested JSON Pointer ref remains unchanged.
    // - The parent root definition is retained so the local ref does not dangle.
    // - Unreferenced root definitions are still pruned.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "name": {"$ref": "#/$defs/User/properties/name"}
        },
        "$defs": {
            "User": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"}
                }
            },
            "name": {"type": "string"},
            "Unused": {"type": "boolean"}
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema {
            schema_type: Some(JsonSchemaType::Single(JsonSchemaPrimitiveType::Object)),
            properties: Some(BTreeMap::from([(
                "name".to_string(),
                JsonSchema {
                    schema_ref: Some("#/$defs/User/properties/name".to_string()),
                    ..Default::default()
                },
            )])),
            defs: Some(BTreeMap::from([(
                "User".to_string(),
                JsonSchema::object(
                    BTreeMap::from([(
                        "name".to_string(),
                        JsonSchema::string(/*description*/ None),
                    )]),
                    /*required*/ None,
                    /*additional_properties*/ None,
                ),
            )])),
            ..Default::default()
        }
    );
}

#[test]
fn parse_tool_input_schema_preserves_percent_encoded_definition_refs() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": {
    //     "user": { "$ref": "#/$defs/User%20Name" },
    //     "profile": { "$ref": "#/%24defs/Profile%7E0Name" }
    //   },
    //   "$defs": {
    //     "User Name": { "type": "string" },
    //     "Profile~Name": { "type": "string" },
    //     "Unused": { "type": "boolean" }
    //   }
    // }
    //
    // Expected normalization behavior:
    // - URI fragment percent encoding is decoded before JSON Pointer `~`
    //   escaping, per RFC 6901 section 6.
    // - The original `$ref` strings are preserved, but their definition
    //   targets are recognized as reachable and retained.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "user": {"$ref": "#/$defs/User%20Name"},
            "profile": {"$ref": "#/%24defs/Profile%7E0Name"}
        },
        "$defs": {
            "User Name": {"type": "string"},
            "Profile~Name": {"type": "string"},
            "Unused": {"type": "boolean"}
        }
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema {
            schema_type: Some(JsonSchemaType::Single(JsonSchemaPrimitiveType::Object)),
            properties: Some(BTreeMap::from([
                (
                    "profile".to_string(),
                    JsonSchema {
                        schema_ref: Some("#/%24defs/Profile%7E0Name".to_string()),
                        ..Default::default()
                    },
                ),
                (
                    "user".to_string(),
                    JsonSchema {
                        schema_ref: Some("#/$defs/User%20Name".to_string()),
                        ..Default::default()
                    },
                ),
            ])),
            defs: Some(BTreeMap::from([
                (
                    "Profile~Name".to_string(),
                    JsonSchema::string(/*description*/ None),
                ),
                (
                    "User Name".to_string(),
                    JsonSchema::string(/*description*/ None),
                ),
            ])),
            ..Default::default()
        }
    );
}

#[test]
fn parse_tool_input_schema_drops_malformed_definition_tables() {
    // Example schema shape:
    // {
    //   "type": "object",
    //   "properties": { "user": { "$ref": "#/$defs/User" } },
    //   "$defs": ["not", "an", "object"]
    // }
    //
    // Expected normalization behavior:
    // - Malformed `$defs` tables are dropped instead of rejecting the schema.
    // - The unresolved local ref remains visible to the model.
    let schema = parse_tool_input_schema(&serde_json::json!({
        "type": "object",
        "properties": {
            "user": {"$ref": "#/$defs/User"}
        },
        "$defs": ["not", "an", "object"]
    }))
    .expect("parse schema");

    assert_eq!(
        schema,
        JsonSchema {
            schema_type: Some(JsonSchemaType::Single(JsonSchemaPrimitiveType::Object)),
            properties: Some(BTreeMap::from([(
                "user".to_string(),
                JsonSchema {
                    schema_ref: Some("#/$defs/User".to_string()),
                    ..Default::default()
                },
            )])),
            ..Default::default()
        }
    );
}
