feat(scada): serve project devices through pooled API
This commit is contained in:
@@ -8,6 +8,7 @@ from psycopg import connect
|
||||
|
||||
from app.core.config import get_pgconn_string
|
||||
from app.infra.db.dynamic_manager import ProjectConnectionManager
|
||||
from app.infra.db.postgresql.scada import ScadaInfoRepository
|
||||
from app.infra.db.timescaledb.sync_pool import timescale_connection
|
||||
from app.native.wndb.commands.api import delete_pattern_cascade
|
||||
from app.native.wndb.core.connection import project_connection, project_transaction
|
||||
@@ -105,6 +106,29 @@ def test_dynamic_pool_replaces_terminated_idle_connection() -> None:
|
||||
asyncio.run(exercise_pool())
|
||||
|
||||
|
||||
def test_scada_api_repository_reads_device_ids_through_dynamic_pool() -> None:
|
||||
async def read_scada_devices() -> list[dict]:
|
||||
manager = ProjectConnectionManager()
|
||||
try:
|
||||
async with manager.pg_connection(
|
||||
uuid4(), "biz_data", get_pgconn_string(db_name=PROJECT), 1, 4
|
||||
) as conn:
|
||||
return await ScadaInfoRepository.get_scadas(conn)
|
||||
finally:
|
||||
await manager.close_all()
|
||||
|
||||
devices = asyncio.run(read_scada_devices())
|
||||
|
||||
assert devices
|
||||
device_ids = [device["device_id"] for device in devices]
|
||||
assert len(device_ids) == len(set(device_ids))
|
||||
assert all(device_ids)
|
||||
assert all(
|
||||
device["longitude"] is not None and device["latitude"] is not None
|
||||
for device in devices
|
||||
)
|
||||
|
||||
|
||||
def test_nested_wndb_writes_roll_back_as_one_transaction() -> None:
|
||||
with pytest.raises(RuntimeError, match="force rollback"):
|
||||
with project_transaction(PROJECT) as conn:
|
||||
|
||||
@@ -17,8 +17,9 @@ class _FakeCursor:
|
||||
async def __aexit__(self, exc_type, exc, tb):
|
||||
return False
|
||||
|
||||
async def execute(self, query):
|
||||
async def execute(self, query, params=None):
|
||||
self.query = query
|
||||
self.params = params
|
||||
|
||||
async def fetchall(self):
|
||||
return [
|
||||
@@ -33,9 +34,14 @@ class _FakeCursor:
|
||||
"reliability": "95",
|
||||
"x": "117.1",
|
||||
"y": "32.9",
|
||||
"longitude": "121.5",
|
||||
"latitude": "30.9",
|
||||
}
|
||||
]
|
||||
|
||||
async def fetchone(self):
|
||||
return (await self.fetchall())[0]
|
||||
|
||||
|
||||
class _FakeConnection:
|
||||
def __init__(self):
|
||||
@@ -62,11 +68,25 @@ def test_get_scadas_normalizes_id_and_type():
|
||||
"reliability": 95,
|
||||
"x": 117.1,
|
||||
"y": 32.9,
|
||||
"longitude": 121.5,
|
||||
"latitude": 30.9,
|
||||
}
|
||||
]
|
||||
assert "node_id" in conn.cursor_instance.query
|
||||
assert "link_id" in conn.cursor_instance.query
|
||||
assert "FROM gis.scada_devices" in conn.cursor_instance.query
|
||||
assert "ST_Transform(geom, 4326)" in conn.cursor_instance.query
|
||||
|
||||
|
||||
def test_get_scada_filters_the_project_view_by_device_id():
|
||||
conn = _FakeConnection()
|
||||
|
||||
result = asyncio.run(ScadaInfoRepository.get_scada(conn, "25470001"))
|
||||
|
||||
assert result is not None
|
||||
assert result["device_id"] == "25470001"
|
||||
assert "WHERE id = %s" in conn.cursor_instance.query
|
||||
assert conn.cursor_instance.params == ("25470001",)
|
||||
|
||||
|
||||
def test_realtime_element_mappings_are_project_local_and_immutable(monkeypatch):
|
||||
|
||||
@@ -3,7 +3,7 @@ from datetime import datetime, timezone
|
||||
from unittest.mock import AsyncMock
|
||||
from uuid import uuid4
|
||||
|
||||
from app.api.v1.endpoints import project_data
|
||||
from app.api.v1.endpoints import scada as scada_endpoint
|
||||
from app.services import timeseries_analysis as composite_queries
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ PROJECT_SCADA = {
|
||||
"reliability": 1.0,
|
||||
"x": 117.1,
|
||||
"y": 32.9,
|
||||
"longitude": 121.5,
|
||||
"latitude": 30.9,
|
||||
}
|
||||
START_TIME = datetime(2026, 6, 1, tzinfo=timezone.utc)
|
||||
END_TIME = datetime(2026, 6, 2, tzinfo=timezone.utc)
|
||||
@@ -128,11 +130,11 @@ def test_element_scada_query_uses_current_project_metadata(monkeypatch):
|
||||
|
||||
def test_scada_info_endpoint_uses_current_project_connection(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
project_data.ScadaInfoRepository,
|
||||
scada_endpoint.ScadaInfoRepository,
|
||||
"get_scadas",
|
||||
AsyncMock(return_value=[PROJECT_SCADA.copy()]),
|
||||
)
|
||||
|
||||
result = asyncio.run(project_data.get_scada_info_with_connection(object()))
|
||||
result = asyncio.run(scada_endpoint.get_scada_devices(object()))
|
||||
|
||||
assert result == {"success": True, "data": [PROJECT_SCADA], "count": 1}
|
||||
assert result == [PROJECT_SCADA]
|
||||
|
||||
Reference in New Issue
Block a user