Files
gh-christianlouis-docuelevate/tests/test_monitor_stalled_steps.py
2026-02-13 09:10:52 +00:00

158 lines
6.1 KiB
Python

"""
Tests for app/tasks/monitor_stalled_steps.py
This module tests the periodic task that monitors and recovers stalled processing steps.
"""
from unittest.mock import MagicMock, patch
import pytest
@pytest.mark.unit
class TestMonitorStalledSteps:
"""Test monitor_stalled_steps task."""
@patch("app.tasks.monitor_stalled_steps.mark_stalled_steps_as_failed")
@patch("app.tasks.monitor_stalled_steps.SessionLocal")
def test_monitor_stalled_steps_no_stalled(self, mock_session_local, mock_mark_stalled):
"""Test monitor_stalled_steps when no stalled steps found."""
from app.tasks.monitor_stalled_steps import monitor_stalled_steps
# Mock database session
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
# No stalled steps
mock_mark_stalled.return_value = 0
# Run task
result = monitor_stalled_steps()
# Verify result
assert result == {"recovered": 0}
mock_mark_stalled.assert_called_once_with(mock_db)
@patch("app.tasks.monitor_stalled_steps.mark_stalled_steps_as_failed")
@patch("app.tasks.monitor_stalled_steps.SessionLocal")
def test_monitor_stalled_steps_with_stalled(self, mock_session_local, mock_mark_stalled):
"""Test monitor_stalled_steps when stalled steps are found."""
from app.tasks.monitor_stalled_steps import monitor_stalled_steps
# Mock database session
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
# Found 3 stalled steps
mock_mark_stalled.return_value = 3
# Run task
result = monitor_stalled_steps()
# Verify result
assert result == {"recovered": 3}
mock_mark_stalled.assert_called_once_with(mock_db)
@patch("app.tasks.monitor_stalled_steps.mark_stalled_steps_as_failed")
@patch("app.tasks.monitor_stalled_steps.SessionLocal")
@patch("app.tasks.monitor_stalled_steps.logger")
def test_monitor_stalled_steps_logs_recovery(self, mock_logger, mock_session_local, mock_mark_stalled):
"""Test that monitor_stalled_steps logs recovery actions."""
from app.tasks.monitor_stalled_steps import monitor_stalled_steps
# Mock database session
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
# Found 2 stalled steps
mock_mark_stalled.return_value = 2
# Run task
result = monitor_stalled_steps()
# Verify logging
mock_logger.warning.assert_called_once()
log_message = mock_logger.warning.call_args[0][0]
assert "Recovered 2 stalled step(s)" in log_message
@patch("app.tasks.monitor_stalled_steps.mark_stalled_steps_as_failed")
@patch("app.tasks.monitor_stalled_steps.SessionLocal")
@patch("app.tasks.monitor_stalled_steps.logger")
def test_monitor_stalled_steps_logs_debug_when_none(self, mock_logger, mock_session_local, mock_mark_stalled):
"""Test that monitor_stalled_steps logs debug message when no stalled steps."""
from app.tasks.monitor_stalled_steps import monitor_stalled_steps
# Mock database session
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
# No stalled steps
mock_mark_stalled.return_value = 0
# Run task
result = monitor_stalled_steps()
# Verify debug logging
mock_logger.debug.assert_called_once()
log_message = mock_logger.debug.call_args[0][0]
assert "No stalled steps found" in log_message
@patch("app.tasks.monitor_stalled_steps.mark_stalled_steps_as_failed")
@patch("app.tasks.monitor_stalled_steps.SessionLocal")
@patch("app.tasks.monitor_stalled_steps.logger")
def test_monitor_stalled_steps_handles_exceptions(self, mock_logger, mock_session_local, mock_mark_stalled):
"""Test that monitor_stalled_steps handles exceptions gracefully."""
from app.tasks.monitor_stalled_steps import monitor_stalled_steps
# Mock database session
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
# Simulate an exception
mock_mark_stalled.side_effect = Exception("Database error")
# Run task
result = monitor_stalled_steps()
# Verify error handling
assert result == {"error": "Database error", "recovered": 0}
mock_logger.error.assert_called_once()
@patch("app.tasks.monitor_stalled_steps.mark_stalled_steps_as_failed")
@patch("app.tasks.monitor_stalled_steps.SessionLocal")
def test_monitor_stalled_steps_uses_context_manager(self, mock_session_local, mock_mark_stalled):
"""Test that monitor_stalled_steps uses context manager for database session."""
from app.tasks.monitor_stalled_steps import monitor_stalled_steps
# Mock database session
mock_db = MagicMock()
mock_context = MagicMock()
mock_context.__enter__ = MagicMock(return_value=mock_db)
mock_context.__exit__ = MagicMock(return_value=False)
mock_session_local.return_value = mock_context
mock_mark_stalled.return_value = 0
# Run task
result = monitor_stalled_steps()
# Verify context manager was used
mock_context.__enter__.assert_called_once()
mock_context.__exit__.assert_called_once()
def test_monitor_stalled_steps_is_celery_task(self):
"""Test that monitor_stalled_steps is registered as a Celery task."""
from app.tasks.monitor_stalled_steps import monitor_stalled_steps
# Should have task attributes
assert hasattr(monitor_stalled_steps, "apply_async")
assert hasattr(monitor_stalled_steps, "delay")
assert callable(monitor_stalled_steps)
def test_monitor_stalled_steps_task_name(self):
"""Test that monitor_stalled_steps has correct task name."""
from app.tasks.monitor_stalled_steps import monitor_stalled_steps
# Check task name
assert monitor_stalled_steps.name == "app.tasks.monitor_stalled_steps.monitor_stalled_steps"