Files
next-tjwater-server/tests/unit/test_online_simulation.py
jiang be6317eb48 feat(server): integrate MCP queries and investigation messaging
Add project-bound OAuth MCP queries and controlled analysis with scoped evidence,
limits, timeouts and a nearby-model spatial query using EPSG:4326 inputs.
Default nearby searches to 200 meters and five results.

Integrate investigation messaging/presentation, health/readiness and typed history;
remove duplicate web search and fix online simulation and compressed baseline writes.
Include project migrations 023-025; keep local credentials and demo seeding excluded.

Validation: 319 focused pytest checks; real PostGIS temporary-table regression;
read-only Jinqian bridge radius comparison; cross-language MCP discovery/calls.
2026-10-19 18:46:58 +08:00

66 lines
3.0 KiB
Python

from datetime import datetime, timedelta
from unittest.mock import Mock
import pytest
from app.services import simulation
START = datetime.fromisoformat("2026-10-08T12:00:00+08:00")
def test_online_simulation_uses_realtime_steps_and_returns_requested_samples(monkeypatch):
calls = []
mappings = object()
monkeypatch.setattr(simulation, "get_time", lambda _: {"HYDRAULIC TIMESTEP": "0:15:00"})
monkeypatch.setattr(simulation, "query_corresponding_element_id_and_query_id", lambda _: mappings)
def run(**kwargs):
calls.append(kwargs)
return {
"units": {"pressure": "MTR", "flow": "LPS"},
"node_results": [{"node": "123422", "result": [{"pressure": len(calls)}]}, {"node": "other", "result": [{}]}],
"link_results": [{"link": "540672", "result": [{"flow": len(calls)}]}],
}
monkeypatch.setattr(simulation, "run_simulation", run)
result = simulation.run_online_simulation("online", START, 3600, output_node_ids=["123422"], output_link_ids=["540672"])
assert calls == [{
"name": "online", "simulation_type": "realtime",
"modify_pattern_start_time": (START + timedelta(minutes=15 * index)).isoformat(timespec="seconds"),
"scada_mappings": mappings,
} for index in range(4)]
assert result["node_results"] == [{"node": "123422", "result": [{"pressure": index} for index in range(1, 5)]}]
assert result["link_results"] == [{"link": "540672", "result": [{"flow": index} for index in range(1, 5)]}]
assert result["times"]["num_periods"] == 4
assert result["times"]["report_step"] == 900
assert result["times"]["sample_times"][-1] == "2026-10-08T12:45:00+08:00"
def test_online_simulation_rejects_invalid_step_before_calculating(monkeypatch):
monkeypatch.setattr(simulation, "get_time", lambda _: {"HYDRAULIC TIMESTEP": "0:00:00"})
run = Mock()
monkeypatch.setattr(simulation, "run_simulation", run)
with pytest.raises(ValueError, match="HYDRAULIC TIMESTEP"):
simulation.run_online_simulation("online", START, 3600)
run.assert_not_called()
def test_mcp_online_simulation_does_not_copy_a_database(monkeypatch):
from app.mcp.scenario_service import calculate_scenario
def reject_copy(*_):
pytest.fail("Online simulation must not clone the project database")
run = Mock(return_value={"node_results": [], "link_results": [], "times": {}, "units": {}})
monkeypatch.setattr("app.native.wndb.core.projects.temporary_project_database", reject_copy)
monkeypatch.setattr(simulation, "run_online_simulation", run, raising=False)
result = calculate_scenario("run_simulation", {
"start_time": START, "duration": 3600,
"output_node_ids": ["123422", "123425"], "output_link_ids": ["540672"],
}, network="online", username="actor")
run.assert_called_once_with("online", START, 3600, output_node_ids=["123422", "123425"], output_link_ids=["540672"])
assert result["input_source"] == "online_model_simulation"
assert result["persisted_baseline"] is True
assert result["persisted_scheme"] is False