CalejoControl/tests/integration/test_phase1_integration_sql...

255 lines
9.7 KiB
Python

"""
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)
)
""")
# 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)
""")
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 == []