@@ -48,6 +48,21 @@ def _thread_payload() -> dict[str, object]:
4848 }
4949
5050
51+ def _webhook_subscription_payload () -> dict [str , object ]:
52+ return {
53+ "subscription_id" : "44444444-4444-4444-8444-444444444444" ,
54+ "owner_agent" : "agent://owner" ,
55+ "callback_url" : "https://integrator.example/webhooks/axme" ,
56+ "event_types" : ["inbox.thread_created" ],
57+ "active" : True ,
58+ "description" : "sdk-test" ,
59+ "created_at" : "2026-02-28T00:00:00Z" ,
60+ "updated_at" : "2026-02-28T00:00:01Z" ,
61+ "revoked_at" : None ,
62+ "secret_hint" : "****hint" ,
63+ }
64+
65+
5166def test_health_success () -> None :
5267 def handler (request : httpx .Request ) -> httpx .Response :
5368 assert request .method == "GET"
@@ -220,3 +235,114 @@ def handler(request: httpx.Request) -> httpx.Response:
220235 assert exc_info .value .retry_after == 30
221236 assert isinstance (exc_info .value .body , dict )
222237 assert exc_info .value .body ["message" ] == "too many"
238+
239+
240+ def test_upsert_webhook_subscription_success () -> None :
241+ subscription = _webhook_subscription_payload ()
242+ request_payload = {
243+ "callback_url" : "https://integrator.example/webhooks/axme" ,
244+ "event_types" : ["inbox.thread_created" ],
245+ "active" : True ,
246+ }
247+
248+ def handler (request : httpx .Request ) -> httpx .Response :
249+ assert request .method == "POST"
250+ assert request .url .path == "/v1/webhooks/subscriptions"
251+ assert request .headers ["idempotency-key" ] == "wh-1"
252+ assert request .read () == b'{"callback_url":"https://integrator.example/webhooks/axme","event_types":["inbox.thread_created"],"active":true}'
253+ return httpx .Response (200 , json = {"ok" : True , "subscription" : subscription })
254+
255+ client = _client (handler )
256+ assert client .upsert_webhook_subscription (request_payload , idempotency_key = "wh-1" ) == {
257+ "ok" : True ,
258+ "subscription" : subscription ,
259+ }
260+
261+
262+ def test_list_webhook_subscriptions_success () -> None :
263+ subscription = _webhook_subscription_payload ()
264+
265+ def handler (request : httpx .Request ) -> httpx .Response :
266+ assert request .method == "GET"
267+ assert request .url .path == "/v1/webhooks/subscriptions"
268+ assert request .url .params .get ("owner_agent" ) == "agent://owner"
269+ return httpx .Response (200 , json = {"ok" : True , "subscriptions" : [subscription ]})
270+
271+ client = _client (handler )
272+ assert client .list_webhook_subscriptions (owner_agent = "agent://owner" ) == {
273+ "ok" : True ,
274+ "subscriptions" : [subscription ],
275+ }
276+
277+
278+ def test_delete_webhook_subscription_success () -> None :
279+ subscription_id = "44444444-4444-4444-8444-444444444444"
280+
281+ def handler (request : httpx .Request ) -> httpx .Response :
282+ assert request .method == "DELETE"
283+ assert request .url .path == f"/v1/webhooks/subscriptions/{ subscription_id } "
284+ assert request .url .params .get ("owner_agent" ) == "agent://owner"
285+ return httpx .Response (200 , json = {"ok" : True , "subscription_id" : subscription_id , "revoked_at" : "2026-02-28T00:00:03Z" })
286+
287+ client = _client (handler )
288+ assert client .delete_webhook_subscription (subscription_id , owner_agent = "agent://owner" ) == {
289+ "ok" : True ,
290+ "subscription_id" : subscription_id ,
291+ "revoked_at" : "2026-02-28T00:00:03Z" ,
292+ }
293+
294+
295+ def test_publish_webhook_event_success () -> None :
296+ event_id = "33333333-3333-4333-8333-333333333333"
297+ request_payload = {"event_type" : "inbox.thread_created" , "source" : "sdk-test" , "payload" : {"thread_id" : "t-1" }}
298+
299+ def handler (request : httpx .Request ) -> httpx .Response :
300+ assert request .method == "POST"
301+ assert request .url .path == "/v1/webhooks/events"
302+ assert request .url .params .get ("owner_agent" ) == "agent://owner"
303+ return httpx .Response (
304+ 200 ,
305+ json = {
306+ "ok" : True ,
307+ "accepted_at" : "2026-02-28T00:00:01Z" ,
308+ "event_type" : "inbox.thread_created" ,
309+ "source" : "sdk-test" ,
310+ "owner_agent" : "agent://owner" ,
311+ "event_id" : event_id ,
312+ "queued_deliveries" : 1 ,
313+ "processed_deliveries" : 1 ,
314+ "delivered" : 1 ,
315+ "pending" : 0 ,
316+ "dead_lettered" : 0 ,
317+ },
318+ )
319+
320+ client = _client (handler )
321+ assert client .publish_webhook_event (request_payload , owner_agent = "agent://owner" )["event_id" ] == event_id
322+
323+
324+ def test_replay_webhook_event_success () -> None :
325+ event_id = "33333333-3333-4333-8333-333333333333"
326+
327+ def handler (request : httpx .Request ) -> httpx .Response :
328+ assert request .method == "POST"
329+ assert request .url .path == f"/v1/webhooks/events/{ event_id } /replay"
330+ assert request .url .params .get ("owner_agent" ) == "agent://owner"
331+ return httpx .Response (
332+ 200 ,
333+ json = {
334+ "ok" : True ,
335+ "event_id" : event_id ,
336+ "owner_agent" : "agent://owner" ,
337+ "event_type" : "inbox.thread_created" ,
338+ "queued_deliveries" : 1 ,
339+ "processed_deliveries" : 1 ,
340+ "delivered" : 1 ,
341+ "pending" : 0 ,
342+ "dead_lettered" : 0 ,
343+ "replayed_at" : "2026-02-28T00:00:02Z" ,
344+ },
345+ )
346+
347+ client = _client (handler )
348+ assert client .replay_webhook_event (event_id , owner_agent = "agent://owner" )["event_id" ] == event_id
0 commit comments