mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
"""Tests for hanzo_tools.core.cloud — what a refused call and an answered one hand back.
|
|
|
|
HanzoCloud runs against an httpx MockTransport answering as api.hanzo.ai does,
|
|
so no socket is opened.
|
|
"""
|
|
|
|
import asyncio
|
|
|
|
import httpx
|
|
import pytest
|
|
from hanzo_tools.core import CloudError, HanzoCloud
|
|
|
|
FREE_PLAN_CAP = {
|
|
"error": {
|
|
"message": "Free plan: today's Kai requests are used. Upgrade for more: https://hanzo.ai/pay",
|
|
"type": "rate_limit_error",
|
|
"code": "free_plan_cap",
|
|
"class": "ours",
|
|
"resets_at": "2026-10-05T00:00:00Z",
|
|
}
|
|
}
|
|
|
|
|
|
def cloud(handler):
|
|
c = HanzoCloud(base_url="https://api.hanzo.ai", key="sk-test")
|
|
c._client = httpx.AsyncClient(base_url="https://api.hanzo.ai", transport=httpx.MockTransport(handler))
|
|
return c
|
|
|
|
|
|
def test_send_hands_back_the_body_and_the_headers():
|
|
c = cloud(lambda r: httpx.Response(200, json={"ok": 1}, headers={"X-Hanzo-Paid-By": "credits"}))
|
|
body, headers = asyncio.run(c.send("POST", "/v1/chat/completions", json={"model": "m"}))
|
|
assert body == {"ok": 1}
|
|
assert headers["x-hanzo-paid-by"] == "credits"
|
|
|
|
|
|
def test_a_refusal_carries_its_whole_body_and_headers():
|
|
c = cloud(lambda r: httpx.Response(429, json=FREE_PLAN_CAP, headers={"X-Hanzo-Usage": "limited"}))
|
|
with pytest.raises(CloudError) as raised:
|
|
asyncio.run(c.post("/v1/decisions", {"model": "kai"}))
|
|
e = raised.value
|
|
assert e.status == 429
|
|
assert e.body == FREE_PLAN_CAP
|
|
assert e.headers["x-hanzo-usage"] == "limited"
|
|
assert "429" in str(e) and "free_plan_cap" in str(e)
|
|
|
|
|
|
def test_a_body_that_is_not_json_is_none_and_a_transport_failure_has_no_answer():
|
|
c = cloud(lambda r: httpx.Response(502, text="<html>bad gateway</html>"))
|
|
with pytest.raises(CloudError) as raised:
|
|
asyncio.run(c.get("/v1/models"))
|
|
assert (raised.value.status, raised.value.body) == (502, None)
|
|
|
|
def down(request):
|
|
raise httpx.ConnectError("no route", request=request)
|
|
|
|
with pytest.raises(CloudError) as raised:
|
|
asyncio.run(cloud(down).get("/v1/models"))
|
|
assert (raised.value.status, raised.value.body, dict(raised.value.headers)) == (None, None, {})
|
|
|
|
|
|
def test_get_and_post_still_answer_the_body_alone():
|
|
c = cloud(lambda r: httpx.Response(200, json={"data": [1, 2]}))
|
|
assert asyncio.run(c.get("/v1/models")) == {"data": [1, 2]}
|
|
assert asyncio.run(c.post("/v1/x", {})) == {"data": [1, 2]}
|