128 lines
4.5 KiB
Python
128 lines
4.5 KiB
Python
|
|
"""Scheduled offline daily Telegram report."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from datetime import datetime, timezone
|
||
|
|
from unittest.mock import AsyncMock, patch
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from server.application.services import offline_daily_report_schedule as sched
|
||
|
|
from server.background.schedule_runner import _cron_match
|
||
|
|
|
||
|
|
|
||
|
|
def test_cron_matches_beijing_1150():
|
||
|
|
# 2026-06-17 11:50 Beijing = 2026-06-17 03:50 UTC
|
||
|
|
at = datetime(2026, 6, 17, 3, 50, tzinfo=timezone.utc)
|
||
|
|
assert _cron_match("50 11 * * *", at) is True
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_run_scheduled_sends_telegram(monkeypatch):
|
||
|
|
monkeypatch.setattr(sched.settings, "OFFLINE_DAILY_REPORT_ENABLED", "true")
|
||
|
|
monkeypatch.setattr(sched, "_health_check_enabled", lambda: False)
|
||
|
|
monkeypatch.setattr(sched, "_already_ran_today", AsyncMock(return_value=False))
|
||
|
|
monkeypatch.setattr(sched, "_mark_ran_today", AsyncMock())
|
||
|
|
monkeypatch.setattr(
|
||
|
|
sched,
|
||
|
|
"collect_offline_snapshot",
|
||
|
|
AsyncMock(return_value={
|
||
|
|
"monitored": 10,
|
||
|
|
"online": 7,
|
||
|
|
"offline": 3,
|
||
|
|
"offline_servers": [{"id": 1, "name": "srv-1"}],
|
||
|
|
}),
|
||
|
|
)
|
||
|
|
send = AsyncMock(return_value=True)
|
||
|
|
monkeypatch.setattr(
|
||
|
|
"server.infrastructure.telegram.send_telegram_offline_daily_report",
|
||
|
|
send,
|
||
|
|
)
|
||
|
|
|
||
|
|
result = await sched.run_scheduled_offline_daily_report()
|
||
|
|
|
||
|
|
assert result["action"] == "sent"
|
||
|
|
assert result["offline"] == 3
|
||
|
|
send.assert_awaited_once()
|
||
|
|
kwargs = send.await_args.kwargs
|
||
|
|
assert kwargs["monitored"] == 10
|
||
|
|
assert kwargs["offline"] == 3
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_run_scheduled_health_check_before_report(monkeypatch):
|
||
|
|
monkeypatch.setattr(sched.settings, "OFFLINE_DAILY_REPORT_ENABLED", "true")
|
||
|
|
monkeypatch.setattr(sched, "_health_check_enabled", lambda: True)
|
||
|
|
monkeypatch.setattr(sched, "_already_ran_today", AsyncMock(return_value=False))
|
||
|
|
monkeypatch.setattr(sched, "_mark_ran_today", AsyncMock())
|
||
|
|
collect = AsyncMock(
|
||
|
|
return_value={
|
||
|
|
"monitored": 10,
|
||
|
|
"online": 8,
|
||
|
|
"offline": 2,
|
||
|
|
"offline_servers": [{"id": 2, "name": "b"}],
|
||
|
|
}
|
||
|
|
)
|
||
|
|
monkeypatch.setattr(sched, "collect_offline_snapshot", collect)
|
||
|
|
health = AsyncMock(return_value={"checked": 2, "online": 1, "offline": 1, "errors": 0})
|
||
|
|
monkeypatch.setattr(sched, "health_check_server_ids", health)
|
||
|
|
send = AsyncMock(return_value=True)
|
||
|
|
monkeypatch.setattr(
|
||
|
|
"server.infrastructure.telegram.send_telegram_offline_daily_report",
|
||
|
|
send,
|
||
|
|
)
|
||
|
|
|
||
|
|
result = await sched.run_scheduled_offline_daily_report(
|
||
|
|
preflight={"checked": 2, "online": 1, "offline": 1, "errors": 0},
|
||
|
|
)
|
||
|
|
|
||
|
|
assert result["action"] == "sent"
|
||
|
|
collect.assert_awaited_once()
|
||
|
|
health.assert_not_awaited()
|
||
|
|
kwargs = send.await_args.kwargs
|
||
|
|
assert kwargs["offline"] == 2
|
||
|
|
assert kwargs["preflight"] == {"checked": 2, "online": 1, "offline": 1, "errors": 0}
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_run_scheduled_preflight_only_when_report_disabled(monkeypatch):
|
||
|
|
monkeypatch.setattr(sched, "_health_check_enabled", lambda: True)
|
||
|
|
monkeypatch.setattr(sched, "_already_ran_preflight_today", AsyncMock(return_value=False))
|
||
|
|
monkeypatch.setattr(sched, "_mark_preflight_ran_today", AsyncMock())
|
||
|
|
monkeypatch.setattr(
|
||
|
|
sched,
|
||
|
|
"collect_offline_snapshot",
|
||
|
|
AsyncMock(return_value={
|
||
|
|
"monitored": 10,
|
||
|
|
"online": 7,
|
||
|
|
"offline": 3,
|
||
|
|
"offline_servers": [{"id": 1, "name": "a"}, {"id": 2, "name": "b"}],
|
||
|
|
}),
|
||
|
|
)
|
||
|
|
health = AsyncMock(return_value={"checked": 2, "online": 1, "offline": 1, "errors": 0})
|
||
|
|
monkeypatch.setattr(sched, "health_check_server_ids", health)
|
||
|
|
|
||
|
|
result = await sched.run_scheduled_offline_preflight()
|
||
|
|
|
||
|
|
assert result["action"] == "checked"
|
||
|
|
health.assert_awaited_once_with([1, 2])
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_run_scheduled_skips_no_monitored(monkeypatch):
|
||
|
|
monkeypatch.setattr(sched.settings, "OFFLINE_DAILY_REPORT_ENABLED", "true")
|
||
|
|
monkeypatch.setattr(sched, "_health_check_enabled", lambda: False)
|
||
|
|
monkeypatch.setattr(sched, "_already_ran_today", AsyncMock(return_value=False))
|
||
|
|
mark = AsyncMock()
|
||
|
|
monkeypatch.setattr(sched, "_mark_ran_today", mark)
|
||
|
|
monkeypatch.setattr(
|
||
|
|
sched,
|
||
|
|
"collect_offline_snapshot",
|
||
|
|
AsyncMock(return_value={"monitored": 0, "online": 0, "offline": 0, "offline_servers": []}),
|
||
|
|
)
|
||
|
|
|
||
|
|
result = await sched.run_scheduled_offline_daily_report()
|
||
|
|
|
||
|
|
assert result["reason"] == "no_monitored_servers"
|
||
|
|
mark.assert_awaited_once()
|