"""Scheduled unset target_path detection.""" from __future__ import annotations from datetime import datetime, timezone from unittest.mock import AsyncMock, MagicMock, patch import pytest from server.application.services import unset_path_detect_schedule as sched from server.background.schedule_runner import _cron_match from server.domain.models import Server def test_cron_matches_beijing_2am(): # 2026-06-17 02:00 Beijing = 2026-06-16 18:00 UTC at = datetime(2026, 6, 16, 18, 0, tzinfo=timezone.utc) assert _cron_match("0 2 * * *", at) is True def test_cron_not_match_other_hours(): at = datetime(2026, 6, 16, 19, 0, tzinfo=timezone.utc) # 03:00 Beijing assert _cron_match("0 2 * * *", at) is False def test_server_ssh_configured(): assert sched.server_ssh_configured( Server(name="a", domain="1.1.1.1", auth_method="password", password="x"), ) assert not sched.server_ssh_configured( Server(name="b", domain="1.1.1.1", auth_method="key"), ) @pytest.mark.asyncio async def test_run_scheduled_skips_when_no_unset_servers(monkeypatch): monkeypatch.setattr(sched.settings, "UNSET_PATH_DETECT_ENABLED", "true") monkeypatch.setattr(sched, "list_unset_server_ids", AsyncMock(return_value=[])) monkeypatch.setattr(sched, "_already_ran_today", AsyncMock(return_value=False)) monkeypatch.setattr(sched, "_detect_path_batch_running", AsyncMock(return_value=False)) mark = AsyncMock() monkeypatch.setattr(sched, "_mark_ran_today", mark) result = await sched.run_scheduled_unset_path_detect() assert result["action"] == "skipped" assert result["reason"] == "no_unset_servers" mark.assert_awaited_once() @pytest.mark.asyncio async def test_run_scheduled_starts_batch_job(monkeypatch): monkeypatch.setattr(sched.settings, "UNSET_PATH_DETECT_ENABLED", "true") monkeypatch.setattr(sched, "list_unset_server_ids", AsyncMock(return_value=[1, 2])) monkeypatch.setattr(sched, "_already_ran_today", AsyncMock(return_value=False)) monkeypatch.setattr(sched, "_detect_path_batch_running", AsyncMock(return_value=False)) monkeypatch.setattr(sched, "_mark_ran_today", AsyncMock()) start = AsyncMock(return_value={"job_id": 99, "label": "自动检测目标路径", "status": "running"}) monkeypatch.setattr( "server.application.services.server_batch_service.start_batch_job", start, ) result = await sched.run_scheduled_unset_path_detect() assert result["action"] == "started" assert result["job_id"] == 99 assert result["server_count"] == 2 start.assert_awaited_once_with( op="detect-path", server_ids=[1, 2], operator="system", params={"scheduled": True, "cron": "0 2 * * *"}, ) @pytest.mark.asyncio async def test_run_scheduled_skips_if_already_ran_today(monkeypatch): monkeypatch.setattr(sched.settings, "UNSET_PATH_DETECT_ENABLED", "true") monkeypatch.setattr(sched, "_already_ran_today", AsyncMock(return_value=True)) result = await sched.run_scheduled_unset_path_detect() assert result == {"action": "skipped", "reason": "already_ran_today"}