@@ -287,3 +287,79 @@ def __getitem__(self, key: str) -> object:
287287 parse_extension_list_response_data ({"extensions" : [_BrokenExtensionMapping ()]})
288288
289289 assert exc_info .value .original_error is None
290+
291+
292+ def test_parse_extension_list_response_data_rejects_non_string_extension_keys ():
293+ with pytest .raises (
294+ HyperbrowserError ,
295+ match = "Expected extension object keys to be strings at index 0" ,
296+ ):
297+ parse_extension_list_response_data (
298+ {
299+ "extensions" : [
300+ {1 : "invalid-key" }, # type: ignore[dict-item]
301+ ]
302+ }
303+ )
304+
305+
306+ def test_parse_extension_list_response_data_wraps_extension_value_read_failures ():
307+ class _BrokenValueLookupMapping (Mapping [str , object ]):
308+ def __iter__ (self ) -> Iterator [str ]:
309+ yield "name"
310+
311+ def __len__ (self ) -> int :
312+ return 1
313+
314+ def __getitem__ (self , key : str ) -> object :
315+ _ = key
316+ raise RuntimeError ("cannot read extension value" )
317+
318+ with pytest .raises (
319+ HyperbrowserError ,
320+ match = "Failed to read extension object value for key 'name' at index 0" ,
321+ ) as exc_info :
322+ parse_extension_list_response_data ({"extensions" : [_BrokenValueLookupMapping ()]})
323+
324+ assert exc_info .value .original_error is not None
325+
326+
327+ def test_parse_extension_list_response_data_sanitizes_extension_value_read_keys ():
328+ class _BrokenValueLookupMapping (Mapping [str , object ]):
329+ def __iter__ (self ) -> Iterator [str ]:
330+ yield "bad\t key"
331+
332+ def __len__ (self ) -> int :
333+ return 1
334+
335+ def __getitem__ (self , key : str ) -> object :
336+ _ = key
337+ raise RuntimeError ("cannot read extension value" )
338+
339+ with pytest .raises (
340+ HyperbrowserError ,
341+ match = "Failed to read extension object value for key 'bad\\ ?key' at index 0" ,
342+ ) as exc_info :
343+ parse_extension_list_response_data ({"extensions" : [_BrokenValueLookupMapping ()]})
344+
345+ assert exc_info .value .original_error is not None
346+
347+
348+ def test_parse_extension_list_response_data_preserves_hyperbrowser_value_read_errors ():
349+ class _BrokenValueLookupMapping (Mapping [str , object ]):
350+ def __iter__ (self ) -> Iterator [str ]:
351+ yield "name"
352+
353+ def __len__ (self ) -> int :
354+ return 1
355+
356+ def __getitem__ (self , key : str ) -> object :
357+ _ = key
358+ raise HyperbrowserError ("custom extension value read failure" )
359+
360+ with pytest .raises (
361+ HyperbrowserError , match = "custom extension value read failure"
362+ ) as exc_info :
363+ parse_extension_list_response_data ({"extensions" : [_BrokenValueLookupMapping ()]})
364+
365+ assert exc_info .value .original_error is None
0 commit comments