281 lines
11 KiB
Python
281 lines
11 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)
|
|
)
|
|
""")
|
|
|
|
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 == [] |