11"""Tests for the webhook alerter module."""
22
3+ import json
34from datetime import UTC , datetime
45from unittest .mock import MagicMock , Mock , patch
56
@@ -217,29 +218,30 @@ def test_build_payload_up_event(self, alerter: Alerter, check_result_up: CheckRe
217218 assert payload ["status" ]["success" ] is True
218219 assert payload ["previous_status" ] == "down"
219220
220- @patch ("webstatuspi.alerter.requests.post " )
221- def test_send_webhook_success (self , mock_post : Mock , alerter : Alerter , check_result_down : CheckResult ) -> None :
221+ @patch ("webstatuspi.alerter.urllib.request.urlopen " )
222+ def test_send_webhook_success (self , mock_urlopen : Mock , alerter : Alerter , check_result_down : CheckResult ) -> None :
222223 """Test successful webhook delivery."""
223- mock_response = MagicMock ()
224- mock_response . raise_for_status . return_value = None
225- mock_post .return_value = mock_response
224+ mock_cm = MagicMock ()
225+ mock_urlopen . return_value . __enter__ = Mock ( return_value = mock_cm )
226+ mock_urlopen .return_value . __exit__ = Mock ( return_value = False )
226227
227228 webhook = alerter ._config .webhooks [0 ]
228229 alerter ._send_webhook (webhook , check_result_down )
229230
230- mock_post .assert_called_once ()
231- args , kwargs = mock_post .call_args
232- assert args [0 ] == "https://example.com/webhook"
233- assert kwargs ["timeout" ] == 10
234- assert isinstance (kwargs ["json" ], dict )
235- assert "event" in kwargs ["json" ]
231+ mock_urlopen .assert_called_once ()
232+ call_args = mock_urlopen .call_args
233+ req = call_args [0 ][0 ]
234+ assert req .full_url == "https://example.com/webhook"
235+ assert call_args [1 ]["timeout" ] == 10
236+ payload = json .loads (req .data )
237+ assert "event" in payload
236238
237- @patch ("webstatuspi.alerter.requests.post " )
238- def test_send_webhook_retry_on_failure (self , mock_post : Mock , check_result_down : CheckResult ) -> None :
239+ @patch ("webstatuspi.alerter.urllib.request.urlopen " )
240+ def test_send_webhook_retry_on_failure (self , mock_urlopen : Mock , check_result_down : CheckResult ) -> None :
239241 """Test that webhook retries on failure."""
240- import requests
242+ import urllib . error
241243
242- mock_post .side_effect = requests . RequestException ("Connection error" )
244+ mock_urlopen .side_effect = urllib . error . URLError ("Connection error" )
243245
244246 webhook = WebhookConfig (
245247 url = "https://example.com/webhook" ,
@@ -253,20 +255,21 @@ def test_send_webhook_retry_on_failure(self, mock_post: Mock, check_result_down:
253255 alerter ._send_webhook (webhook , check_result_down )
254256
255257 # Should attempt 3 times (initial + 2 retries)
256- assert mock_post .call_count == 3
258+ assert mock_urlopen .call_count == 3
257259
258- @patch ("webstatuspi.alerter.requests.post " )
259- def test_send_webhook_success_after_retry (self , mock_post : Mock , check_result_down : CheckResult ) -> None :
260+ @patch ("webstatuspi.alerter.urllib.request.urlopen " )
261+ def test_send_webhook_success_after_retry (self , mock_urlopen : Mock , check_result_down : CheckResult ) -> None :
260262 """Test successful delivery after retry."""
261- mock_response = MagicMock ()
262- mock_response .raise_for_status .return_value = None
263+ import urllib .error
263264
264- import requests
265+ mock_cm = MagicMock ()
266+ mock_cm .__enter__ = Mock (return_value = mock_cm )
267+ mock_cm .__exit__ = Mock (return_value = False )
265268
266269 # Fail first, succeed second
267- mock_post .side_effect = [
268- requests . RequestException ("Connection error" ),
269- mock_response ,
270+ mock_urlopen .side_effect = [
271+ urllib . error . URLError ("Connection error" ),
272+ mock_cm ,
270273 ]
271274
272275 webhook = WebhookConfig (
@@ -281,26 +284,26 @@ def test_send_webhook_success_after_retry(self, mock_post: Mock, check_result_do
281284 alerter ._send_webhook (webhook , check_result_down )
282285
283286 # Should succeed after retry
284- assert mock_post .call_count == 2
287+ assert mock_urlopen .call_count == 2
285288
286- @patch ("webstatuspi.alerter.requests.post " )
287- def test_test_webhooks_all_success (self , mock_post : Mock , alerter : Alerter ) -> None :
289+ @patch ("webstatuspi.alerter.urllib.request.urlopen " )
290+ def test_test_webhooks_all_success (self , mock_urlopen : Mock , alerter : Alerter ) -> None :
288291 """Test successful webhook testing."""
289- mock_response = MagicMock ()
290- mock_response . raise_for_status . return_value = None
291- mock_post .return_value = mock_response
292+ mock_cm = MagicMock ()
293+ mock_urlopen . return_value . __enter__ = Mock ( return_value = mock_cm )
294+ mock_urlopen .return_value . __exit__ = Mock ( return_value = False )
292295
293296 results = alerter .test_webhooks ()
294297
295298 assert results ["https://example.com/webhook" ] is True
296- mock_post .assert_called_once ()
299+ mock_urlopen .assert_called_once ()
297300
298- @patch ("webstatuspi.alerter.requests.post " )
299- def test_test_webhooks_failure (self , mock_post : Mock , alerter : Alerter ) -> None :
301+ @patch ("webstatuspi.alerter.urllib.request.urlopen " )
302+ def test_test_webhooks_failure (self , mock_urlopen : Mock , alerter : Alerter ) -> None :
300303 """Test failed webhook testing."""
301- import requests
304+ import urllib . error
302305
303- mock_post .side_effect = requests . RequestException ("Connection error" )
306+ mock_urlopen .side_effect = urllib . error . URLError ("Connection error" )
304307
305308 results = alerter .test_webhooks ()
306309
@@ -314,10 +317,10 @@ def test_test_webhooks_disabled_webhook(self) -> None:
314317 )
315318 alerter = Alerter (AlertsConfig (webhooks = [webhook ]))
316319
317- with patch ("webstatuspi.alerter.requests.post " ) as mock_post :
320+ with patch ("webstatuspi.alerter.urllib.request.urlopen " ) as mock_urlopen :
318321 results = alerter .test_webhooks ()
319322 assert results ["https://example.com/webhook" ] is False
320- mock_post .assert_not_called ()
323+ mock_urlopen .assert_not_called ()
321324
322325 def test_multiple_webhooks (self ) -> None :
323326 """Test alerter with multiple webhooks."""
@@ -476,25 +479,26 @@ def test_latency_counter_reset_on_normal(self, alerter: Alerter, url_config_with
476479 assert alerter ._state_tracker .consecutive_slow .get ("test_url" , 0 ) == 0
477480 mock_send .assert_not_called () # No alert since we never reached threshold
478481
479- @patch ("webstatuspi.alerter.requests.post " )
482+ @patch ("webstatuspi.alerter.urllib.request.urlopen " )
480483 def test_send_latency_webhook_success (
481- self , mock_post : Mock , alerter : Alerter , url_config_with_threshold : UrlConfig
484+ self , mock_urlopen : Mock , alerter : Alerter , url_config_with_threshold : UrlConfig
482485 ) -> None :
483486 """Test successful latency webhook delivery."""
484- mock_response = MagicMock ()
485- mock_response . raise_for_status . return_value = None
486- mock_post .return_value = mock_response
487+ mock_cm = MagicMock ()
488+ mock_urlopen . return_value . __enter__ = Mock ( return_value = mock_cm )
489+ mock_urlopen .return_value . __exit__ = Mock ( return_value = False )
487490
488491 # Trigger alert
489492 for _ in range (3 ):
490493 alerter .check_latency_alert (url_config_with_threshold , 1500 )
491494
492- mock_post .assert_called_once ()
493- args , kwargs = mock_post .call_args
494- assert args [0 ] == "https://example.com/webhook"
495- assert kwargs ["timeout" ] == 10
495+ mock_urlopen .assert_called_once ()
496+ call_args = mock_urlopen .call_args
497+ req = call_args [0 ][0 ]
498+ assert req .full_url == "https://example.com/webhook"
499+ assert call_args [1 ]["timeout" ] == 10
496500
497- payload = kwargs [ " json" ]
501+ payload = json . loads ( req . data )
498502 assert payload ["event" ] == "latency_high"
499503 assert payload ["url" ]["name" ] == "test_url"
500504 assert payload ["url" ]["url" ] == "https://example.com"
0 commit comments