147 lines
4.7 KiB
Python
147 lines
4.7 KiB
Python
from unittest.mock import MagicMock
|
|
from uuid import uuid4
|
|
|
|
from app.services import scheme_management
|
|
from app.infra.db.project_routing import ActiveProjectRouting, activate_project_routing
|
|
|
|
|
|
def _mock_connection(monkeypatch):
|
|
cursor = MagicMock()
|
|
cursor.rowcount = 1
|
|
connection = MagicMock()
|
|
connection.cursor.return_value.__enter__.return_value = cursor
|
|
context = MagicMock()
|
|
context.__enter__.return_value = connection
|
|
monkeypatch.setattr(scheme_management, "project_connection", lambda _name: context)
|
|
return cursor
|
|
|
|
|
|
def test_repeated_analysis_names_create_distinct_run_ids(monkeypatch) -> None:
|
|
cursor = _mock_connection(monkeypatch)
|
|
arguments = {
|
|
"name": "tjwater_next",
|
|
"scheme_name": "same-window",
|
|
"scheme_type": "burst_analysis",
|
|
"username": "alice",
|
|
"scheme_start_time": "2026-08-24T00:00:00Z",
|
|
"scheme_detail": {},
|
|
}
|
|
|
|
first = scheme_management.create_analysis_run(**arguments)
|
|
second = scheme_management.create_analysis_run(**arguments)
|
|
|
|
assert first != second
|
|
assert cursor.execute.call_count == 2
|
|
assert all(
|
|
"insert into analysis.runs" in call.args[0].lower()
|
|
for call in cursor.execute.call_args_list
|
|
)
|
|
|
|
|
|
def test_update_analysis_run_targets_execution_id(monkeypatch) -> None:
|
|
cursor = _mock_connection(monkeypatch)
|
|
run_id = scheme_management.create_analysis_run(
|
|
name="tjwater_next",
|
|
scheme_name="run",
|
|
scheme_type="burst_analysis",
|
|
username="alice",
|
|
scheme_start_time="2026-08-24T00:00:00Z",
|
|
scheme_detail={},
|
|
)
|
|
cursor.reset_mock()
|
|
|
|
scheme_management.update_analysis_run(
|
|
"tjwater_next",
|
|
run_id,
|
|
status="completed",
|
|
username="alice",
|
|
scheme_detail={"window": "24h"},
|
|
)
|
|
|
|
statement, params = cursor.execute.call_args.args
|
|
assert "where run_id = %s" in statement.lower()
|
|
assert params[-1] == run_id
|
|
assert params[1] == "completed"
|
|
|
|
|
|
def test_run_and_business_result_share_one_transaction(monkeypatch) -> None:
|
|
connection = object()
|
|
context = MagicMock()
|
|
context.__enter__.return_value = connection
|
|
monkeypatch.setattr(
|
|
scheme_management,
|
|
"project_transaction",
|
|
lambda _name: context,
|
|
)
|
|
created_ids = []
|
|
result_ids = []
|
|
monkeypatch.setattr(
|
|
scheme_management.AnalysisRepository,
|
|
"create_run_sync",
|
|
lambda conn, **kwargs: created_ids.append((conn, kwargs["run_id"])),
|
|
)
|
|
monkeypatch.setattr(
|
|
scheme_management.AnalysisRepository,
|
|
"insert_result_sync",
|
|
lambda conn, run_id, **_kwargs: result_ids.append((conn, run_id)),
|
|
)
|
|
|
|
run_id = scheme_management.store_analysis_run_with_result(
|
|
name="tjwater_v2",
|
|
scheme_name="leak-run",
|
|
scheme_type="dma_leak_identification",
|
|
username="alice",
|
|
scheme_start_time="2026-08-24T00:00:00Z",
|
|
scheme_detail={},
|
|
result_type="leakage_identification",
|
|
result_payload={"rows": []},
|
|
)
|
|
|
|
assert created_ids == [(connection, run_id)]
|
|
assert result_ids == [(connection, run_id)]
|
|
|
|
|
|
def test_completed_business_result_emits_one_event_after_result(monkeypatch) -> None:
|
|
connection = object()
|
|
context = MagicMock()
|
|
context.__enter__.return_value = connection
|
|
monkeypatch.setattr(scheme_management, "project_transaction", lambda _name: context)
|
|
calls = []
|
|
monkeypatch.setattr(
|
|
scheme_management.AnalysisRepository,
|
|
"create_run_sync",
|
|
lambda _conn, **_kwargs: calls.append("run"),
|
|
)
|
|
monkeypatch.setattr(
|
|
scheme_management.AnalysisRepository,
|
|
"insert_result_sync",
|
|
lambda _conn, _run_id, **_kwargs: calls.append("result"),
|
|
)
|
|
monkeypatch.setattr(
|
|
scheme_management.AnalysisRepository,
|
|
"append_run_changed_event_sync",
|
|
lambda _conn, **event: calls.append(("event", event)),
|
|
)
|
|
project_id = uuid4()
|
|
routing = ActiveProjectRouting(
|
|
project_code="test", business_dsn="postgresql://test/db", project_id=project_id
|
|
)
|
|
|
|
with activate_project_routing(routing):
|
|
run_id = scheme_management.store_analysis_run_with_result(
|
|
name="test", scheme_name="analysis", scheme_type="dma",
|
|
username="alice", scheme_start_time="2026-09-23T00:00:00Z",
|
|
scheme_detail={}, result_type="summary", result_payload={"rows": []},
|
|
)
|
|
|
|
assert calls[:2] == ["run", "result"]
|
|
assert calls[2][0] == "event"
|
|
assert calls[2][1] == {
|
|
"project_id": project_id,
|
|
"run_id": run_id,
|
|
"revision": 1,
|
|
"status": "completed",
|
|
"occurred_at": calls[2][1]["occurred_at"],
|
|
"results_committed": True,
|
|
}
|