feat(scada): add project monitoring replay
Generic Container CI/CD / test-build-publish (push) Successful in 3m39s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 3m39s

This commit is contained in:
2026-11-04 14:27:21 +08:00
parent f25423dec4
commit 26fa5d1909
53 changed files with 5153 additions and 20 deletions
+60
View File
@@ -0,0 +1,60 @@
"""HTTP boundary checks without project database access."""
from datetime import datetime, timezone
from types import SimpleNamespace
from unittest.mock import AsyncMock
from uuid import uuid4
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from app.api.v1.endpoints.timeseries import scada
from app.domain.schemas.scada import ScadaRawPage
@pytest.fixture
def raw_api(monkeypatch):
app = FastAPI()
app.include_router(scada.router)
project = uuid4()
app.dependency_overrides[scada.get_timescale_connection] = lambda: object()
app.dependency_overrides[scada.get_postgres_connection] = lambda: object()
app.dependency_overrides[scada.get_project_context] = lambda: SimpleNamespace(project_id=project)
devices = AsyncMock(return_value={"P1"})
monkeypatch.setattr(scada.ScadaInfoRepository, "get_existing_device_ids", devices)
read = AsyncMock(return_value=ScadaRawPage(items=[], next_cursor=None, scanned_through=datetime(2026, 10, 2, tzinfo=timezone.utc)))
monkeypatch.setattr(scada, "read_raw_page", read)
with TestClient(app) as client:
yield client, devices, read, project
def test_raw_http_returns_only_read_projection_and_binds_project(raw_api):
client, devices, read, project = raw_api
response = client.get("/timeseries/scada-readings/raw", params={
"start_time": "2026-10-01T00:00:00Z", "end_time": "2026-10-02T00:00:00Z", "device_ids": "P1,P1",
})
assert response.status_code == 200
assert response.json()["items"] == []
assert read.await_args.kwargs["project_id"] == str(project)
assert read.await_args.kwargs["device_ids"] == ["P1"]
devices.assert_awaited_once()
def test_raw_http_rejects_naive_time_before_replay_lookup(raw_api):
client, devices, read, _ = raw_api
response = client.get("/timeseries/scada-readings/raw", params={
"start_time": "2026-10-01T00:00:00", "end_time": "2026-10-02T00:00:00Z",
"device_ids": "P1", "replay_id": str(uuid4()),
})
assert response.status_code == 422
devices.assert_not_awaited()
read.assert_not_awaited()
def test_raw_http_rejects_cross_project_device_before_tsdb(raw_api):
client, _, read, _ = raw_api
response = client.get("/timeseries/scada-readings/raw", params={
"start_time": "2026-10-01T00:00:00Z", "end_time": "2026-10-02T00:00:00Z", "device_ids": "other",
})
assert response.status_code == 422
read.assert_not_awaited()