@@ -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