feat: extend operational investigation workflows
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,6 +300,7 @@ 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",
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ def test_dynamic_investigation_routes_are_in_the_public_contract():
|
||||
assert "get" in paths["/api/v1/operational-tasks/{task_id}/reports"]
|
||||
assert "get" in paths["/api/v1/operational-tasks/{task_id}/reports/{revision}"]
|
||||
assert "post" in paths["/api/v1/operational-tasks/{task_id}/presentation-events"]
|
||||
assert "get" in paths["/api/v1/operational-tasks/{task_id}/presentation-events"]
|
||||
assert "get" in paths["/api/v1/operational-tasks/{task_id}/presentation-cursor"]
|
||||
context_params = paths["/api/v1/operational-tasks/{task_id}/agent-context"]["get"]["parameters"]
|
||||
assert any(item["name"] == "report_revision" and item["in"] == "query" for item in context_params)
|
||||
@@ -123,3 +124,21 @@ async def test_report_endpoints_preserve_interactive_blocks_and_version_context(
|
||||
schemas = app.openapi()["components"]["schemas"]
|
||||
assert "ui_blocks" in schemas["InvestigationReportInput"]["properties"]
|
||||
assert "ui_blocks" in schemas["InvestigationReport"]["properties"]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_presentation_history_route_forwards_read_only_paging_and_domain_errors(monkeypatch):
|
||||
service = SimpleNamespace(presentation_history=AsyncMock(return_value={"items": []}))
|
||||
monkeypatch.setattr(endpoints, "_service", lambda _ctx, _conn: service)
|
||||
task_id = uuid4()
|
||||
await endpoints.get_operational_task_presentation_history(
|
||||
task_id, after=7, through=30, limit=20, ctx=_service_context(), conn=object(),
|
||||
)
|
||||
service.presentation_history.assert_awaited_once_with(task_id, after=7, through=30, limit=20)
|
||||
from app.domain.domain_errors import DomainError
|
||||
service.presentation_history.side_effect = DomainError("TASK_NOT_FOUND", "missing", status=404)
|
||||
with pytest.raises(HTTPException) as error:
|
||||
await endpoints.get_operational_task_presentation_history(
|
||||
task_id, after=0, through=None, limit=100, ctx=_service_context(), conn=object(),
|
||||
)
|
||||
assert error.value.status_code == 404
|
||||
|
||||
@@ -9,7 +9,7 @@ def test_main_server_mounts_peripheral_health_endpoint():
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"service": "peripheral-sample",
|
||||
"origin": "synthetic",
|
||||
"origin": "system_data",
|
||||
"storage": "postgresql",
|
||||
"status": "up",
|
||||
"storage_verified": False,
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import asyncio
|
||||
from uuid import uuid4
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.api.v1.endpoints import operational_tasks as endpoints
|
||||
from tests.unit.test_task_questions import setup, finish
|
||||
|
||||
|
||||
def test_question_http_contract_waits_and_resumes_original_task(monkeypatch):
|
||||
service, repo, task_id, command = setup()
|
||||
app = FastAPI()
|
||||
app.include_router(endpoints.router)
|
||||
monkeypatch.setattr(endpoints, "_service", lambda *_args: service)
|
||||
# Exercise HTTP parsing and response contracts; service tests cover authorization.
|
||||
for route in app.routes:
|
||||
if getattr(route, "name", "") in {"create_operational_task_question", "answer_operational_task_question"}:
|
||||
for dependency in route.dependant.dependencies:
|
||||
app.dependency_overrides[dependency.call] = lambda: None
|
||||
with TestClient(app) as client:
|
||||
path = f"/operational-tasks/{task_id}/questions"
|
||||
invalid = client.post(path, json={**command.model_dump(mode="json"), "project_id": str(uuid4())})
|
||||
assert invalid.status_code == 422
|
||||
created = client.post(path, json=command.model_dump(mode="json"))
|
||||
assert created.status_code == 200, created.text
|
||||
question_id = created.json()["question_request_id"]
|
||||
asyncio.run(finish(service, repo, task_id))
|
||||
answered = client.post(f"{path}/{question_id}/answer", json={
|
||||
"request_id": str(uuid4()), "expected_version": 1,
|
||||
"answers": [{"question_id": "flow", "selected_option_ids": ["unknown"], "custom_text": ""}],
|
||||
})
|
||||
assert answered.status_code == 200, answered.text
|
||||
result = answered.json()
|
||||
assert result["task_id"] == str(task_id) and result["execution_generation"] == 2
|
||||
assert result["active_run"]["status"] == "queued"
|
||||
assert result["questions"][0]["status"] == "answered"
|
||||
assert result["questions"][0]["answered_by"] == service._actor()
|
||||
assert result["evidence"] == []
|
||||
Reference in New Issue
Block a user