276 lines
No EOL
7.3 KiB
Python
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 |