@@ -72,6 +72,17 @@ def strip(self, chars=None): # type: ignore[override]
7272 return object ()
7373
7474
75+ class _BrokenHeaderValueContains (str ):
76+ def __contains__ (self , item ): # type: ignore[override]
77+ _ = item
78+ raise RuntimeError ("header value contains exploded" )
79+
80+
81+ class _BrokenHeaderValueStringify (str ):
82+ def __str__ (self ) -> str :
83+ raise RuntimeError ("header value stringify exploded" )
84+
85+
7586def test_normalize_headers_trims_header_names ():
7687 headers = normalize_headers (
7788 {" X-Correlation-Id " : "abc123" },
@@ -328,6 +339,45 @@ def test_normalize_headers_rejects_control_characters():
328339 )
329340
330341
342+ def test_normalize_headers_wraps_header_character_validation_contains_failures ():
343+ with pytest .raises (
344+ HyperbrowserError , match = "Failed to validate header characters"
345+ ) as exc_info :
346+ normalize_headers (
347+ {"X-Trace-Id" : _BrokenHeaderValueContains ("value" )},
348+ mapping_error_message = "headers must be a mapping of string pairs" ,
349+ )
350+
351+ assert isinstance (exc_info .value .original_error , RuntimeError )
352+
353+
354+ def test_normalize_headers_preserves_header_character_validation_contains_hyperbrowser_failures ():
355+ class _BrokenHeaderValueContains (str ):
356+ def __contains__ (self , item ): # type: ignore[override]
357+ _ = item
358+ raise HyperbrowserError ("custom contains failure" )
359+
360+ with pytest .raises (HyperbrowserError , match = "custom contains failure" ) as exc_info :
361+ normalize_headers (
362+ {"X-Trace-Id" : _BrokenHeaderValueContains ("value" )},
363+ mapping_error_message = "headers must be a mapping of string pairs" ,
364+ )
365+
366+ assert exc_info .value .original_error is None
367+
368+
369+ def test_normalize_headers_wraps_header_character_validation_stringify_failures ():
370+ with pytest .raises (
371+ HyperbrowserError , match = "Failed to validate header characters"
372+ ) as exc_info :
373+ normalize_headers (
374+ {"X-Trace-Id" : _BrokenHeaderValueStringify ("value" )},
375+ mapping_error_message = "headers must be a mapping of string pairs" ,
376+ )
377+
378+ assert isinstance (exc_info .value .original_error , RuntimeError )
379+
380+
331381def test_parse_headers_env_json_rejects_control_characters ():
332382 with pytest .raises (
333383 HyperbrowserError , match = "headers must not contain control characters"
0 commit comments