test_alert_service.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. """Unit tests for alert service."""
  2. import json
  3. from unittest.mock import MagicMock, patch
  4. import pytest
  5. from services.alert_service import AlertService
  6. class TestAlertService:
  7. """Test cases for AlertService."""
  8. def test_check_threshold_greater_than(self):
  9. """Test check_threshold with > operator."""
  10. assert AlertService.check_threshold(10.0, ">", 5.0) is True
  11. assert AlertService.check_threshold(5.0, ">", 5.0) is False
  12. assert AlertService.check_threshold(3.0, ">", 5.0) is False
  13. assert AlertService.check_threshold(None, ">", 5.0) is False
  14. def test_check_threshold_less_than(self):
  15. """Test check_threshold with < operator."""
  16. assert AlertService.check_threshold(3.0, "<", 5.0) is True
  17. assert AlertService.check_threshold(5.0, "<", 5.0) is False
  18. assert AlertService.check_threshold(10.0, "<", 5.0) is False
  19. assert AlertService.check_threshold(None, "<", 5.0) is False
  20. def test_check_threshold_greater_than_or_equal(self):
  21. """Test check_threshold with >= operator."""
  22. assert AlertService.check_threshold(10.0, ">=", 5.0) is True
  23. assert AlertService.check_threshold(5.0, ">=", 5.0) is True
  24. assert AlertService.check_threshold(3.0, ">=", 5.0) is False
  25. def test_check_threshold_less_than_or_equal(self):
  26. """Test check_threshold with <= operator."""
  27. assert AlertService.check_threshold(3.0, "<=", 5.0) is True
  28. assert AlertService.check_threshold(5.0, "<=", 5.0) is True
  29. assert AlertService.check_threshold(10.0, "<=", 5.0) is False
  30. def test_check_threshold_equal(self):
  31. """Test check_threshold with = operator."""
  32. assert AlertService.check_threshold(5.0, "=", 5.0) is True
  33. assert AlertService.check_threshold(5.1, "=", 5.0) is False
  34. assert AlertService.check_threshold(4.9, "=", 5.0) is False
  35. def test_check_threshold_unknown_operator(self):
  36. """Test check_threshold with unknown operator."""
  37. assert AlertService.check_threshold(10.0, "unknown", 5.0) is False
  38. @patch("services.alert_service.redis_client")
  39. def test_create_alert_rule(self, mock_redis):
  40. """Test creating an alert rule."""
  41. # Mock app query
  42. with patch("services.alert_service.db") as mock_db:
  43. mock_app = MagicMock()
  44. mock_app.id = "app-123"
  45. mock_app.tenant_id = "tenant-456"
  46. mock_db.session.query.return_value.filter.return_value.first.return_value = mock_app
  47. # Mock redis operations
  48. mock_redis.hset.return_value = True
  49. mock_redis.expire.return_value = True
  50. mock_redis.sadd.return_value = True
  51. mock_redis.hgetall.return_value = {}
  52. data = {
  53. "name": "Test Alert",
  54. "description": "Test description",
  55. "metric_type": "error_rate",
  56. "operator": ">",
  57. "threshold_value": 5.0,
  58. "notification_channels": {"email": ["test@example.com"]},
  59. }
  60. result = AlertService.create_alert_rule("app-123", data, "user-789")
  61. # Verify redis operations were called
  62. assert mock_redis.hset.called
  63. assert mock_redis.expire.called
  64. assert mock_redis.sadd.called
  65. @patch("services.alert_service.redis_client")
  66. def test_get_alert_rules(self, mock_redis):
  67. """Test getting alert rules for an app."""
  68. # Mock redis operations
  69. mock_redis.smembers.return_value = [b"rule-1", b"rule-2"]
  70. mock_redis.hgetall.side_effect = [
  71. {
  72. b"id": b"rule-1",
  73. b"name": b"Rule 1",
  74. b"metric_type": b"error_rate",
  75. b"operator": b">",
  76. b"threshold_value": b"5.0",
  77. b"is_enabled": b"true",
  78. b"notification_channels": b'{"email": []}',
  79. b"check_interval_minutes": b"5",
  80. b"consecutive_triggers": b"2",
  81. b"trigger_count": b"0",
  82. b"consecutive_trigger_count": b"0",
  83. },
  84. {
  85. b"id": b"rule-2",
  86. b"name": b"Rule 2",
  87. b"metric_type": b"avg_response_time",
  88. b"operator": b">",
  89. b"threshold_value": b"1000.0",
  90. b"is_enabled": b"false",
  91. b"notification_channels": b'{"email": []}',
  92. b"check_interval_minutes": b"10",
  93. b"consecutive_triggers": b"3",
  94. b"trigger_count": b"5",
  95. b"consecutive_trigger_count": b"1",
  96. },
  97. ]
  98. rules = AlertService.get_alert_rules("app-123")
  99. assert len(rules) == 2
  100. assert rules[0]["id"] == "rule-1"
  101. assert rules[0]["is_enabled"] is True
  102. assert rules[0]["threshold_value"] == 5.0
  103. assert rules[1]["id"] == "rule-2"
  104. assert rules[1]["is_enabled"] is False
  105. assert rules[1]["trigger_count"] == 5
  106. @patch("services.alert_service.redis_client")
  107. @patch("services.alert_service.NotificationService")
  108. @patch("services.alert_service.db")
  109. def test_trigger_alert(self, mock_db, mock_notification_service, mock_redis):
  110. """Test triggering an alert."""
  111. # Mock app query
  112. mock_app = MagicMock()
  113. mock_app.name = "Test App"
  114. mock_db.session.query.return_value.filter.return_value.first.return_value = mock_app
  115. # Mock notification service
  116. mock_notification_service.send_alert_notification.return_value = {
  117. "email": {"status": "success"},
  118. }
  119. rule = {
  120. "id": "rule-123",
  121. "app_id": "app-123",
  122. "metric_type": "error_rate",
  123. "operator": ">",
  124. "threshold_value": 5.0,
  125. "notification_channels": {"email": ["test@example.com"]},
  126. "trigger_count": 0,
  127. }
  128. AlertService.trigger_alert(rule, 10.0)
  129. # Verify notification was sent
  130. mock_notification_service.send_alert_notification.assert_called_once()
  131. # Verify log was created
  132. assert mock_redis.hset.called
  133. assert mock_redis.zadd.called
  134. @patch("services.alert_service.redis_client")
  135. def test_get_alert_logs(self, mock_redis):
  136. """Test getting alert logs for a rule."""
  137. # Mock redis operations
  138. mock_redis.zrevrange.return_value = [b"log-1", b"log-2"]
  139. mock_redis.hgetall.side_effect = [
  140. {
  141. b"id": b"log-1",
  142. b"alert_rule_id": b"rule-123",
  143. b"metric_value": b"10.5",
  144. b"threshold_value": b"5.0",
  145. b"notification_status": b'{"email": {"status": "success"}}',
  146. b"error_message": b"",
  147. b"triggered_at": b"2024-10-28T10:00:00Z",
  148. },
  149. {
  150. b"id": b"log-2",
  151. b"alert_rule_id": b"rule-123",
  152. b"metric_value": b"8.2",
  153. b"threshold_value": b"5.0",
  154. b"notification_status": b'{"email": {"status": "success"}}',
  155. b"error_message": b"",
  156. b"triggered_at": b"2024-10-28T09:00:00Z",
  157. },
  158. ]
  159. logs = AlertService.get_alert_logs("rule-123", limit=100)
  160. assert len(logs) == 2
  161. assert logs[0]["id"] == "log-1"
  162. assert logs[0]["metric_value"] == 10.5
  163. assert logs[1]["id"] == "log-2"
  164. assert logs[1]["metric_value"] == 8.2