FactsDB/tests/test_scheduler.py
Jarian Cottingham 93e287f88b test: add comprehensive test suite with 95% coverage
- Add pytest test suite across all modules (config, database, file_processor,
  ai_processor, api, scheduler, monitoring, ftp_server)
- 145 tests covering normal paths, error handling, edge cases
- Mock external dependencies (AI endpoint, requests, pyftpdlib)
- 95% code coverage with fail-under=90 threshold in CI
- Update CI workflow to run pytest with coverage enforcement
- Add pyproject.toml with pytest/coverage configuration
2026-07-06 16:27:51 +00:00

99 lines
4.0 KiB
Python

"""Tests for scheduler module."""
import os
import pytest
from unittest.mock import patch, MagicMock
from factsdb.config import Config, DatabaseConfig, AIEndpointConfig, FTPServerConfig, SchedulerConfig
from factsdb.scheduler import FactExtractionJob, FactExtractionScheduler, get_scheduler
class MockConfig:
"""Lightweight mock Config that avoids env var requirements."""
def __init__(self, db_path=":memory:"):
self.database = DatabaseConfig(path=db_path)
self.ai_endpoint = AIEndpointConfig(url="http://localhost", auth_token="tok")
self.ftp_server = FTPServerConfig(host="0.0.0.0", port=2121, username="u", password="p")
self.scheduler = SchedulerConfig(interval_minutes=10)
@pytest.fixture
def scheduler_config(tmp_path):
return MockConfig(db_path=str(tmp_path / "sched.db"))
class TestFactExtractionJob:
def test_execute_missing_directory(self, scheduler_config):
from factsdb.database import DatabaseManager
from factsdb.file_processor import FileProcessor
from factsdb.ai_processor import AIProcessor
dm = DatabaseManager(scheduler_config.database)
fp = FileProcessor()
ap = AIProcessor(scheduler_config.ai_endpoint)
job = FactExtractionJob(scheduler_config, dm, fp, ap)
with pytest.raises(Exception, match="Directory does not exist"):
job.execute("/nonexistent/dir", "t", "", "gpt-oss")
class TestFactExtractionScheduler:
def test_init(self, scheduler_config):
with patch("factsdb.scheduler.AIProcessor"):
with patch("factsdb.scheduler.DatabaseManager"):
sched = FactExtractionScheduler(scheduler_config)
assert sched.jobs == {}
assert sched.is_running is False
def test_add_job(self, scheduler_config):
with patch("factsdb.scheduler.AIProcessor"):
with patch("factsdb.scheduler.DatabaseManager"):
sched = FactExtractionScheduler(scheduler_config)
sched.add_job("/tmp", "t1", "", "gpt-oss", 5)
jobs = sched.get_jobs()
assert len(jobs) == 1
assert jobs[0]["directory_path"] == "/tmp"
assert jobs[0]["interval_minutes"] == 5
def test_remove_job(self, scheduler_config):
with patch("factsdb.scheduler.AIProcessor"):
with patch("factsdb.scheduler.DatabaseManager"):
sched = FactExtractionScheduler(scheduler_config)
sched.add_job("/tmp", "t1")
sched.remove_job("job_1")
assert len(sched.get_jobs()) == 0
def test_is_job_running(self, scheduler_config):
with patch("factsdb.scheduler.AIProcessor"):
with patch("factsdb.scheduler.DatabaseManager"):
sched = FactExtractionScheduler(scheduler_config)
sched.add_job("/tmp", "t1")
assert sched.is_job_running("job_1") is True
assert sched.is_job_running("job_99") is False
def test_start_stop(self, scheduler_config):
with patch("factsdb.scheduler.AIProcessor"):
with patch("factsdb.scheduler.DatabaseManager"):
sched = FactExtractionScheduler(scheduler_config)
sched.start()
assert sched.is_running is True
sched.stop()
assert sched.is_running is False
def test_get_jobs_empty(self, scheduler_config):
with patch("factsdb.scheduler.AIProcessor"):
with patch("factsdb.scheduler.DatabaseManager"):
sched = FactExtractionScheduler(scheduler_config)
assert sched.get_jobs() == []
class TestGetScheduler:
def test_singleton(self, scheduler_config):
import factsdb.scheduler as sched_mod
sched_mod._scheduler = None
with patch("factsdb.scheduler.AIProcessor"):
with patch("factsdb.scheduler.DatabaseManager"):
s1 = get_scheduler(scheduler_config)
s2 = get_scheduler(scheduler_config)
assert s1 is s2
sched_mod._scheduler = None