"""Pytest configuration and fixtures.""" import contextlib import os import sqlite3 from unittest.mock import MagicMock, patch import pytest from app import app as flask_app from routes import register_blueprints @pytest.fixture(scope="session") def app(): """Create application for testing.""" os.environ["INTERCEPT_DISABLE_AUTH"] = "1" flask_app.config["TESTING"] = True # Disable CSRF for tests flask_app.config["WTF_CSRF_ENABLED"] = False # Register blueprints only if not already registered if "pager" not in flask_app.blueprints: register_blueprints(flask_app) return flask_app @pytest.fixture def client(app): """Create test client.""" return app.test_client() @pytest.fixture def mock_subprocess(): """Patch subprocess.Popen and subprocess.run with configurable returns. Usage: def test_example(mock_subprocess): mock_subprocess['run'].return_value.stdout = 'output' mock_subprocess['run'].return_value.returncode = 0 """ with patch("subprocess.Popen") as mock_popen, patch("subprocess.run") as mock_run: mock_process = MagicMock() mock_process.poll.return_value = None mock_process.stdout = MagicMock() mock_process.stderr = MagicMock() mock_process.pid = 12345 mock_popen.return_value = mock_process mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="") yield { "popen": mock_popen, "process": mock_process, "run": mock_run, } @pytest.fixture def mock_sdr_device(): """Return a mock SDRDevice with configurable type and index. Usage: def test_example(mock_sdr_device): device = mock_sdr_device(device_type='rtlsdr', index=0) """ def _factory(device_type="rtlsdr", index=0): device = MagicMock() device.device_type = device_type device.device_index = index device.name = f"Mock {device_type} #{index}" device.is_available.return_value = True device.build_command.return_value = ["rtl_fm", "-f", "100M"] return device return _factory @pytest.fixture def mock_app_state(): """Patch common app module attributes for route tests. Provides mock process, queue, and lock objects on the app module. """ import queue import app as app_module mock_process = MagicMock() mock_process.poll.return_value = None mock_queue = queue.Queue() mock_lock = MagicMock() patches = { "current_process": mock_process, "pager_queue": mock_queue, "pager_lock": mock_lock, } originals = {} for attr, value in patches.items(): originals[attr] = getattr(app_module, attr, None) setattr(app_module, attr, value) yield { "process": mock_process, "queue": mock_queue, "lock": mock_lock, "module": app_module, } for attr, orig in originals.items(): if orig is None: with contextlib.suppress(AttributeError): delattr(app_module, attr) else: setattr(app_module, attr, orig) @pytest.fixture def mock_check_tool(): """Patch check_tool() to return True for all tools.""" with patch("utils.dependencies.check_tool", return_value=True) as mock: yield mock @pytest.fixture def test_db(tmp_path): """Provide an isolated in-memory SQLite database for tests.""" db_path = tmp_path / "test.db" conn = sqlite3.connect(str(db_path)) conn.row_factory = sqlite3.Row conn.execute("PRAGMA journal_mode = WAL") yield conn conn.close()