Files
ss-tools/backend/tests/plugins/test_llm_analysis_scheduler.py
busya f75c15dbc6 test: massive coverage expansion — 15 new test modules + assistant tool fixes + orthogonal testing
- 10 translate plugin test files (100% coverage on 12 modules)
- assistant/handler tools: 85+ tests covering dispatch, registry, resolvers, routes, llm_planner, 13 tool handlers
- clean release: artifact_catalog_loader, mappers, approval, publication tests
- API routes: translate_helpers, validation_service extensions, datasets to 100%
- notifications: providers/service tests
- services: profile_preference_service
- docs/orthogonal-test-report.md — full speckit.tests audit
- Fixes: 3 git_base async mock failures, 4 assistant handler permission-check patches
- .gitignore: coverage artifacts
2026-06-15 15:38:59 +03:00

137 lines
5.0 KiB
Python

# #region Test.LLMAnalysisScheduler [C:3] [TYPE Module] [SEMANTICS test, llm, analysis, scheduler, cron]
# @BRIEF Tests for llm_analysis/scheduler.py — _parse_cron, schedule_dashboard_validation.
# @RELATION BINDS_TO -> [LLMAnalysisScheduler]
# @TEST_CONTRACT: _parse_cron -> dict | cron expression parsing
# @TEST_CONTRACT: schedule_dashboard_validation -> None | schedules a validation job
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))
import os
os.environ.setdefault("SECRET_KEY", "test-secret-key-for-testing")
os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:")
os.environ.setdefault("AUTH_DATABASE_URL", "sqlite:///:memory:")
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from src.plugins.llm_analysis.scheduler import _parse_cron, schedule_dashboard_validation
class TestParseCron:
"""_parse_cron — basic cron expression parser."""
def test_standard_5_part_cron(self):
"""Standard 5-part cron expression parsed correctly."""
result = _parse_cron("0 8 * * 1-5")
assert result == {
"minute": "0",
"hour": "8",
"day": "*",
"month": "*",
"day_of_week": "1-5",
}
def test_daily_at_midnight(self):
"""Daily at midnight."""
result = _parse_cron("0 0 * * *")
assert result["hour"] == "0"
assert result["minute"] == "0"
def test_every_hour(self):
"""Every hour at minute 0."""
result = _parse_cron("0 * * * *")
assert result["minute"] == "0"
assert result["hour"] == "*"
def test_invalid_parts_count(self):
"""Less or more than 5 parts returns empty dict."""
assert _parse_cron("0 8 * *") == {}
assert _parse_cron("0 8 * * 1-5 extra") == {}
def test_empty_string(self):
"""Empty string returns empty dict."""
assert _parse_cron("") == {}
def test_step_values(self):
"""Step values like */15 parsed correctly."""
result = _parse_cron("*/15 * * * *")
assert result["minute"] == "*/15"
class TestScheduleDashboardValidation:
"""schedule_dashboard_validation — schedules recurring validation."""
def test_schedule_with_params(self):
"""Scheduler and task manager called with correct args."""
mock_scheduler = MagicMock()
mock_task_manager = MagicMock()
with patch("src.plugins.llm_analysis.scheduler.get_scheduler_service", return_value=mock_scheduler), \
patch("src.plugins.llm_analysis.scheduler.get_task_manager", return_value=mock_task_manager):
schedule_dashboard_validation(
dashboard_id="dash-1",
cron_expression="0 6 * * *",
params={"threshold": 0.8},
)
# Scheduler should have added a cron job
mock_scheduler.add_job.assert_called_once()
call_args = mock_scheduler.add_job.call_args
assert call_args[0][1] == "cron" # trigger type
assert call_args[1]["id"] == "llm_val_dash-1"
assert call_args[1]["replace_existing"] is True
assert call_args[1]["minute"] == "0"
assert call_args[1]["hour"] == "6"
def test_schedule_creates_task_on_execution(self):
"""The scheduled job function creates a task when executed."""
mock_scheduler = MagicMock()
mock_task_manager = AsyncMock()
with patch("src.plugins.llm_analysis.scheduler.get_scheduler_service", return_value=mock_scheduler), \
patch("src.plugins.llm_analysis.scheduler.get_task_manager", return_value=mock_task_manager):
schedule_dashboard_validation(
dashboard_id="dash-1",
cron_expression="0 6 * * *",
params={"threshold": 0.8},
)
# Get the job function and call it
job_func = mock_scheduler.add_job.call_args[0][0]
import asyncio
asyncio.run(job_func())
mock_task_manager.create_task.assert_called_once_with(
plugin_id="llm_dashboard_validation",
params={"dashboard_id": "dash-1", "threshold": 0.8},
)
def test_schedule_without_params(self):
"""Schedule without extra params still works."""
mock_scheduler = MagicMock()
mock_task_manager = AsyncMock()
with patch("src.plugins.llm_analysis.scheduler.get_scheduler_service", return_value=mock_scheduler), \
patch("src.plugins.llm_analysis.scheduler.get_task_manager", return_value=mock_task_manager):
schedule_dashboard_validation(
dashboard_id="dash-2",
cron_expression="0 0 * * *",
params={},
)
job_func = mock_scheduler.add_job.call_args[0][0]
import asyncio
asyncio.run(job_func())
mock_task_manager.create_task.assert_called_once_with(
plugin_id="llm_dashboard_validation",
params={"dashboard_id": "dash-2"},
)
# #endregion Test.LLMAnalysisScheduler