Fix nullable enum validation in OpenAPI converter (#2093)

This commit is contained in:
Jeremiah Lowin 2025-10-15 06:30:30 -07:00 committed by GitHub
commit b45499292f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 134 additions and 0 deletions

View file

@ -176,6 +176,10 @@ def _convert_nullable_field(schema: dict[str, Any]) -> dict[str, Any]:
# Wrap allOf in anyOf with null option
result["anyOf"] = [{"allOf": result.pop("allOf")}, {"type": "null"}]
# Handle enum fields - add null to enum values if present
if "enum" in result and None not in result["enum"]:
result["enum"] = result["enum"] + [None]
return result

View file

@ -1,5 +1,8 @@
"""Tests for nullable field handling in OpenAPI schemas."""
import pytest
from jsonschema import ValidationError, validate
from fastmcp.experimental.utilities.openapi.json_schema_converter import (
convert_openapi_schema_to_json_schema,
)
@ -243,3 +246,130 @@ class TestHandleNullableFields:
}
result = convert_openapi_schema_to_json_schema(input_schema, "3.0.0")
assert result == expected
def test_nullable_enum_field(self):
"""Test nullable enum field - issue #2082."""
input_schema = {
"type": "string",
"nullable": True,
"enum": ["VALUE1", "VALUE2", "VALUE3"],
}
expected = {
"type": ["string", "null"],
"enum": ["VALUE1", "VALUE2", "VALUE3", None],
}
result = convert_openapi_schema_to_json_schema(input_schema, "3.0.0")
assert result == expected
def test_nullable_enum_already_contains_null(self):
"""Test nullable enum that already contains None."""
input_schema = {
"type": "string",
"nullable": True,
"enum": ["VALUE1", "VALUE2", None],
}
expected = {
"type": ["string", "null"],
"enum": ["VALUE1", "VALUE2", None],
}
result = convert_openapi_schema_to_json_schema(input_schema, "3.0.0")
assert result == expected
def test_nullable_enum_without_type(self):
"""Test nullable enum without explicit type field."""
input_schema = {
"nullable": True,
"enum": ["VALUE1", "VALUE2", "VALUE3"],
}
expected = {
"enum": ["VALUE1", "VALUE2", "VALUE3", None],
}
result = convert_openapi_schema_to_json_schema(input_schema, "3.0.0")
assert result == expected
def test_non_nullable_enum_unchanged(self):
"""Test that enum without nullable is unchanged."""
input_schema = {
"type": "string",
"enum": ["VALUE1", "VALUE2", "VALUE3"],
}
expected = {
"type": "string",
"enum": ["VALUE1", "VALUE2", "VALUE3"],
}
result = convert_openapi_schema_to_json_schema(input_schema, "3.0.0")
assert result == expected
def test_property_level_nullable_enum(self):
"""Test nullable enum in object properties."""
input_schema = {
"type": "object",
"properties": {
"status": {
"type": "string",
"nullable": True,
"enum": ["ACTIVE", "INACTIVE", "PENDING"],
},
"name": {"type": "string"},
},
}
expected = {
"type": "object",
"properties": {
"status": {
"type": ["string", "null"],
"enum": ["ACTIVE", "INACTIVE", "PENDING", None],
},
"name": {"type": "string"},
},
}
result = convert_openapi_schema_to_json_schema(input_schema, "3.0.0")
assert result == expected
def test_nullable_integer_enum(self):
"""Test nullable enum with integer values."""
input_schema = {
"type": "integer",
"nullable": True,
"enum": [1, 2, 3],
}
expected = {
"type": ["integer", "null"],
"enum": [1, 2, 3, None],
}
result = convert_openapi_schema_to_json_schema(input_schema, "3.0.0")
assert result == expected
class TestNullableFieldValidation:
"""Test that converted schemas validate correctly with jsonschema."""
def test_nullable_string_validates(self):
"""Test that nullable string validates both null and string values."""
openapi_schema = {"type": "string", "nullable": True}
json_schema = convert_openapi_schema_to_json_schema(openapi_schema, "3.0.0")
# Both null and string should validate
validate(instance=None, schema=json_schema)
validate(instance="test", schema=json_schema)
# Other types should fail
with pytest.raises(ValidationError):
validate(instance=123, schema=json_schema)
def test_nullable_enum_validates(self):
"""Test that nullable enum validates null, enum values, and rejects invalid values."""
openapi_schema = {
"type": "string",
"nullable": True,
"enum": ["VALUE1", "VALUE2", "VALUE3"],
}
json_schema = convert_openapi_schema_to_json_schema(openapi_schema, "3.0.0")
# Null and enum values should validate
validate(instance=None, schema=json_schema)
validate(instance="VALUE1", schema=json_schema)
# Invalid values should fail
with pytest.raises(ValidationError):
validate(instance="INVALID", schema=json_schema)