Files
next-tjwater-server/tests/api/test_scada_replay_api.py
T
jiang 26fa5d1909
Generic Container CI/CD / test-build-publish (push) Successful in 3m39s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 3m39s
feat(scada): add project monitoring replay
2026-11-04 14:27:21 +08:00

114 lines
4.5 KiB
Python

import asyncio
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 import scada_replay
from app.auth.project_dependencies import ProjectContext, get_project_context
@pytest.fixture
def replay_api(monkeypatch):
context = ProjectContext(uuid4(), "demo", uuid4(), "member")
project = SimpleNamespace(settings={"scada_monitoring_interval_seconds": 900, "other": True})
metadata = SimpleNamespace(get_project_by_id=AsyncMock(return_value=project))
repo = SimpleNamespace(available=AsyncMock(return_value=True), current=AsyncMock(return_value=None))
create = AsyncMock(side_effect=RuntimeError("creation mock must be configured"))
monkeypatch.setattr(scada_replay.ScadaReplayService, "create", create)
app = FastAPI()
app.include_router(scada_replay.router)
app.dependency_overrides[get_project_context] = lambda: context
app.dependency_overrides[scada_replay.repository] = lambda: repo
app.dependency_overrides[scada_replay.get_metadata_repository] = lambda: metadata
with TestClient(app) as client:
yield client, project, metadata, context, create
def test_capabilities_reads_frequency_from_current_project_settings(replay_api):
client, project, metadata, context, _ = replay_api
response = client.get("/scada-replays/capabilities")
assert response.status_code == 200
assert response.json()["monitoring_interval_seconds"] == 900
assert response.json()["burst_monitoring_ready"] is False
assert "尚未配置" in response.json()["burst_monitoring_reason"]
metadata.get_project_by_id.assert_awaited_once_with(context.project_id)
project.settings = {}
assert client.get("/scada-replays/capabilities").json()["monitoring_interval_seconds"] is None
def test_capabilities_reports_ready_only_with_five_pressures_and_outlet_flow(replay_api):
client, project, *_ = replay_api
project.settings = {
"scada_monitoring_interval_seconds": 900,
"scada_monitoring": {
"pressure_device_ids": ["P1", "P2", "P3", "P4", "P5"],
"outlet_flow_device_ids": ["F1"],
},
}
response = client.get("/scada-replays/capabilities")
assert response.status_code == 200
assert response.json()["burst_monitoring_ready"] is True
assert response.json()["burst_monitoring_reason"] is None
def test_capabilities_reports_only_missing_storage_not_worker():
repo = SimpleNamespace(available=AsyncMock(return_value=False))
context = ProjectContext(uuid4(), "demo", uuid4(), "member")
result = asyncio.run(scada_replay.replay_capabilities(
repo=repo,
ctx=context,
settings=scada_replay.ProjectSettings(scada_monitoring_interval_seconds=900),
))
assert result.available is False
assert result.reason == "需先部署 SCADA 回放存储结构"
def test_step_command_processes_pending_period_in_same_request(monkeypatch):
calls = []
class Service:
def __init__(self, repo, context, timescale):
calls.append((repo, context, timescale))
async def command(self, replay_id, command):
calls.append((replay_id, command.action))
return SimpleNamespace(step_pending=True)
async def process_pending_step(self, replay_id):
calls.append((replay_id, "processed"))
return "completed-period"
monkeypatch.setattr(scada_replay, "ScadaReplayService", Service)
replay_id = uuid4()
context = ProjectContext(uuid4(), "demo", uuid4(), "member")
command = scada_replay.ScadaReplayCommand(
request_id=uuid4(), expected_revision=1, action="step"
)
result = asyncio.run(scada_replay.replay_command(
replay_id, command, repo="repo", ctx=context, timescale="timescale"
))
assert result == "completed-period"
assert calls[-2:] == [(replay_id, "step"), (replay_id, "processed")]
def test_client_cannot_override_project_frequency_on_creation(replay_api):
client, _, _, _, create = replay_api
response = client.post("/scada-replays", json={
"request_id": str(uuid4()), "start_time": "2026-10-08T00:00:00Z",
"end_time": "2026-10-08T01:00:00Z", "device_ids": ["P1"],
"rules": [{"rule_id": "p", "device_ids": ["P1"], "kind": "bounds", "upper": 10, "unit": "m"}],
"monitoring_interval_seconds": 300,
})
assert response.status_code == 422
create.assert_not_awaited()