232 lines
8.3 KiB
Python
232 lines
8.3 KiB
Python
import pytest
|
|
|
|
from infrasynth.webhooks.models import (
|
|
InboundEndpoint,
|
|
InboundEvent,
|
|
OutboundDelivery,
|
|
OutboundEndpoint,
|
|
OutboundSubscription,
|
|
)
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
OUTBOUND_ENDPOINTS_URL = "/api/webhooks/outbound/endpoints/"
|
|
OUTBOUND_SUBSCRIPTIONS_URL = "/api/webhooks/outbound/subscriptions/"
|
|
OUTBOUND_DELIVERIES_URL = "/api/webhooks/outbound/deliveries/"
|
|
INBOUND_ENDPOINTS_URL = "/api/webhooks/inbound/endpoints/"
|
|
INBOUND_EVENTS_URL = "/api/webhooks/inbound/events/"
|
|
|
|
|
|
@pytest.fixture
|
|
def endpoint():
|
|
return OutboundEndpoint.objects.create(
|
|
name="Target",
|
|
url="https://example.com/hook",
|
|
secret="test-secret",
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def subscription(endpoint):
|
|
return OutboundSubscription.objects.create(endpoint=endpoint, event_name="evt.test")
|
|
|
|
|
|
@pytest.fixture
|
|
def inbound_endpoint():
|
|
return InboundEndpoint.objects.create(
|
|
name="Stripe",
|
|
slug="stripe",
|
|
source="stripe",
|
|
secret="wh-secret",
|
|
handler="infrasynth.webhooks.inbound.handlers.BaseInboundHandler",
|
|
)
|
|
|
|
|
|
class TestOutboundEndpointViewSet:
|
|
def test_list(self, authenticated_client, endpoint):
|
|
response = authenticated_client.get(OUTBOUND_ENDPOINTS_URL)
|
|
assert response.status_code == 200
|
|
assert response.data["count"] == 1
|
|
assert response.data["results"][0]["name"] == "Target"
|
|
|
|
def test_requires_auth(self, api_client, endpoint):
|
|
assert api_client.get(OUTBOUND_ENDPOINTS_URL).status_code == 401
|
|
|
|
def test_create(self, authenticated_client):
|
|
response = authenticated_client.post(
|
|
OUTBOUND_ENDPOINTS_URL,
|
|
{
|
|
"name": "New",
|
|
"url": "https://example.com/new",
|
|
"secret": "s",
|
|
},
|
|
format="json",
|
|
)
|
|
assert response.status_code == 201
|
|
assert OutboundEndpoint.objects.filter(name="New").exists()
|
|
|
|
def test_update_and_delete(self, authenticated_client, endpoint):
|
|
response = authenticated_client.patch(
|
|
f"{OUTBOUND_ENDPOINTS_URL}{endpoint.id}/",
|
|
{"name": "Renamed"},
|
|
format="json",
|
|
)
|
|
assert response.status_code == 200
|
|
response = authenticated_client.delete(f"{OUTBOUND_ENDPOINTS_URL}{endpoint.id}/")
|
|
assert response.status_code == 204
|
|
assert not OutboundEndpoint.objects.filter(pk=endpoint.pk).exists()
|
|
|
|
|
|
class TestOutboundSubscriptionViewSet:
|
|
def test_list(self, authenticated_client, subscription):
|
|
response = authenticated_client.get(OUTBOUND_SUBSCRIPTIONS_URL)
|
|
assert response.status_code == 200
|
|
assert response.data["count"] == 1
|
|
assert response.data["results"][0]["event_name"] == "evt.test"
|
|
|
|
def test_list_includes_endpoint_info(self, authenticated_client, subscription):
|
|
response = authenticated_client.get(OUTBOUND_SUBSCRIPTIONS_URL)
|
|
assert response.data["results"][0]["endpoint_info"]["name"] == "Target"
|
|
|
|
def test_create(self, authenticated_client, endpoint):
|
|
response = authenticated_client.post(
|
|
OUTBOUND_SUBSCRIPTIONS_URL,
|
|
{"endpoint": endpoint.id, "event_name": "evt.new"},
|
|
format="json",
|
|
)
|
|
assert response.status_code == 201
|
|
assert OutboundSubscription.objects.filter(event_name="evt.new").exists()
|
|
|
|
def test_requires_auth(self, api_client, subscription):
|
|
assert api_client.get(OUTBOUND_SUBSCRIPTIONS_URL).status_code == 401
|
|
|
|
def test_duplicate_event_rejected(self, authenticated_client, subscription):
|
|
response = authenticated_client.post(
|
|
OUTBOUND_SUBSCRIPTIONS_URL,
|
|
{
|
|
"endpoint": subscription.endpoint_id,
|
|
"event_name": "evt.test",
|
|
},
|
|
format="json",
|
|
)
|
|
assert response.status_code == 400
|
|
|
|
|
|
class TestOutboundDeliveryViewSet:
|
|
def test_list(self, authenticated_client, subscription):
|
|
OutboundDelivery.objects.create(subscription=subscription, payload={"a": 1}, status="success")
|
|
response = authenticated_client.get(OUTBOUND_DELIVERIES_URL)
|
|
assert response.status_code == 200
|
|
assert response.data["count"] == 1
|
|
assert response.data["results"][0]["payload"] == {"a": 1}
|
|
|
|
def test_does_not_allow_create(self, authenticated_client):
|
|
assert authenticated_client.post(OUTBOUND_DELIVERIES_URL, {}, format="json").status_code == 405
|
|
|
|
def test_requires_auth(self, api_client):
|
|
assert api_client.get(OUTBOUND_DELIVERIES_URL).status_code == 401
|
|
|
|
|
|
class TestInboundEndpointViewSet:
|
|
def test_list(self, authenticated_client, inbound_endpoint):
|
|
response = authenticated_client.get(INBOUND_ENDPOINTS_URL)
|
|
assert response.status_code == 200
|
|
assert response.data["count"] == 1
|
|
assert response.data["results"][0]["slug"] == "stripe"
|
|
|
|
def test_create(self, authenticated_client):
|
|
response = authenticated_client.post(
|
|
INBOUND_ENDPOINTS_URL,
|
|
{
|
|
"name": "GitHub",
|
|
"slug": "github",
|
|
"source": "github",
|
|
"secret": "s",
|
|
"handler": "helpdesk.webhook_handlers.JiraWebhookHandler",
|
|
},
|
|
format="json",
|
|
)
|
|
assert response.status_code == 201
|
|
assert InboundEndpoint.objects.filter(slug="github").exists()
|
|
|
|
def test_requires_auth(self, api_client, inbound_endpoint):
|
|
assert api_client.get(INBOUND_ENDPOINTS_URL).status_code == 401
|
|
|
|
|
|
class TestInboundEventViewSet:
|
|
def test_list(self, authenticated_client, inbound_endpoint):
|
|
InboundEvent.objects.create(
|
|
endpoint=inbound_endpoint,
|
|
event_type="invoice.paid",
|
|
raw_payload={"id": "inv_1"},
|
|
)
|
|
response = authenticated_client.get(INBOUND_EVENTS_URL)
|
|
assert response.status_code == 200
|
|
assert response.data["count"] == 1
|
|
assert response.data["results"][0]["event_type"] == "invoice.paid"
|
|
|
|
def test_requires_auth(self, api_client, inbound_endpoint):
|
|
assert api_client.get(INBOUND_EVENTS_URL).status_code == 401
|
|
|
|
|
|
class TestInboundReceiveView:
|
|
URL = "/api/webhooks/inbound/receive/{slug}/"
|
|
|
|
def test_receive_creates_event(self, api_client, inbound_endpoint):
|
|
response = api_client.post(
|
|
self.URL.format(slug="stripe"),
|
|
{
|
|
"payload": {"type": "invoice.paid"},
|
|
"headers": {"X-Event-Type": "invoice.paid"},
|
|
},
|
|
format="json",
|
|
)
|
|
assert response.status_code == 201
|
|
event = InboundEvent.objects.get(endpoint=inbound_endpoint)
|
|
assert event.event_type == "invoice.paid"
|
|
assert event.raw_payload == {"type": "invoice.paid"}
|
|
|
|
def test_receive_is_public(self, api_client, inbound_endpoint):
|
|
response = api_client.post(
|
|
self.URL.format(slug="stripe"),
|
|
{"payload": {}, "headers": {}},
|
|
format="json",
|
|
)
|
|
assert response.status_code == 201
|
|
|
|
def test_receive_unknown_slug_404(self, api_client):
|
|
response = api_client.post(
|
|
self.URL.format(slug="unknown"),
|
|
{"payload": {}, "headers": {}},
|
|
format="json",
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
def test_receive_inactive_endpoint_404(self, api_client, inbound_endpoint):
|
|
inbound_endpoint.is_active = False
|
|
inbound_endpoint.save(update_fields=["is_active"])
|
|
response = api_client.post(
|
|
self.URL.format(slug="stripe"),
|
|
{"payload": {}, "headers": {}},
|
|
format="json",
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
def test_receive_missing_payload_400(self, api_client, inbound_endpoint):
|
|
response = api_client.post(self.URL.format(slug="stripe"), {}, format="json")
|
|
assert response.status_code == 400
|
|
|
|
def test_receive_emits_signal(self, api_client, inbound_endpoint):
|
|
from infrasynth.webhooks.signals import inbound_event_received
|
|
|
|
sent = []
|
|
receiver = lambda **kw: sent.append(kw) # noqa: E731
|
|
inbound_event_received.connect(receiver, weak=False)
|
|
api_client.post(
|
|
self.URL.format(slug="stripe"),
|
|
{"payload": {"a": 1}, "headers": {}},
|
|
format="json",
|
|
)
|
|
assert sent
|
|
assert sent[0]["payload"] == {"a": 1}
|
|
inbound_event_received.disconnect(receiver)
|