fix(isolation): distinguish accident links from same-numbered nodes
Prefer link endpoints for legacy accident IDs and allow explicit node selection. Read authoritative initial statuses and sources, verify source isolation, and separate direct isolation from newly disconnected supply nodes. Return validation errors as HTTP 422. The node-first lookup misrouted adjacent Fengyang pipes 19025 and 19026 into unrelated regions. Regression cases fail against the previous algorithm and pass after this change.
This commit is contained in:
@@ -254,14 +254,16 @@ def test_committed_write_refresh_failure_has_explicit_http_contract() -> None:
|
||||
assert response.json()["code"] == "materialized_view_refresh_failed_after_commit"
|
||||
|
||||
|
||||
def test_valve_isolation_runtime_accepts_frontend_query(monkeypatch) -> None:
|
||||
@pytest.mark.parametrize("element_type", ["auto", "link", "node"])
|
||||
def test_valve_isolation_runtime_accepts_frontend_query(monkeypatch, element_type) -> None:
|
||||
captured: dict[str, object] = {}
|
||||
|
||||
def fake_analyze_valve_isolation(network, accident_element, disabled_valves):
|
||||
def fake_analyze_valve_isolation(network, accident_element, disabled_valves, accident_element_type):
|
||||
captured.update(
|
||||
network=network,
|
||||
accident_element=accident_element,
|
||||
disabled_valves=disabled_valves,
|
||||
accident_element_type=accident_element_type,
|
||||
business_dsn=get_project_pgconn_string(network),
|
||||
timescale_dsn=get_project_timescale_pgconn_string(network),
|
||||
)
|
||||
@@ -288,6 +290,7 @@ def test_valve_isolation_runtime_accepts_frontend_query(monkeypatch) -> None:
|
||||
("accident_element", "P-1"),
|
||||
("accident_element", "P-2"),
|
||||
("disabled_valves", "V-9"),
|
||||
*([] if element_type == "auto" else [("accident_element_type", element_type)]),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -297,11 +300,39 @@ def test_valve_isolation_runtime_accepts_frontend_query(monkeypatch) -> None:
|
||||
"network": "fengyang",
|
||||
"accident_element": ["P-1", "P-2"],
|
||||
"disabled_valves": ["V-9"],
|
||||
"accident_element_type": element_type,
|
||||
"business_dsn": "postgresql://user:password@biz/fengyang",
|
||||
"timescale_dsn": "postgresql://user:password@timescale/fengyang",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"target,disabled,detail",
|
||||
[("P", "typo", "Disabled valve"), ("missing", "V", "Accident element")],
|
||||
)
|
||||
def test_valve_isolation_invalid_ids_return_validation_error(monkeypatch, target, disabled, detail):
|
||||
from app.services import valve_isolation as service
|
||||
|
||||
monkeypatch.setattr(
|
||||
service, "get_valve_isolation_topology",
|
||||
lambda _network: (["P:pipe:A:B", "V:valve:B:R"], ["R"], []),
|
||||
)
|
||||
monkeypatch.setattr(simulation_endpoint, "analyze_valve_isolation", service.analyze_valve_isolation)
|
||||
app = FastAPI()
|
||||
app.include_router(api_router, prefix="/api/v1")
|
||||
_override_project_routing(app, ProjectContext(
|
||||
project_id=uuid4(), project_code="project", user_id=uuid4(), project_role="member",
|
||||
))
|
||||
|
||||
response = TestClient(app, raise_server_exceptions=False).post(
|
||||
"/api/v1/valve-isolation-analyses",
|
||||
params={"accident_element": target, "disabled_valves": disabled},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert detail in response.json()["detail"]
|
||||
|
||||
|
||||
def test_scada_cleaning_runs_are_post() -> None:
|
||||
methods_by_path = {
|
||||
route.path: route.methods
|
||||
|
||||
Reference in New Issue
Block a user