refactor(db)!: clean up business SQL access

- make realtime replacement and analysis result writes transactional\n- consolidate SCADA repositories and remove process-global project state\n- validate SCADA batches and use indexed GIS-backed business queries\n\nBREAKING CHANGE: remove the public analysis result writer and the pipeline-health network_name query parameter.
This commit is contained in:
2026-08-28 11:37:36 +08:00
parent b74799a39d
commit 9b095c7439
34 changed files with 859 additions and 921 deletions
@@ -0,0 +1,59 @@
import asyncio
from datetime import datetime, timezone
from unittest.mock import AsyncMock
import pytest
from fastapi import HTTPException
from app.api.v1.endpoints.timeseries import scada as scada_endpoint
def _reading(device_id: str) -> scada_endpoint.ScadaReadingBatchItem:
return scada_endpoint.ScadaReadingBatchItem(
time=datetime(2026, 8, 27, tzinfo=timezone.utc),
device_id=device_id,
monitored_value=12.5,
)
def test_scada_batch_rejects_devices_missing_from_bizdb(monkeypatch):
monkeypatch.setattr(
scada_endpoint.ScadaInfoRepository,
"get_existing_device_ids",
AsyncMock(return_value={"known"}),
)
insert = AsyncMock()
monkeypatch.setattr(scada_endpoint.ScadaRepository, "insert_scada_batch", insert)
with pytest.raises(HTTPException, match="missing") as exc_info:
asyncio.run(
scada_endpoint.insert_scada_data(
[_reading("known"), _reading("missing")],
conn=object(),
postgres_conn=object(),
)
)
assert exc_info.value.status_code == 422
insert.assert_not_awaited()
def test_scada_batch_writes_only_after_bizdb_validation(monkeypatch):
monkeypatch.setattr(
scada_endpoint.ScadaInfoRepository,
"get_existing_device_ids",
AsyncMock(return_value={"known"}),
)
insert = AsyncMock()
monkeypatch.setattr(scada_endpoint.ScadaRepository, "insert_scada_batch", insert)
result = asyncio.run(
scada_endpoint.insert_scada_data(
[_reading(" known ")],
conn=object(),
postgres_conn=object(),
)
)
assert result == {"message": "Inserted 1 records"}
assert insert.await_args.args[1][0]["device_id"] == "known"
+4 -2
View File
@@ -1,4 +1,5 @@
from datetime import datetime, timezone
from types import SimpleNamespace
from fastapi.testclient import TestClient
@@ -39,7 +40,9 @@ def _load_simulation_module(monkeypatch):
{
"get_time": lambda name: {"HYDRAULIC TIMESTEP": "0:15:00"},
"run_simulation": lambda **kwargs: None,
"query_corresponding_element_id_and_query_id": lambda name: None,
"query_corresponding_element_id_and_query_id": lambda name: SimpleNamespace(
fixed_pumps={}, variable_pumps={}
),
"query_corresponding_pattern_id_and_query_id": lambda name: None,
"query_non_realtime_region": lambda name: [],
"get_source_outflow_region_id": lambda name, region_result: {},
@@ -49,7 +52,6 @@ def _load_simulation_module(monkeypatch):
"get_realtime_region_patterns": lambda name, source_outflow_region_id, realtime_region_pipe_flow_and_demand_id: ({}, {}),
},
)
install_stub(monkeypatch, "app.services.globals", {})
install_stub(
monkeypatch,
"app.services.tjnetwork",