fix(scheduler): ensure scheduler starts after setup/config update
Add ensure_started() to SchedulerService as idempotent entry point. Start scheduler in auth setup run_initialization() after NFO scan. Sync anime_directory and start scheduler in config update endpoint. Add unit and endpoint tests for ensure_started() behavior.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
@@ -207,3 +207,46 @@ async def test_tmdb_validation_endpoint_exists(authenticated_client):
|
||||
assert "message" in data
|
||||
assert data["valid"] is False # Empty key should be invalid
|
||||
assert "required" in data["message"].lower()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_config_with_anime_directory_starts_scheduler(
|
||||
authenticated_client, mock_config_service
|
||||
):
|
||||
"""PUT /api/config with anime_directory syncs and starts scheduler."""
|
||||
mock_scheduler = AsyncMock()
|
||||
|
||||
with patch("src.server.services.scheduler_service.get_scheduler_service") as mock_sched_fn:
|
||||
mock_sched_fn.return_value = mock_scheduler
|
||||
|
||||
with patch("src.config.settings.settings") as mock_settings:
|
||||
mock_settings.anime_directory = None
|
||||
|
||||
resp = await authenticated_client.put(
|
||||
"/api/config",
|
||||
json={"other": {"anime_directory": "/data/anime"}},
|
||||
)
|
||||
|
||||
assert resp.status_code == 200
|
||||
mock_scheduler.ensure_started.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_config_without_anime_directory_does_not_start_scheduler(
|
||||
authenticated_client, mock_config_service
|
||||
):
|
||||
"""PUT /api/config without new anime_directory does not call scheduler.ensure_started()."""
|
||||
mock_scheduler = AsyncMock()
|
||||
|
||||
with patch("src.server.services.scheduler_service.get_scheduler_service") as mock_sched_fn:
|
||||
mock_sched_fn.return_value = mock_scheduler
|
||||
|
||||
with patch("src.config.settings.settings") as mock_settings:
|
||||
mock_settings.anime_directory = "/already/set"
|
||||
|
||||
resp = await authenticated_client.put(
|
||||
"/api/config", json={"other": {}}
|
||||
)
|
||||
|
||||
assert resp.status_code == 200
|
||||
mock_scheduler.ensure_started.assert_not_called()
|
||||
|
||||
@@ -559,3 +559,38 @@ class TestStartupRecovery:
|
||||
info_calls = [str(c) for c in mock_logger.info.call_args_list]
|
||||
assert any("next_run" in c for c in info_calls)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 12.8 ensure_started() – idempotent startup
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestEnsureStarted:
|
||||
@pytest.mark.asyncio
|
||||
async def test_ensure_started_when_not_running(
|
||||
self, scheduler_service, mock_config_service
|
||||
):
|
||||
"""ensure_started() calls start() when scheduler is not running."""
|
||||
# Mock start method
|
||||
scheduler_service.start = AsyncMock()
|
||||
|
||||
# Call ensure_started
|
||||
await scheduler_service.ensure_started()
|
||||
|
||||
# Verify start() was called exactly once
|
||||
scheduler_service.start.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ensure_started_when_already_running(self, scheduler_service):
|
||||
"""ensure_started() returns immediately when already running (idempotent)."""
|
||||
# Set up as already running
|
||||
scheduler_service._is_running = True
|
||||
|
||||
# Mock start method
|
||||
scheduler_service.start = AsyncMock()
|
||||
|
||||
# Call ensure_started
|
||||
await scheduler_service.ensure_started()
|
||||
|
||||
# Verify start() was NOT called
|
||||
scheduler_service.start.assert_not_called()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user