feat(phase-2): workstream 2 — Python SDK (sentryagent-idp)
Sync (requests) and async (httpx) clients with identical API surface to the Node.js SDK. Delivered: - pyproject.toml — python>=3.9, hatchling build, mypy strict config - types.py — all 14-endpoint request/response dataclasses - errors.py — AgentIdPError with from_api_error, from_oauth2_error, network_error - token_manager.py — thread-safe sync TokenManager, 60s refresh buffer - async_token_manager.py — asyncio-safe AsyncTokenManager (httpx) - _request.py — shared sync/async request helper (DRY) - services/agents.py — AgentRegistryClient + AsyncAgentRegistryClient (5 methods each) - services/credentials.py — CredentialClient + AsyncCredentialClient (4 methods each) - services/token.py — TokenClient + AsyncTokenClient (introspect + revoke) - services/audit.py — AuditClient + AsyncAuditClient (query + get) - client.py — AgentIdPClient + AsyncAgentIdPClient - __init__.py — barrel exports - README.md — installation, quick start, full API reference QA gates: - mypy --strict: 0 errors (12 source files) - pytest: 57/57 passed - Coverage: 90.83% (required >= 80%) - All 14 endpoints covered (sync + async) - AgentIdPError raised on all failure paths Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
52
sdk-python/tests/test_errors.py
Normal file
52
sdk-python/tests/test_errors.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""Tests for AgentIdPError."""
|
||||
|
||||
from sentryagent_idp.errors import AgentIdPError
|
||||
|
||||
|
||||
def test_basic_construction() -> None:
|
||||
err = AgentIdPError("AgentNotFoundError", "Agent not found.", 404)
|
||||
assert err.code == "AgentNotFoundError"
|
||||
assert err.http_status == 404
|
||||
assert str(err) == "Agent not found."
|
||||
assert err.details is None
|
||||
|
||||
|
||||
def test_from_api_error_valid_body() -> None:
|
||||
body = {"code": "AgentNotFoundError", "message": "Not found.", "details": {"id": "x"}}
|
||||
err = AgentIdPError.from_api_error(body, 404)
|
||||
assert err.code == "AgentNotFoundError"
|
||||
assert err.http_status == 404
|
||||
assert err.details == {"id": "x"}
|
||||
|
||||
|
||||
def test_from_api_error_unknown_body() -> None:
|
||||
err = AgentIdPError.from_api_error("plain string", 500)
|
||||
assert err.code == "UNKNOWN_ERROR"
|
||||
assert err.http_status == 500
|
||||
|
||||
|
||||
def test_from_oauth2_error() -> None:
|
||||
body = {"error": "invalid_client", "error_description": "Bad credentials."}
|
||||
err = AgentIdPError.from_oauth2_error(body, 401)
|
||||
assert err.code == "invalid_client"
|
||||
assert str(err) == "Bad credentials."
|
||||
assert err.http_status == 401
|
||||
|
||||
|
||||
def test_from_oauth2_error_unknown() -> None:
|
||||
err = AgentIdPError.from_oauth2_error("garbage", 400)
|
||||
assert err.code == "unknown_error"
|
||||
|
||||
|
||||
def test_network_error() -> None:
|
||||
cause = ConnectionError("refused")
|
||||
err = AgentIdPError.network_error(cause)
|
||||
assert err.code == "NETWORK_ERROR"
|
||||
assert err.http_status == 0
|
||||
assert "refused" in str(err)
|
||||
|
||||
|
||||
def test_repr() -> None:
|
||||
err = AgentIdPError("CODE", "msg", 400)
|
||||
assert "AgentIdPError" in repr(err)
|
||||
assert "CODE" in repr(err)
|
||||
Reference in New Issue
Block a user