@@ -32,22 +32,35 @@ def __init__(self, responses: dict[str, list[FakeResponse]]) -> None:
3232 def _next (self , key : str ) -> FakeResponse :
3333 return self ._responses [key ].pop (0 )
3434
35- def get (self , url : str , headers : dict [str , str ], timeout : int ) -> FakeResponse :
35+ def get (
36+ self ,
37+ url : str ,
38+ headers : dict [str , str ],
39+ timeout : int ,
40+ ** kwargs : Any ,
41+ ) -> FakeResponse :
3642 self .calls .append (
37- {"method" : "GET" , "url" : url , "headers" : headers , "timeout" : timeout }
43+ {
44+ "method" : "GET" ,
45+ "url" : url ,
46+ "headers" : headers ,
47+ "timeout" : timeout ,
48+ ** kwargs ,
49+ }
3850 )
3951 if url .endswith ("/initSession" ):
4052 return self ._next ("init" )
4153 if url .endswith ("/killSession" ):
4254 return self ._next ("kill" )
43- raise AssertionError ( f"Unexpected GET { url } " )
55+ return self . _next ( "json " )
4456
4557 def post (
4658 self ,
4759 url : str ,
4860 headers : dict [str , str ],
49- files : list [Any ],
5061 timeout : int ,
62+ files : list [Any ] | None = None ,
63+ ** kwargs : Any ,
5164 ) -> FakeResponse :
5265 self .calls .append (
5366 {
@@ -56,9 +69,48 @@ def post(
5669 "headers" : headers ,
5770 "files" : files ,
5871 "timeout" : timeout ,
72+ ** kwargs ,
5973 }
6074 )
61- return self ._next ("upload" )
75+ if files is not None :
76+ return self ._next ("upload" )
77+ return self ._next ("json" )
78+
79+ def put (
80+ self ,
81+ url : str ,
82+ headers : dict [str , str ],
83+ timeout : int ,
84+ ** kwargs : Any ,
85+ ) -> FakeResponse :
86+ self .calls .append (
87+ {
88+ "method" : "PUT" ,
89+ "url" : url ,
90+ "headers" : headers ,
91+ "timeout" : timeout ,
92+ ** kwargs ,
93+ }
94+ )
95+ return self ._next ("json" )
96+
97+ def delete (
98+ self ,
99+ url : str ,
100+ headers : dict [str , str ],
101+ timeout : int ,
102+ ** kwargs : Any ,
103+ ) -> FakeResponse :
104+ self .calls .append (
105+ {
106+ "method" : "DELETE" ,
107+ "url" : url ,
108+ "headers" : headers ,
109+ "timeout" : timeout ,
110+ ** kwargs ,
111+ }
112+ )
113+ return self ._next ("json" )
62114
63115 def close (self ) -> None :
64116 self .closed = True
@@ -260,6 +312,78 @@ def get(self, url: str, headers: dict[str, str], timeout: int) -> FakeResponse:
260312 assert http .closed is True
261313
262314
315+ def test_request_json_sends_body_and_returns_parsed_payload () -> None :
316+ """``request_json`` serialises the body and decodes the JSON response."""
317+
318+ http = _FakeV1Http (
319+ responses = {
320+ "init" : [FakeResponse (status_code = 200 , payload = {"session_token" : "tk" })],
321+ "json" : [FakeResponse (status_code = 200 , payload = {"ok" : True })],
322+ "kill" : [FakeResponse (status_code = 200 , payload = {})],
323+ }
324+ )
325+ session = _make (http )
326+ result = session .request_json (
327+ "POST" ,
328+ "PluginFieldsContainer" ,
329+ json_body = {"input" : {"name" : "x" }},
330+ )
331+ assert result == {"ok" : True }
332+ post_call = next (call for call in http .calls if call ["method" ] == "POST" )
333+ assert post_call ["url" ].endswith ("/PluginFieldsContainer" )
334+ assert post_call ["data" ] == jsonlib .dumps ({"input" : {"name" : "x" }})
335+ assert post_call ["headers" ]["Content-Type" ] == "application/json"
336+
337+
338+ def test_request_json_supports_get_with_params () -> None :
339+ """``request_json`` forwards query params on GET calls."""
340+
341+ http = _FakeV1Http (
342+ responses = {
343+ "init" : [FakeResponse (status_code = 200 , payload = {"session_token" : "tk" })],
344+ "json" : [FakeResponse (status_code = 200 , payload = [{"id" : 1 }])],
345+ }
346+ )
347+ session = _make (http )
348+ out = session .request_json ("GET" , "PluginFieldsContainer" , params = {"range" : "0-1" })
349+ assert out == [{"id" : 1 }]
350+ get_call = next (
351+ call for call in http .calls if call ["url" ].endswith ("/PluginFieldsContainer" )
352+ )
353+ assert get_call ["params" ] == {"range" : "0-1" }
354+
355+
356+ def test_request_json_returns_empty_dict_on_empty_body () -> None :
357+ """An empty response body decodes as an empty dict instead of raising."""
358+
359+ http = _FakeV1Http (
360+ responses = {
361+ "init" : [FakeResponse (status_code = 200 , payload = {"session_token" : "tk" })],
362+ "json" : [FakeResponse (status_code = 204 , payload = {}, content = b"" )],
363+ }
364+ )
365+ session = _make (http )
366+ assert session .request_json ("DELETE" , "Some/Resource/1" ) == {}
367+
368+
369+ def test_request_json_raises_on_non_success_status () -> None :
370+ """Non-success statuses surface as ``ValueError`` with the body excerpt."""
371+
372+ http = _FakeV1Http (
373+ responses = {
374+ "init" : [FakeResponse (status_code = 200 , payload = {"session_token" : "tk" })],
375+ "json" : [
376+ FakeResponse (status_code = 500 , payload = {"err" : "boom" }),
377+ FakeResponse (status_code = 500 , payload = {"err" : "boom" }),
378+ ],
379+ "kill" : [FakeResponse (status_code = 200 , payload = {})],
380+ }
381+ )
382+ session = _make (http )
383+ with pytest .raises (ValueError , match = "failed" ):
384+ session .request_json ("GET" , "PluginFieldsContainer" )
385+
386+
263387def test_session_token_invalid_marker_triggers_renew () -> None :
264388 """An ``ERROR_SESSION_TOKEN_INVALID`` body marker counts as an auth failure."""
265389
0 commit comments