Major changes: - Organized Python v1 implementation into v1/ subdirectory - Created Rust workspace with 9 modular crates: - wifi-densepose-core: Core types, traits, errors - wifi-densepose-signal: CSI processing, phase sanitization, FFT - wifi-densepose-nn: Neural network inference (ONNX/Candle/tch) - wifi-densepose-api: Axum-based REST/WebSocket API - wifi-densepose-db: SQLx database layer - wifi-densepose-config: Configuration management - wifi-densepose-hardware: Hardware abstraction - wifi-densepose-wasm: WebAssembly bindings - wifi-densepose-cli: Command-line interface Documentation: - ADR-001: Workspace structure - ADR-002: Signal processing library selection - ADR-003: Neural network inference strategy - DDD domain model with bounded contexts Testing: - 69 tests passing across all crates - Signal processing: 45 tests - Neural networks: 21 tests - Core: 3 doc tests Performance targets: - 10x faster CSI processing (~0.5ms vs ~5ms) - 5x lower memory usage (~100MB vs ~500MB) - WASM support for browser deployment
447 lines
16 KiB
Python
447 lines
16 KiB
Python
"""
|
|
Full system integration tests for WiFi-DensePose API
|
|
Tests the complete integration of all components working together.
|
|
"""
|
|
|
|
import asyncio
|
|
import pytest
|
|
import httpx
|
|
import json
|
|
import time
|
|
from pathlib import Path
|
|
from typing import Dict, Any
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
from src.config.settings import get_settings
|
|
from src.app import app
|
|
from src.database.connection import get_database_manager
|
|
from src.services.orchestrator import get_service_orchestrator
|
|
from src.tasks.cleanup import get_cleanup_manager
|
|
from src.tasks.monitoring import get_monitoring_manager
|
|
from src.tasks.backup import get_backup_manager
|
|
|
|
|
|
class TestFullSystemIntegration:
|
|
"""Test complete system integration."""
|
|
|
|
@pytest.fixture
|
|
async def settings(self):
|
|
"""Get test settings."""
|
|
settings = get_settings()
|
|
settings.environment = "test"
|
|
settings.debug = True
|
|
settings.database_url = "sqlite+aiosqlite:///test_integration.db"
|
|
settings.redis_enabled = False
|
|
return settings
|
|
|
|
@pytest.fixture
|
|
async def db_manager(self, settings):
|
|
"""Get database manager for testing."""
|
|
manager = get_database_manager(settings)
|
|
await manager.initialize()
|
|
yield manager
|
|
await manager.close_all_connections()
|
|
|
|
@pytest.fixture
|
|
async def client(self, settings):
|
|
"""Get test HTTP client."""
|
|
async with httpx.AsyncClient(app=app, base_url="http://test") as client:
|
|
yield client
|
|
|
|
@pytest.fixture
|
|
async def orchestrator(self, settings, db_manager):
|
|
"""Get service orchestrator for testing."""
|
|
orchestrator = get_service_orchestrator(settings)
|
|
await orchestrator.initialize()
|
|
yield orchestrator
|
|
await orchestrator.shutdown()
|
|
|
|
async def test_application_startup_and_shutdown(self, settings, db_manager):
|
|
"""Test complete application startup and shutdown sequence."""
|
|
|
|
# Test database initialization
|
|
await db_manager.test_connection()
|
|
stats = await db_manager.get_connection_stats()
|
|
assert stats["database"]["connected"] is True
|
|
|
|
# Test service orchestrator initialization
|
|
orchestrator = get_service_orchestrator(settings)
|
|
await orchestrator.initialize()
|
|
|
|
# Verify services are running
|
|
health_status = await orchestrator.get_health_status()
|
|
assert health_status["status"] in ["healthy", "warning"]
|
|
|
|
# Test graceful shutdown
|
|
await orchestrator.shutdown()
|
|
|
|
# Verify cleanup
|
|
final_stats = await db_manager.get_connection_stats()
|
|
assert final_stats is not None
|
|
|
|
async def test_api_endpoints_integration(self, client, settings, db_manager):
|
|
"""Test API endpoints work with database integration."""
|
|
|
|
# Test health endpoint
|
|
response = await client.get("/health")
|
|
assert response.status_code == 200
|
|
health_data = response.json()
|
|
assert "status" in health_data
|
|
assert "timestamp" in health_data
|
|
|
|
# Test metrics endpoint
|
|
response = await client.get("/metrics")
|
|
assert response.status_code == 200
|
|
|
|
# Test devices endpoint
|
|
response = await client.get("/api/v1/devices")
|
|
assert response.status_code == 200
|
|
devices_data = response.json()
|
|
assert "devices" in devices_data
|
|
assert isinstance(devices_data["devices"], list)
|
|
|
|
# Test sessions endpoint
|
|
response = await client.get("/api/v1/sessions")
|
|
assert response.status_code == 200
|
|
sessions_data = response.json()
|
|
assert "sessions" in sessions_data
|
|
assert isinstance(sessions_data["sessions"], list)
|
|
|
|
@patch('src.core.router_interface.RouterInterface')
|
|
@patch('src.core.csi_processor.CSIProcessor')
|
|
@patch('src.core.pose_estimator.PoseEstimator')
|
|
async def test_data_processing_pipeline(
|
|
self,
|
|
mock_pose_estimator,
|
|
mock_csi_processor,
|
|
mock_router_interface,
|
|
client,
|
|
settings,
|
|
db_manager
|
|
):
|
|
"""Test complete data processing pipeline integration."""
|
|
|
|
# Setup mocks
|
|
mock_router = MagicMock()
|
|
mock_router_interface.return_value = mock_router
|
|
mock_router.connect.return_value = True
|
|
mock_router.start_capture.return_value = True
|
|
mock_router.get_csi_data.return_value = {
|
|
"timestamp": time.time(),
|
|
"csi_matrix": [[1.0, 2.0], [3.0, 4.0]],
|
|
"rssi": -45,
|
|
"noise_floor": -90
|
|
}
|
|
|
|
mock_processor = MagicMock()
|
|
mock_csi_processor.return_value = mock_processor
|
|
mock_processor.process_csi_data.return_value = {
|
|
"processed_csi": [[1.1, 2.1], [3.1, 4.1]],
|
|
"quality_score": 0.85,
|
|
"phase_sanitized": True
|
|
}
|
|
|
|
mock_estimator = MagicMock()
|
|
mock_pose_estimator.return_value = mock_estimator
|
|
mock_estimator.estimate_pose.return_value = {
|
|
"pose_data": {
|
|
"keypoints": [[100, 200], [150, 250]],
|
|
"confidence": 0.9
|
|
},
|
|
"processing_time": 0.05
|
|
}
|
|
|
|
# Test device registration
|
|
device_data = {
|
|
"name": "test_router",
|
|
"ip_address": "192.168.1.1",
|
|
"device_type": "router",
|
|
"model": "test_model"
|
|
}
|
|
|
|
response = await client.post("/api/v1/devices", json=device_data)
|
|
assert response.status_code == 201
|
|
device_response = response.json()
|
|
device_id = device_response["device"]["id"]
|
|
|
|
# Test session creation
|
|
session_data = {
|
|
"device_id": device_id,
|
|
"session_type": "pose_detection",
|
|
"configuration": {
|
|
"sampling_rate": 1000,
|
|
"duration": 60
|
|
}
|
|
}
|
|
|
|
response = await client.post("/api/v1/sessions", json=session_data)
|
|
assert response.status_code == 201
|
|
session_response = response.json()
|
|
session_id = session_response["session"]["id"]
|
|
|
|
# Test CSI data submission
|
|
csi_data = {
|
|
"session_id": session_id,
|
|
"timestamp": time.time(),
|
|
"csi_matrix": [[1.0, 2.0], [3.0, 4.0]],
|
|
"rssi": -45,
|
|
"noise_floor": -90
|
|
}
|
|
|
|
response = await client.post("/api/v1/csi-data", json=csi_data)
|
|
assert response.status_code == 201
|
|
|
|
# Test pose detection retrieval
|
|
response = await client.get(f"/api/v1/sessions/{session_id}/pose-detections")
|
|
assert response.status_code == 200
|
|
|
|
# Test session completion
|
|
response = await client.patch(
|
|
f"/api/v1/sessions/{session_id}",
|
|
json={"status": "completed"}
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
async def test_background_tasks_integration(self, settings, db_manager):
|
|
"""Test background tasks integration."""
|
|
|
|
# Test cleanup manager
|
|
cleanup_manager = get_cleanup_manager(settings)
|
|
cleanup_stats = cleanup_manager.get_stats()
|
|
assert "manager" in cleanup_stats
|
|
|
|
# Run cleanup task
|
|
cleanup_result = await cleanup_manager.run_all_tasks()
|
|
assert cleanup_result["success"] is True
|
|
|
|
# Test monitoring manager
|
|
monitoring_manager = get_monitoring_manager(settings)
|
|
monitoring_stats = monitoring_manager.get_stats()
|
|
assert "manager" in monitoring_stats
|
|
|
|
# Run monitoring task
|
|
monitoring_result = await monitoring_manager.run_all_tasks()
|
|
assert monitoring_result["success"] is True
|
|
|
|
# Test backup manager
|
|
backup_manager = get_backup_manager(settings)
|
|
backup_stats = backup_manager.get_stats()
|
|
assert "manager" in backup_stats
|
|
|
|
# Run backup task
|
|
backup_result = await backup_manager.run_all_tasks()
|
|
assert backup_result["success"] is True
|
|
|
|
async def test_error_handling_integration(self, client, settings, db_manager):
|
|
"""Test error handling across the system."""
|
|
|
|
# Test invalid device creation
|
|
invalid_device_data = {
|
|
"name": "", # Invalid empty name
|
|
"ip_address": "invalid_ip",
|
|
"device_type": "unknown_type"
|
|
}
|
|
|
|
response = await client.post("/api/v1/devices", json=invalid_device_data)
|
|
assert response.status_code == 422
|
|
error_response = response.json()
|
|
assert "detail" in error_response
|
|
|
|
# Test non-existent resource access
|
|
response = await client.get("/api/v1/devices/99999")
|
|
assert response.status_code == 404
|
|
|
|
# Test invalid session creation
|
|
invalid_session_data = {
|
|
"device_id": "invalid_uuid",
|
|
"session_type": "invalid_type"
|
|
}
|
|
|
|
response = await client.post("/api/v1/sessions", json=invalid_session_data)
|
|
assert response.status_code == 422
|
|
|
|
async def test_authentication_and_authorization(self, client, settings):
|
|
"""Test authentication and authorization integration."""
|
|
|
|
# Test protected endpoint without authentication
|
|
response = await client.get("/api/v1/admin/system-info")
|
|
assert response.status_code in [401, 403]
|
|
|
|
# Test with invalid token
|
|
headers = {"Authorization": "Bearer invalid_token"}
|
|
response = await client.get("/api/v1/admin/system-info", headers=headers)
|
|
assert response.status_code in [401, 403]
|
|
|
|
async def test_rate_limiting_integration(self, client, settings):
|
|
"""Test rate limiting integration."""
|
|
|
|
# Make multiple rapid requests to test rate limiting
|
|
responses = []
|
|
for i in range(10):
|
|
response = await client.get("/health")
|
|
responses.append(response.status_code)
|
|
|
|
# Should have at least some successful responses
|
|
assert 200 in responses
|
|
|
|
# Rate limiting might kick in for some requests
|
|
# This depends on the rate limiting configuration
|
|
|
|
async def test_monitoring_and_metrics_integration(self, client, settings, db_manager):
|
|
"""Test monitoring and metrics collection integration."""
|
|
|
|
# Test metrics endpoint
|
|
response = await client.get("/metrics")
|
|
assert response.status_code == 200
|
|
metrics_text = response.text
|
|
|
|
# Check for Prometheus format metrics
|
|
assert "# HELP" in metrics_text
|
|
assert "# TYPE" in metrics_text
|
|
|
|
# Test health check with detailed information
|
|
response = await client.get("/health?detailed=true")
|
|
assert response.status_code == 200
|
|
health_data = response.json()
|
|
|
|
assert "database" in health_data
|
|
assert "services" in health_data
|
|
assert "system" in health_data
|
|
|
|
async def test_configuration_management_integration(self, settings):
|
|
"""Test configuration management integration."""
|
|
|
|
# Test settings validation
|
|
assert settings.environment == "test"
|
|
assert settings.debug is True
|
|
|
|
# Test database URL configuration
|
|
assert "test_integration.db" in settings.database_url
|
|
|
|
# Test Redis configuration
|
|
assert settings.redis_enabled is False
|
|
|
|
# Test logging configuration
|
|
assert settings.log_level in ["DEBUG", "INFO", "WARNING", "ERROR"]
|
|
|
|
async def test_database_migration_integration(self, settings, db_manager):
|
|
"""Test database migration integration."""
|
|
|
|
# Test database connection
|
|
await db_manager.test_connection()
|
|
|
|
# Test table creation
|
|
async with db_manager.get_async_session() as session:
|
|
from sqlalchemy import text
|
|
|
|
# Check if tables exist
|
|
tables_query = text("""
|
|
SELECT name FROM sqlite_master
|
|
WHERE type='table' AND name NOT LIKE 'sqlite_%'
|
|
""")
|
|
|
|
result = await session.execute(tables_query)
|
|
tables = [row[0] for row in result.fetchall()]
|
|
|
|
# Should have our main tables
|
|
expected_tables = ["devices", "sessions", "csi_data", "pose_detections"]
|
|
for table in expected_tables:
|
|
assert table in tables
|
|
|
|
async def test_concurrent_operations_integration(self, client, settings, db_manager):
|
|
"""Test concurrent operations integration."""
|
|
|
|
async def create_device(name: str):
|
|
device_data = {
|
|
"name": f"test_device_{name}",
|
|
"ip_address": f"192.168.1.{name}",
|
|
"device_type": "router",
|
|
"model": "test_model"
|
|
}
|
|
response = await client.post("/api/v1/devices", json=device_data)
|
|
return response.status_code
|
|
|
|
# Create multiple devices concurrently
|
|
tasks = [create_device(str(i)) for i in range(5)]
|
|
results = await asyncio.gather(*tasks)
|
|
|
|
# All should succeed
|
|
assert all(status == 201 for status in results)
|
|
|
|
# Verify all devices were created
|
|
response = await client.get("/api/v1/devices")
|
|
assert response.status_code == 200
|
|
devices_data = response.json()
|
|
assert len(devices_data["devices"]) >= 5
|
|
|
|
async def test_system_resource_management(self, settings, db_manager, orchestrator):
|
|
"""Test system resource management integration."""
|
|
|
|
# Test connection pool management
|
|
stats = await db_manager.get_connection_stats()
|
|
assert "database" in stats
|
|
assert "pool_size" in stats["database"]
|
|
|
|
# Test service resource usage
|
|
health_status = await orchestrator.get_health_status()
|
|
assert "memory_usage" in health_status
|
|
assert "cpu_usage" in health_status
|
|
|
|
# Test cleanup of resources
|
|
await orchestrator.cleanup_resources()
|
|
|
|
# Verify resources are cleaned up
|
|
final_stats = await db_manager.get_connection_stats()
|
|
assert final_stats is not None
|
|
|
|
|
|
@pytest.mark.integration
|
|
class TestSystemPerformance:
|
|
"""Test system performance under load."""
|
|
|
|
async def test_api_response_times(self, client):
|
|
"""Test API response times under normal load."""
|
|
|
|
start_time = time.time()
|
|
response = await client.get("/health")
|
|
end_time = time.time()
|
|
|
|
assert response.status_code == 200
|
|
assert (end_time - start_time) < 1.0 # Should respond within 1 second
|
|
|
|
async def test_database_query_performance(self, db_manager):
|
|
"""Test database query performance."""
|
|
|
|
async with db_manager.get_async_session() as session:
|
|
from sqlalchemy import text
|
|
|
|
start_time = time.time()
|
|
result = await session.execute(text("SELECT 1"))
|
|
end_time = time.time()
|
|
|
|
assert result.scalar() == 1
|
|
assert (end_time - start_time) < 0.1 # Should complete within 100ms
|
|
|
|
async def test_memory_usage_stability(self, orchestrator):
|
|
"""Test memory usage remains stable."""
|
|
|
|
import psutil
|
|
import os
|
|
|
|
process = psutil.Process(os.getpid())
|
|
initial_memory = process.memory_info().rss
|
|
|
|
# Perform some operations
|
|
for _ in range(10):
|
|
health_status = await orchestrator.get_health_status()
|
|
assert health_status is not None
|
|
|
|
final_memory = process.memory_info().rss
|
|
memory_increase = final_memory - initial_memory
|
|
|
|
# Memory increase should be reasonable (less than 50MB)
|
|
assert memory_increase < 50 * 1024 * 1024
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"]) |