114 lines
4.5 KiB
Python
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()
|