Skip to content

Commit d28a1a6

Browse files
authored
add regression tests for #1113 (#1122)
1 parent fd11eca commit d28a1a6

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed

tests/server/fastmcp/test_func_metadata.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,98 @@ def func_with_str_and_int(a: str, b: int):
454454
assert result["b"] == 123
455455

456456

457+
def test_str_annotation_preserves_json_string():
458+
"""
459+
Regression test for PR #1113: Ensure that when a parameter is annotated as str,
460+
valid JSON strings are NOT parsed into Python objects.
461+
462+
This test would fail before the fix (JSON string would be parsed to dict)
463+
and passes after the fix (JSON string remains as string).
464+
"""
465+
466+
def process_json_config(config: str, enabled: bool = True) -> str:
467+
"""Function that expects a JSON string as a string parameter."""
468+
# In real use, this function might validate or transform the JSON string
469+
# before parsing it, or pass it to another service as-is
470+
return f"Processing config: {config}"
471+
472+
meta = func_metadata(process_json_config)
473+
474+
# Test case 1: JSON object as string
475+
json_obj_str = '{"database": "postgres", "port": 5432}'
476+
result = meta.pre_parse_json({"config": json_obj_str, "enabled": True})
477+
478+
# The config parameter should remain as a string, NOT be parsed to a dict
479+
assert isinstance(result["config"], str)
480+
assert result["config"] == json_obj_str
481+
482+
# Test case 2: JSON array as string
483+
json_array_str = '["item1", "item2", "item3"]'
484+
result = meta.pre_parse_json({"config": json_array_str})
485+
486+
# Should remain as string
487+
assert isinstance(result["config"], str)
488+
assert result["config"] == json_array_str
489+
490+
# Test case 3: JSON string value (double-encoded)
491+
json_string_str = '"This is a JSON string"'
492+
result = meta.pre_parse_json({"config": json_string_str})
493+
494+
# Should remain as the original string with quotes
495+
assert isinstance(result["config"], str)
496+
assert result["config"] == json_string_str
497+
498+
# Test case 4: Complex nested JSON as string
499+
complex_json_str = '{"users": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}], "count": 2}'
500+
result = meta.pre_parse_json({"config": complex_json_str})
501+
502+
# Should remain as string
503+
assert isinstance(result["config"], str)
504+
assert result["config"] == complex_json_str
505+
506+
507+
@pytest.mark.anyio
508+
async def test_str_annotation_runtime_validation():
509+
"""
510+
Regression test for PR #1113: Test runtime validation with string parameters
511+
containing valid JSON to ensure they are passed as strings, not parsed objects.
512+
"""
513+
514+
def handle_json_payload(payload: str, strict_mode: bool = False) -> str:
515+
"""Function that processes a JSON payload as a string."""
516+
# This function expects to receive the raw JSON string
517+
# It might parse it later after validation or logging
518+
assert isinstance(payload, str), f"Expected str, got {type(payload)}"
519+
return f"Handled payload of length {len(payload)}"
520+
521+
meta = func_metadata(handle_json_payload)
522+
523+
# Test with a JSON object string
524+
json_payload = '{"action": "create", "resource": "user", "data": {"name": "Test User"}}'
525+
526+
result = await meta.call_fn_with_arg_validation(
527+
handle_json_payload,
528+
fn_is_async=False,
529+
arguments_to_validate={"payload": json_payload, "strict_mode": True},
530+
arguments_to_pass_directly=None,
531+
)
532+
533+
# The function should have received the string and returned successfully
534+
assert result == f"Handled payload of length {len(json_payload)}"
535+
536+
# Test with JSON array string
537+
json_array_payload = '["task1", "task2", "task3"]'
538+
539+
result = await meta.call_fn_with_arg_validation(
540+
handle_json_payload,
541+
fn_is_async=False,
542+
arguments_to_validate={"payload": json_array_payload},
543+
arguments_to_pass_directly=None,
544+
)
545+
546+
assert result == f"Handled payload of length {len(json_array_payload)}"
547+
548+
457549
# Tests for structured output functionality
458550

459551

0 commit comments

Comments
 (0)