Skip to content

Commit f0b08a1

Browse files
committed
add unit test
Signed-off-by: tdruez <tdruez@aboutcode.org>
1 parent f83e09b commit f0b08a1

2 files changed

Lines changed: 143 additions & 4 deletions

File tree

notification/tasks.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88

99
import logging
1010

11+
from django.apps import apps
12+
1113
from django_rq import job
1214

15+
from notification.models import WebhookSubscription
16+
1317
logger = logging.getLogger("dje")
1418

1519

@@ -22,10 +26,6 @@ def deliver_webhook_task(
2226
instance_pk=None,
2327
):
2428
"""Deliver a webhook payload to the target URL of the given WebhookSubscription."""
25-
from django.apps import apps
26-
27-
from notification.models import WebhookSubscription
28-
2929
try:
3030
webhook_subscription = WebhookSubscription.objects.get(pk=webhook_subscription_pk)
3131
except WebhookSubscription.DoesNotExist:

notification/tests/test_models.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,42 @@
66
# See https://aboutcode.org for more information about AboutCode FOSS projects.
77
#
88

9+
import json
10+
from unittest.mock import MagicMock
11+
from unittest.mock import patch
12+
913
from django.test import TestCase
1014
from django.test.utils import override_settings
1115

16+
import requests as req
17+
1218
from dje.models import Dataspace
1319
from dje.tests import create_superuser
20+
from notification.models import WebhookDelivery
1421
from notification.models import WebhookSubscription
22+
from notification.models import fire_webhooks
23+
24+
25+
class WebhookSubscriptionQuerySetTestCase(TestCase):
26+
def setUp(self):
27+
self.nexb_dataspace = Dataspace.objects.create(name="nexB")
28+
self.active_webhook = WebhookSubscription.objects.create(
29+
dataspace=self.nexb_dataspace,
30+
target_url="http://1.2.3.4/",
31+
event="request.added",
32+
is_active=True,
33+
)
34+
self.inactive_webhook = WebhookSubscription.objects.create(
35+
dataspace=self.nexb_dataspace,
36+
target_url="http://1.2.3.5/",
37+
event="request.added",
38+
is_active=False,
39+
)
40+
41+
def test_active_returns_only_active_subscriptions(self):
42+
active = list(WebhookSubscription.objects.active())
43+
self.assertIn(self.active_webhook, active)
44+
self.assertNotIn(self.inactive_webhook, active)
1545

1646

1747
class WebhookSubscriptionModelTestCase(TestCase):
@@ -36,6 +66,15 @@ def test_webhook_subscription_dict(self):
3666
}
3767
self.assertEqual(expected, self.webhook.dict())
3868

69+
def test_webhook_subscription_get_headers_default(self):
70+
self.assertEqual({"Content-Type": "application/json"}, self.webhook.get_headers())
71+
72+
def test_webhook_subscription_get_headers_with_extra(self):
73+
self.webhook.extra_headers = {"X-Token": "abc"}
74+
self.webhook.save()
75+
expected = {"Content-Type": "application/json", "X-Token": "abc"}
76+
self.assertEqual(expected, self.webhook.get_headers())
77+
3978
def test_webhook_subscription_get_extra_headers(self):
4079
self.webhook.extra_headers = {"Header": "{{ENV_VALUE}}"}
4180
self.webhook.save()
@@ -46,3 +85,103 @@ def test_webhook_subscription_get_extra_headers(self):
4685
expected = {"Header": "some_value"}
4786
with override_settings(DEJACODE_WEBHOOK_ENV={"ENV_VALUE": "some_value"}):
4887
self.assertEqual(expected, self.webhook.get_extra_headers())
88+
89+
@patch("requests.post")
90+
def test_webhook_subscription_deliver_inactive_returns_false(self, mock_post):
91+
self.webhook.is_active = False
92+
self.webhook.save()
93+
result = self.webhook.deliver(None, payload_override={"key": "val"})
94+
self.assertFalse(result)
95+
mock_post.assert_not_called()
96+
97+
@patch("requests.post")
98+
def test_webhook_subscription_deliver_request_exception_saves_error(self, mock_post):
99+
mock_post.side_effect = req.exceptions.ConnectionError("Connection refused")
100+
delivery = self.webhook.deliver(None, payload_override={"key": "val"})
101+
self.assertIsNotNone(delivery)
102+
self.assertFalse(delivery.delivered)
103+
self.assertIn("Connection refused", delivery.delivery_error)
104+
105+
@patch("requests.post")
106+
def test_webhook_subscription_deliver_payload_override_merges_extra_payload(self, mock_post):
107+
mock_response = MagicMock()
108+
mock_response.status_code = 200
109+
mock_response.text = ""
110+
mock_post.return_value = mock_response
111+
self.webhook.extra_payload = {"env": "prod"}
112+
self.webhook.save()
113+
self.webhook.deliver(None, payload_override={"key": "val"})
114+
call_data = json.loads(mock_post.call_args[1]["data"])
115+
self.assertEqual("prod", call_data["env"])
116+
self.assertEqual("val", call_data["key"])
117+
118+
@patch("requests.post")
119+
def test_webhook_subscription_deliver_payload_override_without_extra_payload(self, mock_post):
120+
mock_response = MagicMock()
121+
mock_response.status_code = 200
122+
mock_response.text = ""
123+
mock_post.return_value = mock_response
124+
self.webhook.deliver(None, payload_override={"key": "val"})
125+
call_data = json.loads(mock_post.call_args[1]["data"])
126+
self.assertEqual({"key": "val"}, call_data)
127+
128+
129+
class WebhookDeliveryModelTestCase(TestCase):
130+
def setUp(self):
131+
self.nexb_dataspace = Dataspace.objects.create(name="nexB")
132+
self.webhook = WebhookSubscription.objects.create(
133+
dataspace=self.nexb_dataspace,
134+
target_url="http://1.2.3.4/",
135+
event="request.added",
136+
)
137+
self.delivery = WebhookDelivery.objects.create(
138+
dataspace=self.nexb_dataspace,
139+
webhook_subscription=self.webhook,
140+
target_url=self.webhook.target_url,
141+
payload={"key": "value"},
142+
)
143+
144+
def test_webhook_delivery_str(self):
145+
self.assertIn(f"uuid={self.delivery.uuid}", str(self.delivery))
146+
147+
def test_webhook_delivery_delivered_false_when_no_status_code(self):
148+
self.assertFalse(self.delivery.delivered)
149+
150+
def test_webhook_delivery_delivered_true_when_status_code_set(self):
151+
self.delivery.response_status_code = 200
152+
self.assertTrue(self.delivery.delivered)
153+
154+
def test_webhook_delivery_success_on_2xx(self):
155+
for status_code in (200, 201, 202):
156+
self.delivery.response_status_code = status_code
157+
self.assertTrue(self.delivery.success, f"Expected success for {status_code}")
158+
159+
def test_webhook_delivery_success_false_on_non_2xx(self):
160+
for status_code in (400, 404, 500):
161+
self.delivery.response_status_code = status_code
162+
self.assertFalse(self.delivery.success, f"Expected failure for {status_code}")
163+
164+
165+
class FireWebhooksTestCase(TestCase):
166+
def setUp(self):
167+
self.nexb_dataspace = Dataspace.objects.create(name="nexB")
168+
169+
def test_fire_webhooks_requires_dataspace_or_instance(self):
170+
with self.assertRaises(AttributeError):
171+
fire_webhooks("request.added", None)
172+
173+
@patch("requests.post")
174+
def test_fire_webhooks_no_matching_event_makes_no_request(self, mock_post):
175+
fire_webhooks("no.such.event", None, dataspace=self.nexb_dataspace)
176+
mock_post.assert_not_called()
177+
178+
@patch("requests.post")
179+
def test_fire_webhooks_skips_inactive_subscriptions(self, mock_post):
180+
WebhookSubscription.objects.create(
181+
dataspace=self.nexb_dataspace,
182+
target_url="http://1.2.3.4/",
183+
event="request.added",
184+
is_active=False,
185+
)
186+
fire_webhooks("request.added", None, dataspace=self.nexb_dataspace)
187+
mock_post.assert_not_called()

0 commit comments

Comments
 (0)