""" Pytest configuration and shared fixtures for detector worker tests. """ import pytest import tempfile import os from unittest.mock import Mock, MagicMock, patch from typing import Dict, Any, Generator # Add the project root to the path so we can import detector_worker import sys from pathlib import Path project_root = Path(__file__).parent.parent sys.path.insert(0, str(project_root)) from detector_worker.core.config import get_config_manager, ConfigurationManager from detector_worker.core.dependency_injection import get_container, DetectorWorkerContainer from detector_worker.core.singleton_managers import ( ModelStateManager, StreamStateManager, SessionStateManager, CacheStateManager, CameraStateManager, PipelineStateManager ) @pytest.fixture def temp_dir(): """Create a temporary directory for tests.""" with tempfile.TemporaryDirectory() as tmpdir: yield tmpdir @pytest.fixture def mock_config(): """Mock configuration data for tests.""" return { "poll_interval_ms": 100, "max_streams": 5, "target_fps": 10, "reconnect_interval_sec": 5, "max_retries": 3, "heartbeat_interval": 2, "session_timeout": 600, "models_dir": "models", "log_level": "INFO", "database": { "enabled": False, "host": "localhost", "port": 5432, "database": "test_db", "username": "test_user", "password": "test_pass", "schema": "public" }, "redis": { "enabled": False, "host": "localhost", "port": 6379, "password": None, "db": 0 } } @pytest.fixture def mock_detection_result(): """Mock detection result for tests.""" return { "class": "car", "confidence": 0.85, "bbox": [100, 200, 300, 400], "id": 12345, "branch_results": {} } @pytest.fixture def mock_frame(): """Mock frame data for tests.""" import numpy as np return np.zeros((480, 640, 3), dtype=np.uint8) @pytest.fixture def mock_model_tree(): """Mock model tree structure for tests.""" return { "modelId": "test_model_v1", "modelFile": "test_model.pt", "multiClass": True, "expectedClasses": ["car", "truck"], "triggerClasses": ["car"], "minConfidence": 0.8, "branches": [], "actions": [] } @pytest.fixture def mock_pipeline_context(): """Mock pipeline context for tests.""" return { "camera_id": "test_camera_001", "display_id": "display_001", "session_id": "session_12345", "timestamp": 1640995200000, "subscription_id": "sub_001" } @pytest.fixture(autouse=True) def reset_singletons(): """Reset singleton managers before each test.""" # Clear all singleton state before each test yield # Cleanup after test try: ModelStateManager().clear_all() StreamStateManager().clear_all() SessionStateManager().clear_all() CacheStateManager().clear_all() CameraStateManager().clear_all() PipelineStateManager().clear_all() except Exception: pass # Ignore cleanup errors @pytest.fixture def isolated_config_manager(temp_dir, mock_config): """Create an isolated configuration manager for testing.""" config_file = os.path.join(temp_dir, "test_config.json") import json with open(config_file, 'w') as f: json.dump(mock_config, f) # Create a fresh ConfigurationManager for testing from detector_worker.core.config import JsonFileProvider, EnvironmentProvider manager = ConfigurationManager() manager._providers.clear() # Remove default providers manager.add_provider(JsonFileProvider(config_file)) return manager @pytest.fixture def mock_websocket(): """Mock WebSocket for testing.""" mock_ws = Mock() mock_ws.accept = Mock() mock_ws.send_text = Mock() mock_ws.send_json = Mock() mock_ws.receive_text = Mock() mock_ws.receive_json = Mock() mock_ws.close = Mock() mock_ws.client_state = Mock() mock_ws.client_state.DISCONNECTED = False return mock_ws @pytest.fixture def mock_redis_client(): """Mock Redis client for testing.""" mock_redis = Mock() mock_redis.get = Mock(return_value=None) mock_redis.set = Mock(return_value=True) mock_redis.delete = Mock(return_value=1) mock_redis.exists = Mock(return_value=0) mock_redis.expire = Mock(return_value=True) mock_redis.publish = Mock(return_value=1) return mock_redis @pytest.fixture def mock_database_connection(): """Mock database connection for testing.""" mock_conn = Mock() mock_cursor = Mock() mock_cursor.execute = Mock() mock_cursor.fetchone = Mock(return_value=None) mock_cursor.fetchall = Mock(return_value=[]) mock_cursor.fetchmany = Mock(return_value=[]) mock_cursor.rowcount = 1 mock_conn.cursor = Mock(return_value=mock_cursor) mock_conn.commit = Mock() mock_conn.rollback = Mock() mock_conn.close = Mock() return mock_conn @pytest.fixture def mock_yolo_model(): """Mock YOLO model for testing.""" mock_model = Mock() # Mock results with boxes mock_result = Mock() mock_result.boxes = Mock() mock_result.boxes.data = Mock() mock_result.boxes.conf = Mock() mock_result.boxes.cls = Mock() mock_result.boxes.id = Mock() # Mock track method mock_model.track = Mock(return_value=[mock_result]) mock_model.predict = Mock(return_value=[mock_result]) return mock_model @pytest.fixture def sample_detection_data(): """Sample detection data for testing.""" return [ { "class": "car", "confidence": 0.92, "bbox": [100, 150, 250, 300], "id": 1001, "branch_results": {} }, { "class": "truck", "confidence": 0.87, "bbox": [300, 200, 450, 350], "id": 1002, "branch_results": {} } ] @pytest.fixture def sample_session_data(): """Sample session data for testing.""" return { "session_id": "session_test_001", "display_id": "display_test_001", "camera_id": "camera_test_001", "created_at": 1640995200.0, "last_activity": 1640995200.0, "detection_data": { "car_brand": "Toyota", "car_model": "Camry", "license_plate": "ABC-123" } } # Helper functions for tests def create_mock_detection_result(class_name: str = "car", confidence: float = 0.85, track_id: int = 1001): """Helper function to create mock detection results.""" return { "class": class_name, "confidence": confidence, "bbox": [100, 200, 300, 400], "id": track_id, "branch_results": {} } def create_mock_regions_dict(detections: list = None): """Helper function to create mock regions dictionary.""" if detections is None: detections = [create_mock_detection_result()] regions = {} for det in detections: regions[det["class"]] = { "bbox": det["bbox"], "confidence": det["confidence"], "detection": det } return regions