use adk_core::{GenericSchemaAdapter, SchemaAdapter};
use proptest::prelude::*;
use serde_json::{Number, Value};
fn arb_json_value() -> impl Strategy<Value = Value> {
arb_json_value_depth(0, 4)
}
fn arb_json_value_depth(current_depth: u32, max_depth: u32) -> impl Strategy<Value = Value> {
let leaf = prop_oneof![
Just(Value::Null),
any::<bool>().prop_map(Value::Bool),
any::<i64>().prop_map(|n| Value::Number(Number::from(n))),
"[a-zA-Z0-9_$#/]{0,30}".prop_map(Value::String),
];
if current_depth >= max_depth {
leaf.boxed()
} else {
let next_depth = current_depth + 1;
prop_oneof![
4 => leaf.clone(),
1 => prop::collection::vec(arb_json_value_depth(next_depth, max_depth), 0..4)
.prop_map(Value::Array),
1 => prop::collection::hash_map(
"[a-zA-Z_$][a-zA-Z0-9_]{0,15}",
arb_json_value_depth(next_depth, max_depth),
0..5,
)
.prop_map(|map| {
let obj: serde_json::Map<String, Value> = map.into_iter().collect();
Value::Object(obj)
}),
]
.boxed()
}
}
fn arb_schema_like_value() -> impl Strategy<Value = Value> {
prop_oneof![
arb_json_value(),
prop_oneof![
Just("string"),
Just("number"),
Just("integer"),
Just("boolean"),
Just("array"),
Just("object"),
Just("null"),
]
.prop_map(|t| { serde_json::json!({ "type": t }) }),
"[a-z_]{1,10}".prop_map(|name| {
serde_json::json!({
"type": "object",
"properties": {
name: { "type": "string" }
}
})
}),
Just(serde_json::json!({
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {}
})),
Just(serde_json::json!({
"type": "object",
"if": { "properties": { "x": { "type": "number" } } },
"then": { "required": ["x"] },
"else": { "required": ["y"] }
})),
any::<i64>().prop_map(|n| {
serde_json::json!({
"type": "integer",
"const": n
})
}),
Just(serde_json::json!({
"type": "string",
"enum": ["a", "b", null, "c"]
})),
prop_oneof![
Just("date-time"),
Just("email"),
Just("hostname"),
Just("ipv4"),
Just("custom-format"),
]
.prop_map(|fmt| {
serde_json::json!({
"type": "string",
"format": fmt
})
}),
Just(serde_json::json!({
"anyOf": [
{ "type": "string" },
{ "type": "null" }
]
})),
Just(serde_json::json!({
"allOf": [
{ "type": "object", "properties": { "a": { "type": "string" } } },
{ "properties": { "b": { "type": "number" } } }
]
})),
Just(serde_json::json!({
"type": "object",
"properties": {
"child": { "$ref": "#/definitions/Child" }
},
"definitions": {
"Child": { "type": "string" }
}
})),
Just(serde_json::json!({
"type": "object",
"properties": { "x": { "type": "number" } },
"additionalProperties": false
})),
Just(serde_json::json!({
"type": "object",
"properties": {
"l1": {
"type": "object",
"properties": {
"l2": {
"type": "object",
"properties": {
"l3": {
"type": "object",
"properties": {
"l4": {
"type": "object",
"properties": {
"l5": {
"type": "object",
"properties": {
"l6": { "type": "string" }
}
}
}
}
}
}
}
}
}
}
}
})),
]
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_generic_adapter_never_panics(schema in arb_json_value()) {
let adapter = GenericSchemaAdapter;
let result = adapter.normalize_schema(schema);
let serialized = serde_json::to_string(&result);
prop_assert!(serialized.is_ok(), "Result must be serializable JSON");
}
#[test]
fn prop_generic_adapter_never_panics_schema_like(schema in arb_schema_like_value()) {
let adapter = GenericSchemaAdapter;
let result = adapter.normalize_schema(schema);
let serialized = serde_json::to_string(&result);
prop_assert!(serialized.is_ok(), "Result must be serializable JSON");
}
}