import httpx import pytest from iti.service_client import ( ServiceClient, ServiceBusinessError, ServiceConfig, ServiceHTTPError, ServiceUnavailableError, ) def test_service_client_sends_json_and_token(): def handler(request: httpx.Request) -> httpx.Response: assert request.headers["Authorization"] == "Bearer token" return httpx.Response(200, json={"ok": True}) client = ServiceClient( ServiceConfig.from_mapping( "demo", {"base_url": "https://example.test", "token": "token"}, ), transport=httpx.MockTransport(handler), ) assert client.get("/ping") == {"ok": True} def test_service_client_unwraps_envelope(): client = ServiceClient( ServiceConfig.from_mapping("demo", {"base_url": "https://example.test"}), transport=httpx.MockTransport( lambda request: httpx.Response( 200, json={"data": {"ok": True}, "code": 200, "message": "成功"}, ) ), ) assert client.get("/ping") == {"ok": True} def test_service_client_raises_for_business_errors(): client = ServiceClient( ServiceConfig.from_mapping("demo", {"base_url": "https://example.test"}), transport=httpx.MockTransport( lambda request: httpx.Response( 200, json={"data": None, "code": 403, "message": "无权限"}, ) ), ) with pytest.raises(ServiceBusinessError) as exc: client.get("/ping") assert exc.value.code == 403 assert exc.value.status_code == 200 def test_service_client_prefers_envelope_code_on_http_error(): client = ServiceClient( ServiceConfig.from_mapping("demo", {"base_url": "https://example.test"}), transport=httpx.MockTransport( lambda request: httpx.Response( 401, json={"data": {"reason": "expired"}, "code": 401, "message": "未认证"}, ) ), ) with pytest.raises(ServiceBusinessError) as exc: client.get("/ping") assert exc.value.code == 401 assert exc.value.status_code == 401 assert exc.value.data == {"reason": "expired"} def test_service_client_raises_for_http_errors(): client = ServiceClient( ServiceConfig.from_mapping("demo", {"base_url": "https://example.test"}), transport=httpx.MockTransport(lambda request: httpx.Response(503, text="down")), ) with pytest.raises(ServiceHTTPError) as exc: client.get("/ping") assert exc.value.status_code == 503 def test_service_client_circuit_breaker_opens_after_failures(): client = ServiceClient( ServiceConfig.from_mapping( "demo", { "base_url": "https://example.test", "circuit_breaker": {"enabled": True, "fail_max": 1}, }, ), transport=httpx.MockTransport(lambda request: httpx.Response(503, text="down")), ) with pytest.raises(ServiceHTTPError): client.get("/ping") with pytest.raises(ServiceUnavailableError, match="circuit breaker"): client.get("/ping")