feat(investigations): refine report authoring and replay flushing
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
from datetime import datetime, timezone
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from app.mcp.analysis_models import FlushingInput
|
||||
from app.mcp.extended_models import SavedScenarioInput
|
||||
from app.mcp.mutation_service import saved_scenario
|
||||
from app.mcp.scenario_service import calculate_scenario, flushing_valve_controls
|
||||
|
||||
|
||||
def parameters(**changes):
|
||||
return {"context_handle": "a" * 43, "start_time": datetime(2026, 7, 16, tzinfo=timezone.utc),
|
||||
"duration": 1800, "output_node_ids": ["J1"], "drainage_node_id": "J2",
|
||||
"flushing_flow_m3h": 12, **changes}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("changes", [
|
||||
{"closed_valves": ["V1"], "opened_valves": ["V1"]},
|
||||
{"closed_valves": ["V1", "V1"]},
|
||||
{"opened_valves": ["V1", "V1"]},
|
||||
])
|
||||
def test_conflicting_or_duplicate_valves_are_rejected_for_preview_and_saved_scenarios(changes):
|
||||
values = parameters(**changes)
|
||||
with pytest.raises(ValidationError):
|
||||
FlushingInput.model_validate(values)
|
||||
with pytest.raises(ValidationError):
|
||||
SavedScenarioInput.model_validate({"context_handle": values.pop("context_handle"),
|
||||
"request_id": uuid4(), "name": "冲洗", "scenario": "flushing", "parameters": values})
|
||||
|
||||
|
||||
def test_preview_and_saved_flushing_apply_identical_open_and_closed_states(monkeypatch):
|
||||
from app.services import simulation_scenarios
|
||||
|
||||
calls = []
|
||||
monkeypatch.setattr(simulation_scenarios, "flushing_analysis", lambda **kw: calls.append(kw) or {
|
||||
"node_results": [{"node": "J1", "result": []}], "link_results": []})
|
||||
monkeypatch.setattr("app.native.wndb.model.options.get_option", lambda _: {"UNITS": "LPS"})
|
||||
args = FlushingInput.model_validate(parameters(opened_valves=["V-in"], closed_valves=["V-user"]))
|
||||
output = calculate_scenario("analyze_flushing", args.model_dump(exclude={"context_handle", "timeout_seconds"}),
|
||||
network="owned", username="actor")
|
||||
values = SavedScenarioInput.model_validate({"context_handle": args.context_handle,
|
||||
"request_id": uuid4(), "name": "分区冲洗", "scenario": "flushing",
|
||||
"parameters": args.model_dump(exclude={"context_handle", "timeout_seconds"})})
|
||||
saved_scenario("owned", "actor", values)
|
||||
for call in calls:
|
||||
assert call["valve_control"] == {"V-in": {"status": "OPEN"}, "V-user": {"status": "CLOSED"}}
|
||||
assert "modify_valve_opening" not in call
|
||||
assert call["drainage_node_ID"] == "J2" and call["flushing_flow"] == 12
|
||||
assert calls[0]["persist_results"] is False and calls[1]["persist_results"] is True
|
||||
assert output["flushing_plan"] == {
|
||||
"opened_valves": ["V-in"], "closed_valves": ["V-user"], "drainage_node_id": "J2",
|
||||
"flushing_flow_m3h": 12, "estimated_discharge_volume_m3": 6,
|
||||
"state_basis": "independent_current_model_scenario", "result_scope": "hydraulic_only",
|
||||
"boundary_source": "model_with_start_time_scada",
|
||||
"field_execution_verified": False, "water_quality_clearance_verified": False,
|
||||
}
|
||||
|
||||
|
||||
def test_legacy_closed_only_inputs_remain_valid():
|
||||
args = FlushingInput.model_validate(parameters(closed_valves=["V1"]))
|
||||
assert args.opened_valves == []
|
||||
assert flushing_valve_controls(args.model_dump()) == {"V1": {"status": "CLOSED"}}
|
||||
|
||||
|
||||
def test_explicit_opening_reopens_a_closed_valve_without_overwriting_its_setting(monkeypatch):
|
||||
from app.services import simulation
|
||||
|
||||
model = {"V-in": {"link": "V-in", "status": "CLOSED", "setting": 15},
|
||||
"V-user": {"link": "V-user", "status": "ACTIVE", "setting": 23}}
|
||||
monkeypatch.setattr(simulation, "get_status", lambda project, valve: dict(model[valve]))
|
||||
monkeypatch.setattr(simulation, "set_status", lambda project, changes: model.update({
|
||||
row["link"]: row for row in changes.operations}))
|
||||
simulation._apply_valve_control("temporary", flushing_valve_controls({
|
||||
"opened_valves": ["V-in"], "closed_valves": ["V-user"]}))
|
||||
assert model["V-in"] == {"link": "V-in", "status": "OPEN", "setting": 15}
|
||||
assert model["V-user"] == {"link": "V-user", "status": "CLOSED", "setting": 23}
|
||||
Reference in New Issue
Block a user