141 lines
4.6 KiB
Python
141 lines
4.6 KiB
Python
"""Alert streak gate — consecutive over-threshold before broadcast."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
import pytest
|
|
|
|
from server.utils.alert_streak import (
|
|
process_metric_alerts,
|
|
record_over_threshold,
|
|
reset_streak,
|
|
streak_required,
|
|
)
|
|
|
|
|
|
def test_streak_required_minimum_one():
|
|
with patch("server.utils.alert_streak.settings") as mock_settings:
|
|
mock_settings.ALERT_STREAK_REQUIRED = 0
|
|
assert streak_required() == 1
|
|
mock_settings.ALERT_STREAK_REQUIRED = 3
|
|
assert streak_required() == 3
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_record_and_reset_streak():
|
|
redis = AsyncMock()
|
|
redis.incr = AsyncMock(return_value=2)
|
|
redis.expire = AsyncMock()
|
|
redis.delete = AsyncMock()
|
|
|
|
assert await record_over_threshold(redis, 10, "cpu") == 2
|
|
redis.incr.assert_called_once_with("alert_streak:10:cpu")
|
|
redis.expire.assert_called_once()
|
|
|
|
await reset_streak(redis, 10, "cpu")
|
|
redis.delete.assert_called_once_with("alert_streak:10:cpu")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_suppresses_until_streak_reached():
|
|
redis = AsyncMock()
|
|
redis.smembers = AsyncMock(return_value=set())
|
|
redis.incr = AsyncMock(side_effect=[1, 2])
|
|
redis.expire = AsyncMock()
|
|
redis.sadd = AsyncMock()
|
|
redis.delete = AsyncMock()
|
|
|
|
thresholds = {"cpu": 80, "mem": 80, "disk": 80}
|
|
system_info = {"cpu_usage": 95.0, "mem_usage": 10.0, "disk_usage": 10.0}
|
|
|
|
with patch("server.utils.alert_streak.streak_required", return_value=3):
|
|
with patch("server.utils.alert_streak.broadcast_alert", AsyncMock()) as mock_alert:
|
|
await process_metric_alerts(
|
|
redis=redis,
|
|
server_id=477,
|
|
server_name="test",
|
|
system_info=system_info,
|
|
thresholds=thresholds,
|
|
)
|
|
mock_alert.assert_not_called()
|
|
|
|
await process_metric_alerts(
|
|
redis=redis,
|
|
server_id=477,
|
|
server_name="test",
|
|
system_info=system_info,
|
|
thresholds=thresholds,
|
|
)
|
|
mock_alert.assert_not_called()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_fires_on_third_consecutive_sample():
|
|
redis = AsyncMock()
|
|
redis.smembers = AsyncMock(return_value=set())
|
|
redis.incr = AsyncMock(return_value=3)
|
|
redis.expire = AsyncMock()
|
|
redis.sadd = AsyncMock()
|
|
|
|
thresholds = {"cpu": 80, "mem": 80, "disk": 80}
|
|
system_info = {"cpu_usage": 100.0, "mem_usage": 10.0, "disk_usage": 10.0}
|
|
|
|
with patch("server.utils.alert_streak.streak_required", return_value=3):
|
|
with patch("server.utils.alert_streak.broadcast_alert", AsyncMock()) as mock_alert:
|
|
await process_metric_alerts(
|
|
redis=redis,
|
|
server_id=477,
|
|
server_name="飒遂",
|
|
system_info=system_info,
|
|
thresholds=thresholds,
|
|
)
|
|
mock_alert.assert_called_once_with(477, "cpu", 100.0, "飒遂")
|
|
redis.sadd.assert_called_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_does_not_rebroadcast_while_already_active():
|
|
redis = AsyncMock()
|
|
redis.smembers = AsyncMock(return_value={"cpu"})
|
|
redis.incr = AsyncMock(return_value=5)
|
|
redis.expire = AsyncMock()
|
|
redis.sadd = AsyncMock()
|
|
|
|
thresholds = {"cpu": 80, "mem": 80, "disk": 80}
|
|
system_info = {"cpu_usage": 100.0, "mem_usage": 10.0, "disk_usage": 10.0}
|
|
|
|
with patch("server.utils.alert_streak.streak_required", return_value=3):
|
|
with patch("server.utils.alert_streak.broadcast_alert", AsyncMock()) as mock_alert:
|
|
await process_metric_alerts(
|
|
redis=redis,
|
|
server_id=477,
|
|
server_name="飒遂",
|
|
system_info=system_info,
|
|
thresholds=thresholds,
|
|
)
|
|
mock_alert.assert_not_called()
|
|
redis.sadd.assert_not_called()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_reset_streak_on_recovery_path():
|
|
redis = AsyncMock()
|
|
redis.smembers = AsyncMock(return_value={"cpu"})
|
|
redis.delete = AsyncMock()
|
|
redis.srem = AsyncMock()
|
|
|
|
thresholds = {"cpu": 80, "mem": 80, "disk": 80}
|
|
system_info = {"cpu_usage": 40.0, "mem_usage": 10.0, "disk_usage": 10.0}
|
|
|
|
with patch("server.utils.alert_streak.broadcast_recovery", AsyncMock()) as mock_recovery:
|
|
await process_metric_alerts(
|
|
redis=redis,
|
|
server_id=477,
|
|
server_name="飒遂",
|
|
system_info=system_info,
|
|
thresholds=thresholds,
|
|
)
|
|
mock_recovery.assert_called_once_with(477, "cpu", 40.0, "飒遂")
|
|
redis.delete.assert_any_call("alert_streak:477:cpu")
|