130 lines
5.1 KiB
Python
130 lines
5.1 KiB
Python
from contextlib import contextmanager
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from django.db import transaction
|
|
|
|
from infrasynth.webhooks.models import (
|
|
OutboundDelivery,
|
|
OutboundEndpoint,
|
|
OutboundSubscription,
|
|
)
|
|
from infrasynth.webhooks.registry import EventDefinition, EventRegistry
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
@contextmanager
|
|
def _emit_and_run_commit_hooks():
|
|
with transaction.atomic():
|
|
yield
|
|
connection = transaction.get_connection()
|
|
pending = list(connection.run_on_commit)
|
|
connection.run_on_commit = []
|
|
for entry in pending:
|
|
func = entry[1]
|
|
func()
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def clean_registry():
|
|
EventRegistry._events.clear()
|
|
yield
|
|
EventRegistry._events.clear()
|
|
|
|
|
|
class TestRegister:
|
|
def test_register_creates_definition(self):
|
|
EventRegistry.register("helpdesk.ticket.created")
|
|
event = EventRegistry.get_registered_events()["helpdesk.ticket.created"]
|
|
assert isinstance(event, EventDefinition)
|
|
assert event.name == "helpdesk.ticket.created"
|
|
|
|
def test_register_with_metadata(self):
|
|
EventRegistry.register(
|
|
"helpdesk.ticket.resolved",
|
|
description="Ticket resolved",
|
|
example_payload={"ticket_id": 1},
|
|
schema={"type": "object"},
|
|
)
|
|
event = EventRegistry.get_registered_events()["helpdesk.ticket.resolved"]
|
|
assert event.description == "Ticket resolved"
|
|
assert event.example_payload == {"ticket_id": 1}
|
|
assert event.schema == {"type": "object"}
|
|
|
|
def test_get_registered_events_returns_copy(self):
|
|
EventRegistry.register("evt.a")
|
|
events = EventRegistry.get_registered_events()
|
|
events["evt.a"] = EventDefinition(name="mutated")
|
|
assert "evt.a" in EventRegistry.get_registered_events()
|
|
assert EventRegistry.get_registered_events()["evt.a"].name == "evt.a"
|
|
|
|
|
|
class TestEmit:
|
|
@pytest.fixture
|
|
def endpoint(self):
|
|
return OutboundEndpoint.objects.create(
|
|
name="Target",
|
|
url="https://example.com/hook",
|
|
secret="test-secret",
|
|
)
|
|
|
|
def test_emit_delivers_to_subscriber(self, endpoint):
|
|
subscription = OutboundSubscription.objects.create(endpoint=endpoint, event_name="helpdesk.ticket.created")
|
|
with mock.patch("infrasynth.webhooks.dispatch.requests.post") as post:
|
|
post.return_value.status_code = 200
|
|
post.return_value.text = "ok"
|
|
with _emit_and_run_commit_hooks():
|
|
EventRegistry.emit("helpdesk.ticket.created", {"ticket_id": 1})
|
|
|
|
delivery = OutboundDelivery.objects.get(subscription=subscription)
|
|
assert delivery.status == OutboundDelivery.Status.SUCCESS
|
|
assert delivery.payload == '{"ticket_id": 1}'
|
|
|
|
def test_emit_wildcard_subscription(self, endpoint):
|
|
subscription = OutboundSubscription.objects.create(endpoint=endpoint, event_name="*")
|
|
with mock.patch("infrasynth.webhooks.dispatch.requests.post") as post:
|
|
post.return_value.status_code = 200
|
|
post.return_value.text = "ok"
|
|
with _emit_and_run_commit_hooks():
|
|
EventRegistry.emit("any.event", {"x": 1})
|
|
|
|
assert OutboundDelivery.objects.filter(subscription=subscription).exists()
|
|
|
|
def test_emit_inactive_subscription_skipped(self, endpoint):
|
|
OutboundSubscription.objects.create(endpoint=endpoint, event_name="evt.x", is_active=False)
|
|
with mock.patch("infrasynth.webhooks.dispatch.requests.post") as post:
|
|
with _emit_and_run_commit_hooks():
|
|
EventRegistry.emit("evt.x", {})
|
|
post.assert_not_called()
|
|
assert not OutboundDelivery.objects.exists()
|
|
|
|
def test_emit_inactive_endpoint_skipped(self, endpoint):
|
|
OutboundSubscription.objects.create(endpoint=endpoint, event_name="evt.x", is_active=True)
|
|
endpoint.is_active = False
|
|
endpoint.save(update_fields=["is_active"])
|
|
with mock.patch("infrasynth.webhooks.dispatch.requests.post") as post:
|
|
with _emit_and_run_commit_hooks():
|
|
EventRegistry.emit("evt.x", {})
|
|
post.assert_not_called()
|
|
|
|
def test_emit_no_subscriptions_noop(self):
|
|
with mock.patch("infrasynth.webhooks.dispatch.requests.post") as post:
|
|
with _emit_and_run_commit_hooks():
|
|
EventRegistry.emit("unsubscribed.event", {})
|
|
post.assert_not_called()
|
|
|
|
def test_emit_sends_payload_template(self, endpoint):
|
|
subscription = OutboundSubscription.objects.create(
|
|
endpoint=endpoint,
|
|
event_name="evt.template",
|
|
payload_template='{"event": "{{ event.type }}"}',
|
|
)
|
|
with mock.patch("infrasynth.webhooks.dispatch.requests.post") as post:
|
|
post.return_value.status_code = 200
|
|
post.return_value.text = "ok"
|
|
with _emit_and_run_commit_hooks():
|
|
EventRegistry.emit("evt.template", {"type": "created"})
|
|
|
|
delivery = OutboundDelivery.objects.get(subscription=subscription)
|
|
assert delivery.payload == '{"event": "created"}'
|