"""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()