homelab_automation/tests/backend/test_services_scheduler.py
Bruno Charest ecefbc8611
Some checks failed
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
Clean up test files and debug artifacts, add node_modules to gitignore, export DashboardManager for testing, and enhance pytest configuration with comprehensive test markers and settings
2025-12-15 08:15:49 -05:00

367 lines
12 KiB
Python

"""
Tests pour le service Scheduler.
Couvre:
- Validation des expressions cron
- Création/mise à jour/suppression de schedules
- Pause/Resume
- Statistiques
- Exécution planifiée (mocked)
"""
import pytest
from datetime import datetime, timezone, timedelta
from unittest.mock import patch, AsyncMock, MagicMock
from freezegun import freeze_time
pytestmark = pytest.mark.unit
class TestSchedulerCronValidation:
"""Tests pour la validation des expressions cron."""
def test_validate_cron_valid_expression(self):
"""Expression cron valide retourne valid=True."""
from app.services.scheduler_service import SchedulerService
service = SchedulerService()
result = service.validate_cron_expression("0 2 * * *")
assert result["valid"] is True
assert result["expression"] == "0 2 * * *"
assert result["error"] is None
assert len(result["next_runs"]) == 5
def test_validate_cron_invalid_expression(self):
"""Expression cron invalide retourne valid=False."""
from app.services.scheduler_service import SchedulerService
service = SchedulerService()
result = service.validate_cron_expression("invalid cron")
assert result["valid"] is False
assert result["error"] is not None
def test_validate_cron_edge_cases(self):
"""Expressions cron valides complexes."""
from app.services.scheduler_service import SchedulerService
service = SchedulerService()
# Every hour
result = service.validate_cron_expression("0 * * * *")
assert result["valid"] is True
# Every Monday at 9am
result = service.validate_cron_expression("0 9 * * 1")
assert result["valid"] is True
# First of every month
result = service.validate_cron_expression("0 0 1 * *")
assert result["valid"] is True
class TestSchedulerServiceCache:
"""Tests pour la gestion du cache des schedules."""
def test_get_all_schedules_empty(self):
"""Cache vide retourne liste vide."""
from app.services.scheduler_service import SchedulerService
service = SchedulerService()
schedules = service.get_all_schedules()
assert schedules == []
def test_get_schedule_not_found(self):
"""Schedule inexistant retourne None."""
from app.services.scheduler_service import SchedulerService
service = SchedulerService()
schedule = service.get_schedule("nonexistent")
assert schedule is None
def test_add_schedule_to_cache(self):
"""Ajoute un schedule au cache."""
from app.services.scheduler_service import SchedulerService
from app.schemas.schedule_api import Schedule
service = SchedulerService()
schedule = Schedule(
id="test-schedule",
name="Test Schedule",
playbook="test.yml",
target="all",
schedule_type="once",
enabled=False,
)
service.add_schedule_to_cache(schedule)
assert service.get_schedule("test-schedule") is not None
def test_remove_schedule_from_cache(self):
"""Supprime un schedule du cache."""
from app.services.scheduler_service import SchedulerService
from app.schemas.schedule_api import Schedule
service = SchedulerService()
schedule = Schedule(
id="to-remove",
name="Remove Me",
playbook="test.yml",
target="all",
schedule_type="once",
enabled=False,
)
service.add_schedule_to_cache(schedule)
assert service.get_schedule("to-remove") is not None
service.remove_schedule_from_cache("to-remove")
assert service.get_schedule("to-remove") is None
class TestSchedulerServiceFiltering:
"""Tests pour le filtrage des schedules."""
def test_filter_by_enabled(self):
"""Filtre par état enabled."""
from app.services.scheduler_service import SchedulerService
from app.schemas.schedule_api import Schedule
service = SchedulerService()
service.add_schedule_to_cache(Schedule(
id="enabled-1", name="Enabled", playbook="test.yml",
target="all", schedule_type="once", enabled=True
))
service.add_schedule_to_cache(Schedule(
id="disabled-1", name="Disabled", playbook="test.yml",
target="all", schedule_type="once", enabled=False
))
enabled = service.get_all_schedules(enabled=True)
disabled = service.get_all_schedules(enabled=False)
assert len(enabled) == 1
assert enabled[0].id == "enabled-1"
assert len(disabled) == 1
assert disabled[0].id == "disabled-1"
def test_filter_by_playbook(self):
"""Filtre par nom de playbook."""
from app.services.scheduler_service import SchedulerService
from app.schemas.schedule_api import Schedule
service = SchedulerService()
service.add_schedule_to_cache(Schedule(
id="backup-1", name="Backup", playbook="backup.yml",
target="all", schedule_type="once", enabled=True
))
service.add_schedule_to_cache(Schedule(
id="health-1", name="Health", playbook="health-check.yml",
target="all", schedule_type="once", enabled=True
))
backup_schedules = service.get_all_schedules(playbook="backup")
assert len(backup_schedules) == 1
assert backup_schedules[0].id == "backup-1"
class TestSchedulerServicePauseResume:
"""Tests pour pause/resume des schedules."""
def test_pause_schedule(self):
"""Pause un schedule."""
from app.services.scheduler_service import SchedulerService
from app.schemas.schedule_api import Schedule
service = SchedulerService()
schedule = Schedule(
id="pausable",
name="Pausable",
playbook="test.yml",
target="all",
schedule_type="once",
enabled=True
)
service.add_schedule_to_cache(schedule)
result = service.pause_schedule("pausable")
assert result is True
assert service.get_schedule("pausable").enabled is False
def test_pause_nonexistent_schedule(self):
"""Pause d'un schedule inexistant retourne False."""
from app.services.scheduler_service import SchedulerService
service = SchedulerService()
result = service.pause_schedule("nonexistent")
assert result is False
def test_resume_schedule(self):
"""Resume un schedule en pause."""
from app.services.scheduler_service import SchedulerService
from app.schemas.schedule_api import Schedule
service = SchedulerService()
schedule = Schedule(
id="resumable",
name="Resumable",
playbook="test.yml",
target="all",
schedule_type="once",
enabled=False
)
service.add_schedule_to_cache(schedule)
result = service.resume_schedule("resumable")
assert result is True
assert service.get_schedule("resumable").enabled is True
class TestSchedulerServiceStats:
"""Tests pour les statistiques des schedules."""
def test_get_stats_empty(self):
"""Stats avec cache vide."""
from app.services.scheduler_service import SchedulerService
service = SchedulerService()
stats = service.get_stats()
assert stats.total == 0
assert stats.active == 0
assert stats.paused == 0
def test_get_stats_with_schedules(self):
"""Stats avec des schedules."""
from app.services.scheduler_service import SchedulerService
from app.schemas.schedule_api import Schedule
service = SchedulerService()
# Add mix of enabled/disabled
for i in range(3):
service.add_schedule_to_cache(Schedule(
id=f"active-{i}",
name=f"Active {i}",
playbook="test.yml",
target="all",
schedule_type="once",
enabled=True
))
for i in range(2):
service.add_schedule_to_cache(Schedule(
id=f"paused-{i}",
name=f"Paused {i}",
playbook="test.yml",
target="all",
schedule_type="once",
enabled=False
))
stats = service.get_stats()
assert stats.total == 5
assert stats.active == 3
assert stats.paused == 2
@freeze_time("2024-01-15 10:00:00")
def test_get_upcoming_executions(self):
"""Récupère les prochaines exécutions."""
from app.services.scheduler_service import SchedulerService
from app.schemas.schedule_api import Schedule
service = SchedulerService()
future_time = datetime.now(timezone.utc) + timedelta(hours=2)
service.add_schedule_to_cache(Schedule(
id="upcoming-1",
name="Upcoming",
playbook="test.yml",
target="all",
schedule_type="once",
enabled=True,
next_run_at=future_time
))
upcoming = service.get_upcoming_executions(limit=5)
assert len(upcoming) == 1
assert upcoming[0]["schedule_id"] == "upcoming-1"
class TestSchedulerServiceUpdate:
"""Tests pour la mise à jour des schedules."""
def test_update_schedule_name(self):
"""Met à jour le nom d'un schedule."""
from app.services.scheduler_service import SchedulerService
from app.schemas.schedule_api import Schedule, ScheduleUpdateRequest
service = SchedulerService()
schedule = Schedule(
id="update-me",
name="Old Name",
playbook="test.yml",
target="all",
schedule_type="once",
enabled=True
)
service.add_schedule_to_cache(schedule)
update = ScheduleUpdateRequest(name="New Name")
updated = service.update_schedule("update-me", update)
assert updated is not None
assert updated.name == "New Name"
def test_update_schedule_not_found(self):
"""Mise à jour d'un schedule inexistant retourne None."""
from app.services.scheduler_service import SchedulerService
from app.schemas.schedule_api import ScheduleUpdateRequest
service = SchedulerService()
update = ScheduleUpdateRequest(name="New Name")
result = service.update_schedule("nonexistent", update)
assert result is None
def test_delete_schedule(self):
"""Supprime un schedule."""
from app.services.scheduler_service import SchedulerService
from app.schemas.schedule_api import Schedule
service = SchedulerService()
schedule = Schedule(
id="delete-me",
name="Delete Me",
playbook="test.yml",
target="all",
schedule_type="once",
enabled=True
)
service.add_schedule_to_cache(schedule)
result = service.delete_schedule("delete-me")
assert result is True
assert service.get_schedule("delete-me") is None