use adk_core::schema_utils;
use adk_core::{GenericSchemaAdapter, SchemaAdapter};
use serde_json::json;
#[test]
fn test_gemini_strips_null_from_enum_arrays() {
let mut schema = json!({
"type": "string",
"enum": ["active", null, "inactive", null]
});
schema_utils::strip_null_from_enum(&mut schema);
assert_eq!(schema["enum"], json!(["active", "inactive"]));
}
#[test]
fn test_gemini_strips_single_null_from_enum() {
let mut schema = json!({
"type": "string",
"enum": ["yes", "no", null]
});
schema_utils::strip_null_from_enum(&mut schema);
assert_eq!(schema["enum"], json!(["yes", "no"]));
}
#[test]
fn test_gemini_removes_empty_enum_after_null_stripping() {
let mut schema = json!({
"type": "string",
"enum": [null, null]
});
schema_utils::strip_null_from_enum(&mut schema);
assert!(
schema.get("enum").is_none(),
"enum keyword should be removed when all values are null"
);
}
#[test]
fn test_gemini_removes_enum_with_only_null() {
let mut schema = json!({
"type": "string",
"enum": [null]
});
schema_utils::strip_null_from_enum(&mut schema);
assert!(schema.get("enum").is_none());
}
#[test]
fn test_openai_preserves_null_in_enums() {
let adapter = GenericSchemaAdapter;
let schema = json!({
"type": "string",
"enum": ["active", null, "inactive"]
});
let result = adapter.normalize_schema(schema);
assert_eq!(result["enum"], json!(["active", null, "inactive"]));
}
#[test]
fn test_openai_preserves_all_null_enum() {
let adapter = GenericSchemaAdapter;
let schema = json!({
"type": "string",
"enum": [null, null]
});
let result = adapter.normalize_schema(schema);
assert_eq!(result["enum"], json!([null, null]));
}
#[test]
fn test_gemini_strips_null_from_nested_enums() {
let mut schema = json!({
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["on", null, "off"]
},
"mode": {
"type": "string",
"enum": [null]
}
}
});
schema_utils::strip_null_from_enum(&mut schema);
assert_eq!(schema["properties"]["status"]["enum"], json!(["on", "off"]));
assert!(schema["properties"]["mode"].get("enum").is_none());
}
#[test]
fn test_strip_null_from_enum_no_null_present() {
let mut schema = json!({
"type": "string",
"enum": ["a", "b", "c"]
});
schema_utils::strip_null_from_enum(&mut schema);
assert_eq!(schema["enum"], json!(["a", "b", "c"]));
}
#[test]
fn test_gemini_converts_const_string_to_enum() {
let mut schema = json!({
"type": "string",
"const": "fixed_value"
});
schema_utils::convert_const_to_enum(&mut schema);
assert!(schema.get("const").is_none(), "const should be removed");
assert_eq!(schema["enum"], json!(["fixed_value"]));
}
#[test]
fn test_openai_converts_const_to_enum() {
let adapter = GenericSchemaAdapter;
let schema = json!({
"type": "integer",
"const": 42
});
let result = adapter.normalize_schema(schema);
assert!(result.get("const").is_none());
assert_eq!(result["enum"], json!([42]));
}
#[test]
fn test_openai_strict_converts_const_to_enum() {
let mut schema = json!({
"type": "boolean",
"const": true
});
schema_utils::convert_const_to_enum(&mut schema);
assert!(schema.get("const").is_none());
assert_eq!(schema["enum"], json!([true]));
}
#[test]
fn test_anthropic_preserves_const() {
let mut schema = json!({
"type": "string",
"const": "preserved_value"
});
schema_utils::strip_schema_keyword(&mut schema);
schema_utils::strip_conditional_keywords(&mut schema);
schema_utils::add_implicit_object_type(&mut schema);
assert_eq!(schema["const"], json!("preserved_value"));
assert!(schema.get("enum").is_none(), "enum should not be created");
}
#[test]
fn test_null_const_with_null_stripping_adapter() {
let mut schema = json!({
"type": "string",
"const": null
});
schema_utils::convert_const_to_enum(&mut schema);
assert_eq!(schema["enum"], json!([null]), "const null becomes enum [null]");
assert!(schema.get("const").is_none());
schema_utils::strip_null_from_enum(&mut schema);
assert!(
schema.get("enum").is_none(),
"enum should be removed after stripping the only null value"
);
}
#[test]
fn test_const_to_enum_nested() {
let mut schema = json!({
"type": "object",
"properties": {
"version": {
"type": "string",
"const": "v1"
},
"count": {
"type": "integer",
"const": 0
}
}
});
schema_utils::convert_const_to_enum(&mut schema);
assert_eq!(schema["properties"]["version"]["enum"], json!(["v1"]));
assert!(schema["properties"]["version"].get("const").is_none());
assert_eq!(schema["properties"]["count"]["enum"], json!([0]));
assert!(schema["properties"]["count"].get("const").is_none());
}
#[test]
fn test_const_to_enum_object_value() {
let mut schema = json!({
"const": {"key": "value"}
});
schema_utils::convert_const_to_enum(&mut schema);
assert!(schema.get("const").is_none());
assert_eq!(schema["enum"], json!([{"key": "value"}]));
}
#[test]
fn test_const_to_enum_array_value() {
let mut schema = json!({
"const": [1, 2, 3]
});
schema_utils::convert_const_to_enum(&mut schema);
assert!(schema.get("const").is_none());
assert_eq!(schema["enum"], json!([[1, 2, 3]]));
}
const ALLOWED_FORMATS: &[&str] =
&["date-time", "date", "time", "email", "uri", "uuid", "int32", "int64", "float", "double"];
#[test]
fn test_gemini_retains_allowed_formats() {
for &fmt in ALLOWED_FORMATS {
let mut schema = json!({
"type": "string",
"format": fmt
});
schema_utils::strip_unsupported_formats(&mut schema, ALLOWED_FORMATS);
assert_eq!(schema["format"], fmt, "format '{fmt}' should be retained");
}
}
#[test]
fn test_gemini_strips_unsupported_formats() {
let unsupported = ["hostname", "ipv4", "ipv6", "iri", "json-pointer", "regex", "custom"];
for fmt in unsupported {
let mut schema = json!({
"type": "string",
"format": fmt
});
schema_utils::strip_unsupported_formats(&mut schema, ALLOWED_FORMATS);
assert!(schema.get("format").is_none(), "format '{fmt}' should be stripped");
}
}
#[test]
fn test_recursive_format_handling_in_nested_schemas() {
let mut schema = json!({
"type": "object",
"properties": {
"email_field": {
"type": "string",
"format": "email"
},
"hostname_field": {
"type": "string",
"format": "hostname"
},
"nested": {
"type": "object",
"properties": {
"date_field": {
"type": "string",
"format": "date-time"
},
"custom_field": {
"type": "string",
"format": "custom-format"
}
}
}
}
});
schema_utils::strip_unsupported_formats(&mut schema, ALLOWED_FORMATS);
assert_eq!(schema["properties"]["email_field"]["format"], "email");
assert_eq!(schema["properties"]["nested"]["properties"]["date_field"]["format"], "date-time");
assert!(schema["properties"]["hostname_field"].get("format").is_none());
assert!(schema["properties"]["nested"]["properties"]["custom_field"].get("format").is_none());
}
#[test]
fn test_anthropic_preserves_all_formats() {
let formats = [
"date-time",
"date",
"time",
"email",
"uri",
"uuid",
"hostname",
"ipv4",
"ipv6",
"iri",
"json-pointer",
"regex",
"custom-format",
];
for fmt in formats {
let mut schema = json!({
"type": "string",
"format": fmt
});
schema_utils::strip_schema_keyword(&mut schema);
schema_utils::strip_conditional_keywords(&mut schema);
schema_utils::add_implicit_object_type(&mut schema);
assert_eq!(schema["format"], fmt, "Anthropic should preserve format '{fmt}'");
}
}
#[test]
fn test_openai_strips_unsupported_formats() {
let adapter = GenericSchemaAdapter;
let schema = json!({
"type": "string",
"format": "hostname"
});
let result = adapter.normalize_schema(schema);
assert!(result.get("format").is_none());
}
#[test]
fn test_openai_retains_allowed_formats() {
let adapter = GenericSchemaAdapter;
let schema = json!({
"type": "string",
"format": "date-time"
});
let result = adapter.normalize_schema(schema);
assert_eq!(result["format"], "date-time");
}
#[test]
fn test_format_handling_in_array_items() {
let mut schema = json!({
"type": "array",
"items": {
"type": "string",
"format": "ipv4"
}
});
schema_utils::strip_unsupported_formats(&mut schema, ALLOWED_FORMATS);
assert!(schema["items"].get("format").is_none());
}
#[test]
fn test_format_handling_in_additional_properties() {
let mut schema = json!({
"type": "object",
"additionalProperties": {
"type": "string",
"format": "uri"
}
});
schema_utils::strip_unsupported_formats(&mut schema, ALLOWED_FORMATS);
assert_eq!(schema["additionalProperties"]["format"], "uri");
}
#[test]
fn test_format_handling_in_any_of() {
let mut schema = json!({
"anyOf": [
{ "type": "string", "format": "email" },
{ "type": "string", "format": "hostname" }
]
});
schema_utils::strip_unsupported_formats(&mut schema, ALLOWED_FORMATS);
assert_eq!(schema["anyOf"][0]["format"], "email");
assert!(schema["anyOf"][1].get("format").is_none());
}