Files
wehub-resource-sync 60e0ffc959
Upgrade checks / Notify on failure (push) Has been cancelled
Upgrade checks / Close issue on success (push) Has been cancelled
Schema Crash Test / Real-world schema crash test (232K schemas) (push) Has been cancelled
Run static analysis / static_analysis (push) Has been cancelled
Tests / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Tests / Tests with lowest-direct dependencies (push) Has been cancelled
Tests / MCP conformance tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
Tests / Package install smoke (push) Has been cancelled
Upgrade checks / Static analysis (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Upgrade checks / Integration tests (push) Has been cancelled
Update MCPServerConfig Schema / update-config-schema (push) Has been cancelled
Update SDK Documentation / update-sdk-docs (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:59 +08:00

234 lines
7.8 KiB
Python

"""Tests to ensure OpenAPI schema generation works correctly."""
import pytest
from fastmcp.utilities.openapi.models import (
HTTPRoute,
ParameterInfo,
RequestBodyInfo,
)
from fastmcp.utilities.openapi.schemas import (
_combine_schemas_and_map_params,
)
class TestSchemaGeneration:
"""Test that OpenAPI schema generation produces correct schemas."""
def test_optional_parameter_nullable_behavior(self):
"""Test that optional parameters are not made nullable - they can simply be omitted."""
route = HTTPRoute(
method="GET",
path="/test",
operation_id="test_op",
parameters=[
ParameterInfo(
name="required_param",
location="query",
required=True,
schema={"type": "string"},
),
ParameterInfo(
name="optional_param",
location="query",
required=False,
schema={"type": "string"},
),
],
)
schema, _ = _combine_schemas_and_map_params(route)
# Required parameter should have simple type
assert schema["properties"]["required_param"]["type"] == "string"
assert "anyOf" not in schema["properties"]["required_param"]
# Optional parameters should preserve original schema without making it nullable
assert "anyOf" not in schema["properties"]["optional_param"]
assert schema["properties"]["optional_param"]["type"] == "string"
# Required list should only include required parameters
assert "required_param" in schema["required"]
assert "optional_param" not in schema["required"]
def test_parameter_collision_handling(self):
"""Test that parameter collisions are handled with suffixes."""
route = HTTPRoute(
method="PUT",
path="/users/{id}",
operation_id="update_user",
parameters=[
ParameterInfo(
name="id",
location="path",
required=True,
schema={"type": "integer"},
)
],
request_body=RequestBodyInfo(
required=True,
content_schema={
"application/json": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"},
},
"required": ["name"],
}
},
),
)
schema, param_map = _combine_schemas_and_map_params(route)
# Should have path parameter with suffix
assert "id__path" in schema["properties"]
# Should have body parameter without suffix
assert "id" in schema["properties"]
# Should have name parameter from body
assert "name" in schema["properties"]
# Required should include path param (suffixed) and required body params
required = set(schema["required"])
assert "id__path" in required
assert "name" in required
# Parameter map should correctly map suffixed parameter
assert param_map["id__path"]["location"] == "path"
assert param_map["id__path"]["openapi_name"] == "id"
assert param_map["id"]["location"] == "body"
assert param_map["name"]["location"] == "body"
def test_non_body_parameter_collision_handling(self):
"""Test that same-named parameters in different locations use suffixes."""
route = HTTPRoute(
method="GET",
path="/users/{id}",
operation_id="get_user",
parameters=[
ParameterInfo(
name="id",
location="path",
required=True,
schema={"type": "string"},
),
ParameterInfo(
name="id",
location="query",
required=False,
schema={"type": "string"},
),
ParameterInfo(
name="id",
location="header",
required=False,
schema={"type": "string"},
),
],
)
schema, param_map = _combine_schemas_and_map_params(route)
properties = schema["properties"]
assert "id" not in properties
assert "id__path" in properties
assert "id__query" in properties
assert "id__header" in properties
assert schema["required"] == ["id__path"]
assert param_map["id__path"] == {"location": "path", "openapi_name": "id"}
assert param_map["id__query"] == {"location": "query", "openapi_name": "id"}
assert param_map["id__header"] == {"location": "header", "openapi_name": "id"}
@pytest.mark.parametrize(
"param_type",
[
{"type": "integer"},
{"type": "number"},
{"type": "boolean"},
{"type": "array", "items": {"type": "string"}},
{"type": "object", "properties": {"name": {"type": "string"}}},
],
)
def test_nullable_behavior_different_types(self, param_type):
"""Test nullable behavior works for all parameter types."""
route = HTTPRoute(
method="GET",
path="/test",
operation_id="test_op",
parameters=[
ParameterInfo(
name="optional_param",
location="query",
required=False,
schema=param_type,
)
],
)
schema, _ = _combine_schemas_and_map_params(route)
# Should preserve original schema without making it nullable
param = schema["properties"]["optional_param"]
assert "anyOf" not in param
# Should match the original parameter schema
for key, value in param_type.items():
assert param[key] == value
def test_no_parameters_no_body(self):
"""Test schema generation when there are no parameters or body."""
route = HTTPRoute(
method="GET",
path="/health",
operation_id="health_check",
)
schema, param_map = _combine_schemas_and_map_params(route)
# Should have empty object schema
assert schema["type"] == "object"
assert schema["properties"] == {}
assert schema["required"] == []
assert param_map == {}
def test_body_only_no_parameters(self):
"""Test schema generation with only request body, no parameters."""
body_schema = {
"application/json": {
"type": "object",
"properties": {
"title": {"type": "string"},
"description": {"type": "string"},
},
"required": ["title"],
}
}
route = HTTPRoute(
method="POST",
path="/items",
operation_id="create_item",
request_body=RequestBodyInfo(
required=True,
content_schema=body_schema,
),
)
schema, param_map = _combine_schemas_and_map_params(route)
# Should have body properties
assert "title" in schema["properties"]
assert "description" in schema["properties"]
# Required should match body requirements
assert "title" in schema["required"]
assert "description" not in schema["required"]
# Parameter map should map body properties
assert param_map["title"]["location"] == "body"
assert param_map["description"]["location"] == "body"