fix(scada): preserve observation fields in chart evidence
This commit is contained in:
@@ -1116,6 +1116,31 @@ def test_time_series_sql_is_bounded_and_field_names_cannot_inject_sql():
|
||||
asyncio.run(check())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("field", ["monitored_value", "cleaned_value"])
|
||||
def test_scada_detail_preserves_the_requested_field_with_its_value_alias(monkeypatch, field):
|
||||
at = datetime.now(timezone.utc)
|
||||
monkeypatch.setattr(query_service, "permissions_for_context", lambda ctx: PERMISSIONS)
|
||||
monkeypatch.setattr(query_service.reads, "series_rows", AsyncMock(return_value=[{
|
||||
"device_id": "D1", "time": at, "value": 32.0,
|
||||
}]))
|
||||
monkeypatch.setattr(query_service.reads, "scada_devices", AsyncMock(return_value=[{
|
||||
"device_id": "D1", "measurement_unit": "m",
|
||||
}]))
|
||||
|
||||
class Scope:
|
||||
project = object()
|
||||
connection = Connection()
|
||||
|
||||
@asynccontextmanager
|
||||
async def timescale(self):
|
||||
yield self.connection
|
||||
|
||||
args = ScadaInput(context_handle="a" * 43, device_ids=["D1"], field=field,
|
||||
start=at - timedelta(hours=3), end=at)
|
||||
result = asyncio.run(query_service.execute_query(Scope(), binding(), TOOLS["query_scada_readings"], args))
|
||||
assert result.data[0][field] == result.data[0]["value"] == 32.0
|
||||
|
||||
|
||||
def test_analysis_preserves_run_units_and_reports_inference(monkeypatch):
|
||||
from app.mcp.models import AnalysisValuesInput
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""SCADA display coverage is independent of whether a device supports a conclusion."""
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from copy import deepcopy
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
@@ -16,6 +17,44 @@ END = datetime(2026, 7, 16, tzinfo=timezone.utc)
|
||||
START = END - timedelta(hours=3)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_value_only_detail_evidence_cannot_claim_missing_observations():
|
||||
from app.services.scada_presentation_coverage import verify_chart
|
||||
task, repository, _, evidence_id = await investigation()
|
||||
detail = deepcopy(repository.evidence[evidence_id])
|
||||
detail["evidence_id"] = "detail-values"
|
||||
rows = detail["data_snapshot"]["data"]["data"]["readings"]
|
||||
detail["data_snapshot"]["data"]["data"] = [
|
||||
{"device_id": row["device_id"], "time": row["time"], "value": row["monitored_value"]}
|
||||
for row in rows
|
||||
]
|
||||
value = chart(devices=["524529"])
|
||||
value["series"][0].update(data=[None] * 13, sample_times=[None] * 13)
|
||||
with pytest.raises(DomainError) as failure:
|
||||
verify_chart(task, value, [repository.evidence[evidence_id], detail], ["detail-values"])
|
||||
assert failure.value.code == "SCADA_CHART_EVIDENCE_REQUIRED"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
@pytest.mark.parametrize("field", ["monitored_value", "cleaned_value"])
|
||||
async def test_detail_fields_do_not_discard_other_observed_fields(field):
|
||||
from app.services.scada_presentation_coverage import verify_chart
|
||||
task, repository, _, evidence_id = await investigation()
|
||||
detail = deepcopy(repository.evidence[evidence_id])
|
||||
detail["evidence_id"] = "detail-cleaned"
|
||||
rows = detail["data_snapshot"]["data"]["data"]["readings"]
|
||||
detail["data_snapshot"]["data"]["data"] = [
|
||||
{"device_id": row["device_id"], "time": row["time"], "value": row["cleaned_value"] + 10,
|
||||
"cleaned_value": row["cleaned_value"] + 10} for row in rows
|
||||
]
|
||||
value = chart(devices=["524529"])
|
||||
value["scada"]["value_field"] = field
|
||||
if field == "cleaned_value":
|
||||
value["series"][0]["data"] = [point + 10 for point in value["series"][0]["data"]]
|
||||
result = verify_chart(task, value, [repository.evidence[evidence_id], detail], [evidence_id, "detail-cleaned"])
|
||||
assert result["gaps"] == []
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def anyio_backend():
|
||||
return "asyncio"
|
||||
|
||||
Reference in New Issue
Block a user