fix(scada): align location capability and investigation guidance
Generic Container CI/CD / test-build-publish (push) Successful in 5m25s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 5m26s

This commit is contained in:
2026-11-07 20:14:24 +08:00
parent bf8ea45d40
commit 01bad116c1
9 changed files with 59 additions and 21 deletions
+25 -1
View File
@@ -10,7 +10,7 @@ from fastapi import HTTPException
from app.mcp.analysis_models import BurstLocationInput
from app.mcp.catalog import TOOLS
from app.mcp.models import BoundContext
from app.mcp.models import BoundContext, ScadaInput
from app.mcp import query_service
from app.services.scada_replay_queries import replay_burst_location_inputs, replay_snapshot_rows
@@ -18,6 +18,30 @@ from app.services.scada_replay_queries import replay_burst_location_inputs, repl
START = datetime(2026, 10, 8, tzinfo=timezone.utc)
def test_replay_origin_is_preserved_without_a_provenance_only_data_gap(monkeypatch):
from app.services import scada_replay_queries
monkeypatch.setattr(query_service, "authorize_query", AsyncMock())
reading = {"time": START.isoformat(), "device_id": "P1", "cleaned_value": 20.0,
"monitored_value": 20.0, "quality": "valid"}
read = AsyncMock(return_value=[reading])
monkeypatch.setattr(scada_replay_queries, "replay_snapshot_rows", read)
project_id = uuid4()
context = {"replay_id": str(uuid4()), "knowledge_cutoff": (START + timedelta(minutes=15)).isoformat(),
"batch_sequence": 9}
connection = object()
scope = SimpleNamespace(connection=connection, project=SimpleNamespace(project_id=project_id))
binding = SimpleNamespace(replay_context=context)
args = ScadaInput(context_handle="a" * 43, device_ids=["P1"], start=START,
end=START + timedelta(minutes=15))
result = asyncio.run(query_service.execute_query(scope, binding, TOOLS["query_scada_readings"], args))
assert result.origin == "synthetic"
assert result.data[0]["device_id"] == "P1"
assert result.data_gaps == []
read.assert_awaited_once_with(connection, project_id, context, ["P1"], args.start, args.end,
args.limit + 1, args.offset)
class Cursor:
def __init__(self, rows): self.rows = rows
async def fetchone(self): return self.rows[0] if self.rows else None