""" Integration tests for Phase 1 components using SQLite. These tests use SQLite for integration testing without requiring PostgreSQL. """ import pytest import pytest_asyncio from typing import Dict, Any import os import tempfile import sqlite3 from src.database.flexible_client import FlexibleDatabaseClient from src.core.auto_discovery import AutoDiscovery from src.core.safety import SafetyLimitEnforcer @pytest.mark.integration @pytest.mark.database class TestPhase1IntegrationSQLite: """Integration tests for Phase 1 components using SQLite.""" @pytest_asyncio.fixture(scope="class") async def integration_db_client(self): """Create SQLite database client for integration tests.""" # Create temporary SQLite database temp_db = tempfile.NamedTemporaryFile(suffix='.db', delete=False) temp_db.close() # Create schema and test data conn = sqlite3.connect(temp_db.name) cursor = conn.cursor() # Create tables cursor.execute(""" CREATE TABLE pump_stations ( station_id TEXT PRIMARY KEY, station_name TEXT, location TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) """) cursor.execute(""" CREATE TABLE pumps ( station_id TEXT, pump_id TEXT, pump_name TEXT, control_type TEXT, min_speed_hz REAL DEFAULT 20.0, max_speed_hz REAL DEFAULT 60.0, default_setpoint_hz REAL DEFAULT 35.0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (station_id, pump_id), FOREIGN KEY (station_id) REFERENCES stations(station_id) ) """) cursor.execute(""" CREATE TABLE pump_plans ( plan_id INTEGER PRIMARY KEY AUTOINCREMENT, station_id TEXT, pump_id TEXT, target_flow_m3h REAL, target_power_kw REAL, target_level_m REAL, suggested_speed_hz REAL, interval_start TIMESTAMP, interval_end TIMESTAMP, plan_version INTEGER, plan_status TEXT DEFAULT 'ACTIVE', plan_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, plan_updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, optimization_run_id TEXT, FOREIGN KEY (station_id, pump_id) REFERENCES pumps(station_id, pump_id) ) """) cursor.execute(""" CREATE TABLE pump_feedback ( feedback_id INTEGER PRIMARY KEY AUTOINCREMENT, station_id TEXT, pump_id TEXT, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, actual_speed_hz REAL, actual_power_kw REAL, actual_flow_m3h REAL, wet_well_level_m REAL, pump_running BOOLEAN, alarm_active BOOLEAN, alarm_code TEXT, FOREIGN KEY (station_id, pump_id) REFERENCES pumps(station_id, pump_id) ) """) cursor.execute(""" CREATE TABLE emergency_stop_events ( event_id INTEGER PRIMARY KEY AUTOINCREMENT, triggered_by TEXT, reason TEXT, station_id TEXT, pump_id TEXT, event_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, cleared_by TEXT, cleared_timestamp TIMESTAMP, cleared_notes TEXT, FOREIGN KEY (station_id, pump_id) REFERENCES pumps(station_id, pump_id) ) """) cursor.execute(""" CREATE TABLE pump_safety_limits ( station_id TEXT, pump_id TEXT, hard_min_speed_hz REAL, hard_max_speed_hz REAL, hard_min_level_m REAL, hard_max_level_m REAL, hard_max_power_kw REAL, max_speed_change_hz_per_min REAL, PRIMARY KEY (station_id, pump_id), FOREIGN KEY (station_id, pump_id) REFERENCES pumps(station_id, pump_id) ) """) # Insert test data cursor.execute(""" INSERT INTO pump_stations (station_id, station_name, location) VALUES ('STATION_001', 'Main Pump Station', 'Downtown Area'), ('STATION_002', 'Secondary Station', 'Industrial Zone') """) cursor.execute(""" INSERT INTO pumps (station_id, pump_id, pump_name, control_type, min_speed_hz, max_speed_hz, default_setpoint_hz) VALUES ('STATION_001', 'PUMP_001', 'Main Pump 1', 'DIRECT_SPEED', 20.0, 60.0, 35.0), ('STATION_001', 'PUMP_002', 'Main Pump 2', 'LEVEL_CONTROLLED', 20.0, 60.0, 35.0), ('STATION_002', 'PUMP_001', 'Secondary Pump 1', 'POWER_CONTROLLED', 20.0, 60.0, 35.0) """) cursor.execute(""" INSERT INTO pump_plans ( station_id, pump_id, target_flow_m3h, target_power_kw, target_level_m, suggested_speed_hz, interval_start, interval_end, plan_version, optimization_run_id ) VALUES ('STATION_001', 'PUMP_001', 150.0, NULL, NULL, 42.5, datetime('now', '-1 hour'), datetime('now', '+1 hour'), 1, 'OPT_RUN_001'), ('STATION_001', 'PUMP_002', NULL, NULL, 2.5, 38.0, datetime('now', '-1 hour'), datetime('now', '+1 hour'), 1, 'OPT_RUN_001'), ('STATION_002', 'PUMP_001', NULL, 18.5, NULL, 40.0, datetime('now', '-1 hour'), datetime('now', '+1 hour'), 1, 'OPT_RUN_001') """) cursor.execute(""" INSERT INTO pump_feedback ( station_id, pump_id, actual_speed_hz, actual_power_kw, actual_flow_m3h, wet_well_level_m, pump_running, alarm_active ) VALUES ('STATION_001', 'PUMP_001', 42.5, 16.2, 148.5, 1.8, 1, 0), ('STATION_001', 'PUMP_002', 38.0, 14.8, 135.2, 2.3, 1, 0), ('STATION_002', 'PUMP_001', 40.0, 18.3, 142.1, 1.9, 1, 0) """) cursor.execute(""" INSERT INTO pump_safety_limits ( station_id, pump_id, hard_min_speed_hz, hard_max_speed_hz, hard_min_level_m, hard_max_level_m, hard_max_power_kw, max_speed_change_hz_per_min ) VALUES ('STATION_001', 'PUMP_001', 20.0, 60.0, 1.0, 3.0, 25.0, 10.0), ('STATION_001', 'PUMP_002', 20.0, 60.0, 1.0, 3.0, 25.0, 10.0), ('STATION_002', 'PUMP_001', 20.0, 60.0, 1.0, 3.0, 25.0, 10.0) """) conn.commit() conn.close() # Create database client with SQLite URL client = FlexibleDatabaseClient(f"sqlite:///{temp_db.name}") await client.connect() client.create_tables() yield client await client.disconnect() # Clean up os.unlink(temp_db.name) @pytest.mark.asyncio async def test_database_connection_integration(self, integration_db_client): """Test database connection and basic operations.""" # Test health check assert integration_db_client.health_check() is True # Test connection stats stats = integration_db_client.get_connection_stats() assert stats["status"] == "connected" @pytest.mark.asyncio async def test_database_queries_integration(self, integration_db_client): """Test database queries with real database.""" # Test getting pump stations stations = integration_db_client.get_pump_stations() assert isinstance(stations, list) assert len(stations) == 2 # Test getting pumps pumps = integration_db_client.get_pumps() assert isinstance(pumps, list) assert len(pumps) == 3 # Test getting specific pump pump = integration_db_client.get_pump('STATION_001', 'PUMP_001') assert pump is not None assert pump['pump_id'] == 'PUMP_001' @pytest.mark.asyncio async def test_auto_discovery_integration(self, integration_db_client): """Test auto-discovery with real database.""" discovery = AutoDiscovery(integration_db_client) await discovery.discover() # Test getting stations stations = discovery.get_stations() assert len(stations) == 2 # Test getting pumps pumps = discovery.get_pumps('STATION_001') assert len(pumps) == 2 # Test getting specific pump pump = discovery.get_pump('STATION_001', 'PUMP_001') assert pump is not None assert pump['control_type'] == 'DIRECT_SPEED' @pytest.mark.asyncio async def test_safety_framework_integration(self, integration_db_client): """Test safety framework with real database.""" safety_enforcer = SafetyLimitEnforcer(integration_db_client) # Test loading safety limits await safety_enforcer.load_safety_limits() # Test enforcing limits safe_setpoint, violations = safety_enforcer.enforce_setpoint('STATION_001', 'PUMP_001', 65.0) assert safe_setpoint <= 60.0 # Should be limited to max speed safe_setpoint, violations = safety_enforcer.enforce_setpoint('STATION_001', 'PUMP_001', 15.0) assert safe_setpoint >= 20.0 # Should be limited to min speed @pytest.mark.asyncio async def test_component_interaction(self, integration_db_client): """Test interaction between components.""" # Initialize components discovery = AutoDiscovery(integration_db_client) await discovery.discover() safety_enforcer = SafetyLimitEnforcer(integration_db_client) await safety_enforcer.load_safety_limits() # Test integrated workflow pump = discovery.get_pump('STATION_001', 'PUMP_001') assert pump is not None # Test safety enforcement on discovered pump safe_setpoint, violations = safety_enforcer.enforce_setpoint( pump['station_id'], pump['pump_id'], 70.0 # Above max limit ) assert safe_setpoint <= 60.0 @pytest.mark.asyncio async def test_error_handling_integration(self, integration_db_client): """Test error handling with real database.""" # Test with non-existent pump pump = integration_db_client.get_pump('NON_EXISTENT', 'PUMP_001') assert pump is None # Test with non-existent station pumps = integration_db_client.get_pumps('NON_EXISTENT') assert pumps == []