Files
BanGUI/backend/tests/test_startup.py
Lukas 825a67f13a Add multi-worker detection for APScheduler safety
- Add _check_single_worker_mode() to startup.py that detects and rejects
  multi-worker configurations, raising a clear RuntimeError with instructions
- Set BANGUI_WORKERS=1 as default in Dockerfile.backend
- Document single-worker requirement in compose.prod.yml
- Add 'Deployment Constraints' section to Architekture.md explaining why
  single-worker mode is required and detailing future multi-worker support
- Add '9.1 Background Tasks and Scheduler Architecture' section to
  Backend-Development.md documenting task structure and single-worker requirement
- Add comprehensive test suite (test_startup.py) covering all scenarios:
  allows single worker, rejects multi-worker, validates config format,
  and verifies informative error messages

This fix addresses TASK-002 which identified that in-process APScheduler is
unsafe in multi-worker deployments due to each worker creating independent
scheduler instances, causing duplicate background job execution.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-26 11:39:51 +02:00

58 lines
2.1 KiB
Python

"""Unit tests for application startup and resource initialization."""
import os
from unittest.mock import patch
import pytest
from app.startup import _check_single_worker_mode
def test_check_single_worker_mode_allows_workers_1() -> None:
"""Single-worker mode is allowed and no error is raised."""
with patch.dict(os.environ, {"BANGUI_WORKERS": "1"}):
# Should not raise
_check_single_worker_mode()
def test_check_single_worker_mode_allows_no_env_var() -> None:
"""When BANGUI_WORKERS is not set, no error is raised."""
with patch.dict(os.environ, {}, clear=False):
# Remove BANGUI_WORKERS if it exists
os.environ.pop("BANGUI_WORKERS", None)
# Should not raise
_check_single_worker_mode()
def test_check_single_worker_mode_rejects_workers_2() -> None:
"""Multi-worker mode (N=2) raises a clear RuntimeError."""
with patch.dict(os.environ, {"BANGUI_WORKERS": "2"}), pytest.raises(
RuntimeError, match="cannot run with multiple workers"
):
_check_single_worker_mode()
def test_check_single_worker_mode_rejects_workers_4() -> None:
"""Multi-worker mode (N=4) raises a clear RuntimeError."""
with patch.dict(os.environ, {"BANGUI_WORKERS": "4"}), pytest.raises(
RuntimeError, match="cannot run with multiple workers"
):
_check_single_worker_mode()
def test_check_single_worker_mode_rejects_invalid_workers_value() -> None:
"""Invalid BANGUI_WORKERS value raises a clear RuntimeError."""
with patch.dict(os.environ, {"BANGUI_WORKERS": "not-a-number"}), pytest.raises(
RuntimeError, match="must be an integer"
):
_check_single_worker_mode()
def test_check_single_worker_mode_error_message_is_informative() -> None:
"""Error message includes instructions and reference to documentation."""
with patch.dict(os.environ, {"BANGUI_WORKERS": "2"}), pytest.raises(RuntimeError) as exc_info:
_check_single_worker_mode()
error_message = str(exc_info.value)
assert "multiple workers" in error_message
assert "Architekture.md" in error_message