python-detector-worker/tests/conftest.py
2025-09-12 18:55:23 +07:00

276 lines
No EOL
7.3 KiB
Python

"""
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