@@ -454,6 +454,98 @@ def func_with_str_and_int(a: str, b: int):
454
454
assert result ["b" ] == 123
455
455
456
456
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
+
457
549
# Tests for structured output functionality
458
550
459
551
0 commit comments