Spaces:
Running
Running
File size: 9,508 Bytes
c4df749 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | """Tests for nullable field handling in OpenAPI schemas."""
from fastmcp.utilities.openapi import _handle_nullable_fields
class TestHandleNullableFields:
"""Test conversion of OpenAPI nullable fields to JSON Schema format."""
def test_root_level_nullable_string(self):
"""Test nullable string at root level."""
input_schema = {"type": "string", "nullable": True}
expected = {"type": ["string", "null"]}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_root_level_nullable_integer(self):
"""Test nullable integer at root level."""
input_schema = {"type": "integer", "nullable": True}
expected = {"type": ["integer", "null"]}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_root_level_nullable_boolean(self):
"""Test nullable boolean at root level."""
input_schema = {"type": "boolean", "nullable": True}
expected = {"type": ["boolean", "null"]}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_property_level_nullable_fields(self):
"""Test nullable fields in properties."""
input_schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"company": {"type": "string", "nullable": True},
"age": {"type": "integer", "nullable": True},
"active": {"type": "boolean", "nullable": True},
},
}
expected = {
"type": "object",
"properties": {
"name": {"type": "string"},
"company": {"type": ["string", "null"]},
"age": {"type": ["integer", "null"]},
"active": {"type": ["boolean", "null"]},
},
}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_mixed_nullable_and_non_nullable(self):
"""Test mix of nullable and non-nullable fields."""
input_schema = {
"type": "object",
"properties": {
"required_field": {"type": "string"},
"optional_nullable": {"type": "string", "nullable": True},
"optional_non_nullable": {"type": "string"},
},
"required": ["required_field"],
}
expected = {
"type": "object",
"properties": {
"required_field": {"type": "string"},
"optional_nullable": {"type": ["string", "null"]},
"optional_non_nullable": {"type": "string"},
},
"required": ["required_field"],
}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_nullable_false_ignored(self):
"""Test that nullable: false is ignored (removed but no type change)."""
input_schema = {"type": "string", "nullable": False}
expected = {"type": "string"}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_no_nullable_field_unchanged(self):
"""Test that schemas without nullable field are unchanged."""
input_schema = {
"type": "object",
"properties": {"name": {"type": "string"}},
}
expected = input_schema.copy()
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_nullable_without_type_removes_nullable(self):
"""Test that nullable field is removed even without type."""
input_schema = {"nullable": True, "description": "Some field"}
expected = {"description": "Some field"}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_preserves_other_fields(self):
"""Test that other fields are preserved during conversion."""
input_schema = {
"type": "string",
"nullable": True,
"description": "A nullable string",
"example": "test",
"format": "email",
}
expected = {
"type": ["string", "null"],
"description": "A nullable string",
"example": "test",
"format": "email",
}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_non_dict_input_unchanged(self):
"""Test that non-dict inputs are returned unchanged."""
assert _handle_nullable_fields("string") == "string" # type: ignore[arg-type]
assert _handle_nullable_fields(123) == 123 # type: ignore[arg-type]
assert _handle_nullable_fields(None) is None # type: ignore[arg-type]
assert _handle_nullable_fields([1, 2, 3]) == [1, 2, 3] # type: ignore[arg-type]
def test_performance_optimization_no_copy_when_unchanged(self):
"""Test that schemas without nullable fields return the same object (no copy)."""
input_schema = {
"type": "object",
"properties": {"name": {"type": "string"}},
}
result = _handle_nullable_fields(input_schema)
# Should return the exact same object, not a copy
assert result is input_schema
def test_union_types_with_nullable(self):
"""Test nullable handling with existing union types (type as array)."""
input_schema = {"type": ["string", "integer"], "nullable": True}
expected = {"type": ["string", "integer", "null"]}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_already_nullable_union_unchanged(self):
"""Test that union types already containing null are not modified."""
input_schema = {"type": ["string", "null"], "nullable": True}
expected = {"type": ["string", "null"]}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_property_level_union_with_nullable(self):
"""Test nullable handling with union types in properties."""
input_schema = {
"type": "object",
"properties": {"value": {"type": ["string", "integer"], "nullable": True}},
}
expected = {
"type": "object",
"properties": {"value": {"type": ["string", "integer", "null"]}},
}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_complex_union_nullable_scenarios(self):
"""Test various complex union type scenarios."""
# Already has null in different position
input1 = {"type": ["null", "string", "integer"], "nullable": True}
result1 = _handle_nullable_fields(input1)
assert result1 == {"type": ["null", "string", "integer"]}
# Single item array
input2 = {"type": ["string"], "nullable": True}
result2 = _handle_nullable_fields(input2)
assert result2 == {"type": ["string", "null"]}
def test_oneof_with_nullable(self):
"""Test nullable handling with oneOf constructs."""
input_schema = {
"oneOf": [{"type": "string"}, {"type": "integer"}],
"nullable": True,
}
expected = {
"anyOf": [{"type": "string"}, {"type": "integer"}, {"type": "null"}]
}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_anyof_with_nullable(self):
"""Test nullable handling with anyOf constructs."""
input_schema = {
"anyOf": [{"type": "string"}, {"type": "integer"}],
"nullable": True,
}
expected = {
"anyOf": [{"type": "string"}, {"type": "integer"}, {"type": "null"}]
}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_anyof_already_nullable(self):
"""Test anyOf that already contains null type."""
input_schema = {
"anyOf": [{"type": "string"}, {"type": "null"}],
"nullable": True,
}
expected = {"anyOf": [{"type": "string"}, {"type": "null"}]}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_allof_with_nullable(self):
"""Test nullable handling with allOf constructs."""
input_schema = {
"allOf": [{"type": "string"}, {"minLength": 1}],
"nullable": True,
}
expected = {
"anyOf": [
{"allOf": [{"type": "string"}, {"minLength": 1}]},
{"type": "null"},
]
}
result = _handle_nullable_fields(input_schema)
assert result == expected
def test_property_level_oneof_with_nullable(self):
"""Test nullable handling with oneOf in properties."""
input_schema = {
"type": "object",
"properties": {
"value": {
"oneOf": [{"type": "string"}, {"type": "integer"}],
"nullable": True,
}
},
}
expected = {
"type": "object",
"properties": {
"value": {
"anyOf": [{"type": "string"}, {"type": "integer"}, {"type": "null"}]
}
},
}
result = _handle_nullable_fields(input_schema)
assert result == expected
|