Refactor: PHASE 8: Testing & Integration

This commit is contained in:
ziesorx 2025-09-12 18:55:23 +07:00
parent af34f4fd08
commit 9e8c6804a7
32 changed files with 17128 additions and 0 deletions

276
tests/conftest.py Normal file
View file

@ -0,0 +1,276 @@
"""
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