Files
ss-tools/backend/tests/test_core_scheduler.py
busya c6189876b3 chore(lint): apply ruff --fix (4443 auto-fixes)
Auto-fixed categories:
- F401: unused imports removed
- I001: import blocks sorted
- W293: trailing whitespace stripped
- UP035: deprecated typing imports replaced
- SIM: simplify suggestions applied
- ARG: unused args prefixed with underscore
- T201: print statements removed
- F841: unused variables removed
- RUF059: unpacked variables prefixed

Remaining ~1500 unfixable errors (C901, B904, N806, E402) require manual work.

Backend smoke tests: 13/13 passed.
2026-05-14 11:20:17 +03:00

176 lines
6.2 KiB
Python

# #region TestCoreScheduler [C:3] [TYPE Module] [SEMANTICS test, scheduler, apscheduler, load, reload]
# @BRIEF Verify SchedulerService load_schedules and translation job add/remove contracts.
# @RELATION BINDS_TO -> [SchedulerService]
# @TEST_EDGE: load_schedules_reloads — jobs survive remove_all_jobs when re-registered from DB
# @TEST_EDGE: add_remove_translation_job — job correctly registered and removed from APScheduler
# @TEST_INVARIANT: translation_jobs_persist_across_reload -> VERIFIED_BY: [test_load_schedules_reloads_translation_schedules]
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
from unittest.mock import MagicMock, patch
from apscheduler.schedulers.background import BackgroundScheduler
# ---------------------------------------------------------------------------
# Helpers — fresh scheduler & mock config
# ---------------------------------------------------------------------------
def _make_mock_config_manager(backup_env_id="env1", backup_cron="0 3 * * *"):
"""Build a mock ConfigManager returning a single backup-enabled environment."""
mgr = MagicMock()
mock_env = MagicMock()
mock_env.id = backup_env_id
mock_env.backup_schedule = MagicMock()
mock_env.backup_schedule.enabled = True
mock_env.backup_schedule.cron_expression = backup_cron
mock_config = MagicMock()
mock_config.environments = [mock_env]
mgr.get_config.return_value = mock_config
return mgr
def _make_mock_task_manager():
"""Build a mock TaskManager returning no active tasks."""
tm = MagicMock()
tm.get_tasks.return_value = []
return tm
# ---------------------------------------------------------------------------
# Fixture helpers — start/stop a real BackgroundScheduler
# ---------------------------------------------------------------------------
def _start_scheduler():
"""Create and start a BackgroundScheduler. Returns the scheduler instance."""
sched = BackgroundScheduler()
sched.start()
return sched
def _stop_scheduler(sched):
"""Shutdown a BackgroundScheduler if running."""
if sched.running:
sched.shutdown(wait=False)
# #region test_load_schedules_reloads_translation_schedules [C:1] [TYPE Function]
def test_load_schedules_reloads_translation_schedules():
"""Verify load_schedules re-registers translation jobs after remove_all_jobs."""
from src.core.scheduler import SchedulerService
sched = _start_scheduler()
try:
cfg = _make_mock_config_manager()
tm = _make_mock_task_manager()
svc = SchedulerService.__new__(SchedulerService)
svc.task_manager = tm
svc.config_manager = cfg
svc.scheduler = sched
# Pre-register a translation job (simulates what happens before reload)
svc.add_translation_job(
schedule_id="sched-1",
job_id="job-1",
cron_expression="0 2 * * *",
)
# Verify it exists BEFORE reload
job_before = sched.get_job("translate_sched-1")
assert job_before is not None, "Translation job should exist before reload"
# Mock the DB query that load_schedules uses internally
mock_schedule = MagicMock()
mock_schedule.id = "sched-1"
mock_schedule.job_id = "job-1"
mock_schedule.cron_expression = "0 2 * * *"
mock_schedule.timezone = "UTC"
mock_schedule.execution_mode = "full"
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.all.return_value = [mock_schedule]
with patch("src.core.scheduler.SessionLocal", return_value=mock_db):
svc.load_schedules()
# After reload, the translation job should STILL exist
job_after = sched.get_job("translate_sched-1")
assert job_after is not None, (
"Translation job should survive load_schedules (re-registered from DB)"
)
# Backup job should also exist (added from config)
backup_job = sched.get_job("backup_env1")
assert backup_job is not None, "Backup job should be registered by load_schedules"
finally:
_stop_scheduler(sched)
# #endregion test_load_schedules_reloads_translation_schedules
# #region test_add_and_remove_translation_job [C:1] [TYPE Function]
def test_add_and_remove_translation_job():
"""Verify add_translation_job registers and remove_translation_job removes."""
from src.core.scheduler import SchedulerService
sched = _start_scheduler()
try:
cfg = _make_mock_config_manager()
tm = _make_mock_task_manager()
svc = SchedulerService.__new__(SchedulerService)
svc.task_manager = tm
svc.config_manager = cfg
svc.scheduler = sched
# Should NOT exist before add
assert sched.get_job("translate_sched-x") is None
svc.add_translation_job(
schedule_id="sched-x",
job_id="job-x",
cron_expression="*/5 * * * *",
)
# Should exist after add
job = sched.get_job("translate_sched-x")
assert job is not None, "Translation job should be registered after add"
assert job.id == "translate_sched-x"
svc.remove_translation_job("sched-x")
# Should NOT exist after remove
assert sched.get_job("translate_sched-x") is None, (
"Translation job should be removed after remove_translation_job"
)
finally:
_stop_scheduler(sched)
# #endregion test_add_and_remove_translation_job
# #region test_remove_nonexistent_translation_job_no_error [C:1] [TYPE Function]
def test_remove_nonexistent_translation_job_no_error():
"""Verify remove_translation_job handles non-existent job silently."""
from src.core.scheduler import SchedulerService
sched = _start_scheduler()
try:
cfg = _make_mock_config_manager()
tm = _make_mock_task_manager()
svc = SchedulerService.__new__(SchedulerService)
svc.task_manager = tm
svc.config_manager = cfg
svc.scheduler = sched
# Should not raise — silent ignore
svc.remove_translation_job("nonexistent-sched")
finally:
_stop_scheduler(sched)
# #endregion test_remove_nonexistent_translation_job_no_error
# #endregion TestCoreScheduler