Compare commits

..
Author SHA1 Message Date
jiang d3a4433386 test(replay): cover exploratory simulations without impact maps
Generic Container CI/CD / test-build-publish (push) Successful in 4m10s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 4m10s
2026-11-15 12:44:06 +08:00
@@ -42,27 +42,29 @@ def context(monkeypatch):
return scope, binding return scope, binding
def test_replay_prediction_can_extend_past_cutoff_without_reading_future_observations(monkeypatch): @pytest.mark.parametrize("include_impact", [True, False])
def test_replay_prediction_can_extend_past_cutoff_without_reading_future_observations(monkeypatch, include_impact):
scope, binding = context(monkeypatch) scope, binding = context(monkeypatch)
worker = AsyncMock(return_value={"impact": {"affected_node_count": 2}}) worker = AsyncMock(return_value={"impact": {"affected_node_count": 2}})
monkeypatch.setattr(analysis_service, "calculate_replay_contaminant_simulation", worker) monkeypatch.setattr(analysis_service, "calculate_replay_contaminant_simulation", worker)
result = asyncio.run(query_service.execute_query(scope, binding, TOOLS["run_contaminant_simulation"], args())) parameters = args(include_impact_coverage=include_impact)
result = asyncio.run(query_service.execute_query(scope, binding, TOOLS["run_contaminant_simulation"], parameters))
assert result.origin == "synthetic" and result.data["result"]["impact"]["affected_node_count"] == 2 assert result.origin == "synthetic" and result.data["result"]["impact"]["affected_node_count"] == 2
assert "预测" in result.data_gaps[0] assert "预测" in result.data_gaps[0]
scope.connection.commit.assert_awaited_once() scope.connection.commit.assert_awaited_once()
worker.assert_awaited_once() worker.assert_awaited_once()
assert worker.await_args.args[0].include_impact_coverage is include_impact
assert worker.await_args.kwargs == {"network": "project", "username": "actor"} assert worker.await_args.kwargs == {"network": "project", "username": "actor"}
assert should_queue(TOOLS["run_contaminant_simulation"], args()) assert should_queue(TOOLS["run_contaminant_simulation"], args())
@pytest.mark.parametrize("problem", ["future_start", "no_impact", "reader", "permission"]) @pytest.mark.parametrize("problem", ["future_start", "reader", "permission"])
def test_replay_admission_keeps_time_permission_and_reader_boundaries(monkeypatch, problem): def test_replay_admission_keeps_time_permission_and_reader_boundaries(monkeypatch, problem):
scope, binding = context(monkeypatch) scope, binding = context(monkeypatch)
parameters = args() parameters = args()
if problem == "future_start": parameters = args(start_time="2026-07-16T08:15:00+08:00") if problem == "future_start": parameters = args(start_time="2026-07-16T08:15:00+08:00")
if problem == "no_impact": parameters = args(include_impact_coverage=False)
if problem == "reader": binding.purpose = "task_reader" if problem == "reader": binding.purpose = "task_reader"
if problem == "permission": binding.permission_cap.remove("scada.view") if problem == "permission": binding.permission_cap.remove("scada.view")
with pytest.raises(HTTPException) as caught: with pytest.raises(HTTPException) as caught:
asyncio.run(query_service.authorize_query(scope, binding, TOOLS["run_contaminant_simulation"], parameters)) asyncio.run(query_service.authorize_query(scope, binding, TOOLS["run_contaminant_simulation"], parameters))
assert caught.value.status_code == (422 if problem in {"future_start", "no_impact"} else 403) assert caught.value.status_code == (422 if problem == "future_start" else 403)