Skip to content

Commit b0bc49d

Browse files
razor-xclaude
andauthored
test: cover malformed error responses (#599)
The error handling in SeamHttpClient distinguishes standard Seam error bodies, which become SeamHttpApiError, from everything else, which falls through is_api_error_response to raise_for_status. Only the first half was covered: the suite never exercised a non-JSON body, malformed JSON, a JSON body without an error object, or an error object without string type and message fields. Cover all four against the recording server, which grows an optional content type override so it can serve malformed JSON. The fake cannot produce these responses. This closes the same gap just restored in the Ruby SDK, where the equivalent branches briefly lost their specs. Claude-Session: https://claude.ai/code/session_011DzapiU8A9NMdyoTybL9xB Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1546ac7 commit b0bc49d

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

test/conftest.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ def recording_server_fixture():
5858
def recording_server(responses):
5959
"""Serve the given (status, body) responses, repeating the last one.
6060
61+
A response may also be (status, body, content_type) to override the
62+
content type inferred from the body, e.g. to serve malformed JSON.
63+
6164
Yields the endpoint along with the list of requests received so far.
6265
"""
6366

@@ -80,7 +83,8 @@ def do_POST(self): # BaseHTTPRequestHandler dispatches on this name.
8083
}
8184
)
8285

83-
status, payload = remaining.pop(0) if len(remaining) > 1 else remaining[0]
86+
response = remaining.pop(0) if len(remaining) > 1 else remaining[0]
87+
status, payload, *rest = response
8488

8589
if isinstance(payload, str):
8690
content_type = "text/plain"
@@ -89,6 +93,9 @@ def do_POST(self): # BaseHTTPRequestHandler dispatches on this name.
8993
content_type = "application/json"
9094
body = json.dumps(payload).encode()
9195

96+
if rest:
97+
content_type = rest[0]
98+
9299
self.send_response(status)
93100
self.send_header("content-type", content_type)
94101
self.send_header("content-length", str(len(body)))

test/http_error_test.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,50 @@ def test_seam_http_throws_http_error_on_non_standard_response(server):
6060
seam.devices.list()
6161

6262
assert exc_info.value.response.status_code == 503
63+
64+
65+
# The fake cannot produce malformed error responses, so the recording server
66+
# drives the bodies that must fall through is_api_error_response and raise a
67+
# plain HTTPError rather than being parsed into a SeamHttpApiError.
68+
def test_seam_http_raises_http_error_on_non_json_response(recording_server):
69+
with recording_server([(500, "Internal Server Error")]) as (endpoint, _):
70+
seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint)
71+
72+
with pytest.raises(niquests.HTTPError) as exc_info:
73+
seam.devices.list()
74+
75+
assert exc_info.value.response.status_code == 500
76+
77+
78+
def test_seam_http_raises_http_error_on_malformed_json(recording_server):
79+
responses = [(500, "{invalid json", "application/json")]
80+
81+
with recording_server(responses) as (endpoint, _):
82+
seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint)
83+
84+
with pytest.raises(niquests.HTTPError) as exc_info:
85+
seam.devices.list()
86+
87+
assert exc_info.value.response.status_code == 500
88+
89+
90+
def test_seam_http_raises_http_error_on_json_without_error_object(recording_server):
91+
with recording_server([(500, {"message": "Some error"})]) as (endpoint, _):
92+
seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint)
93+
94+
with pytest.raises(niquests.HTTPError) as exc_info:
95+
seam.devices.list()
96+
97+
assert exc_info.value.response.status_code == 500
98+
99+
100+
def test_seam_http_raises_http_error_on_error_object_without_type_and_message(
101+
recording_server,
102+
):
103+
with recording_server([(500, {"error": {"code": 500}})]) as (endpoint, _):
104+
seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint)
105+
106+
with pytest.raises(niquests.HTTPError) as exc_info:
107+
seam.devices.list()
108+
109+
assert exc_info.value.response.status_code == 500

0 commit comments

Comments
 (0)