Loading tests/application/test_terminate_service_command_coordinator.py +71 −0 Changes for tests/application/test_terminate_service_command_coordinator.py: 71 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -164,3 +164,74 @@ async def test_handle_republishes_terminal_event_for_terminal_duplicate() -> Non assert subject == "event.srm.operation.completed" assert payload["status"] == "completed" assert payload["service_order_id"] == str(duplicate_order_id) async def test_handle_publishes_failed_event_when_complete_raises() -> None: session_maker = FakeSessionMaker() accepted_order_id = UUID("aaaaaaaa-0000-4000-8000-000000000001") failed_event = SrmOperationCompletedV1( schema_version="1.0", operation_id=UUID("11111111-2222-3333-4444-555555555555"), status=OperationCompletedStatus.FAILED, service_order_id=accepted_order_id, error={"type": "about:blank", "title": "failed", "status": 500}, correlation_id="corr-appinst-001", completed_at=datetime.fromisoformat("2026-06-19T12:01:00+00:00"), ) accept_use_case = AsyncMock() accept_use_case.accept.return_value = TerminateServiceResult( accepted_order_id=accepted_order_id ) complete_use_case = AsyncMock() complete_use_case.complete.side_effect = RuntimeError("adapter timeout") fail_use_case = AsyncMock() fail_use_case.fail_after_accept.return_value = failed_event use_case_factory = MagicMock(side_effect=[accept_use_case, complete_use_case, fail_use_case]) publisher = AsyncMock() coordinator = TerminateServiceCommandCoordinator( session_maker=session_maker, # type: ignore[arg-type] use_case_factory=use_case_factory, publisher=publisher, ) await coordinator.handle(_valid_envelope()) session_maker.contexts[1].session.rollback.assert_awaited_once() session_maker.contexts[2].session.commit.assert_awaited_once() fail_use_case.fail_after_accept.assert_awaited_once() assert publisher.publish.await_count == 2 subject, payload = publisher.publish.await_args_list[1].args assert subject == "event.srm.operation.completed" assert payload["status"] == "failed" async def test_handle_publishes_fallback_failed_event_when_fail_after_accept_raises() -> None: session_maker = FakeSessionMaker() accepted_order_id = UUID("aaaaaaaa-0000-4000-8000-000000000001") accept_use_case = AsyncMock() accept_use_case.accept.return_value = TerminateServiceResult( accepted_order_id=accepted_order_id, target_zone_id=UUID("642f6105-7015-4af1-a4d1-e1ecb8437abc"), ) complete_use_case = AsyncMock() complete_use_case.complete.side_effect = RuntimeError("adapter timeout") fail_use_case = AsyncMock() fail_use_case.fail_after_accept.side_effect = RuntimeError("database down") use_case_factory = MagicMock(side_effect=[accept_use_case, complete_use_case, fail_use_case]) publisher = AsyncMock() coordinator = TerminateServiceCommandCoordinator( session_maker=session_maker, # type: ignore[arg-type] use_case_factory=use_case_factory, publisher=publisher, ) await coordinator.handle(_valid_envelope()) session_maker.contexts[2].session.rollback.assert_awaited_once() assert publisher.publish.await_count == 2 subject, payload = publisher.publish.await_args_list[1].args assert subject == "event.srm.operation.completed" assert payload["status"] == "failed" assert payload["service_order_id"] == str(accepted_order_id) assert payload["instances"][0]["status"] == "failed" assert payload["instances"][0]["error"]["detail"] == "adapter timeout" tests/unit/test_terminate_service_use_case.py +56 −0 Changes for tests/unit/test_terminate_service_use_case.py: 56 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -288,3 +288,59 @@ async def test_accept_replays_completed_event_when_completed_operation_order_exi "status": "completed", } ] async def test_fail_after_accept_marks_order_failed_and_reports_failed_instance() -> None: operation_id = uuid4() service_instance = _service_instance() service_order = _service_order(operation_id, target_service_instance_id=service_instance.id) service_orders = AsyncMock() service_orders.get_by_id.return_value = service_order completed_event = await _use_case( service_orders=service_orders, publisher=AsyncMock(), ).fail_after_accept( _command(operation_id, service_instance_id=service_instance.id), TerminateServiceResult( accepted_order_id=service_order.id, target_zone_id=service_instance.zone_id, ), RuntimeError("adapter timeout"), ) service_orders.update_state.assert_awaited_once() assert service_orders.update_state.await_args.args[:2] == ( service_order.id, ServiceOrderState.FAILED, ) assert service_orders.update_state.await_args.kwargs["failure_detail"]["detail"] == ( "adapter timeout" ) assert completed_event.status == "failed" assert completed_event.error is None assert len(completed_event.instances) == 1 assert completed_event.instances[0]["service_instance_id"] == str(service_instance.id) assert completed_event.instances[0]["status"] == "failed" assert completed_event.instances[0]["error"]["detail"] == "adapter timeout" async def test_fail_after_accept_without_zone_reports_top_level_error() -> None: operation_id = uuid4() service_order = _service_order(operation_id) service_orders = AsyncMock() service_orders.get_by_id.return_value = service_order completed_event = await _use_case( service_orders=service_orders, publisher=AsyncMock(), ).fail_after_accept( _command(operation_id), TerminateServiceResult(accepted_order_id=service_order.id), RuntimeError("adapter timeout"), ) assert completed_event.status == "failed" assert completed_event.instances == [] assert completed_event.error is not None assert completed_event.error["detail"] == "adapter timeout" Loading
tests/application/test_terminate_service_command_coordinator.py +71 −0 Changes for tests/application/test_terminate_service_command_coordinator.py: 71 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -164,3 +164,74 @@ async def test_handle_republishes_terminal_event_for_terminal_duplicate() -> Non assert subject == "event.srm.operation.completed" assert payload["status"] == "completed" assert payload["service_order_id"] == str(duplicate_order_id) async def test_handle_publishes_failed_event_when_complete_raises() -> None: session_maker = FakeSessionMaker() accepted_order_id = UUID("aaaaaaaa-0000-4000-8000-000000000001") failed_event = SrmOperationCompletedV1( schema_version="1.0", operation_id=UUID("11111111-2222-3333-4444-555555555555"), status=OperationCompletedStatus.FAILED, service_order_id=accepted_order_id, error={"type": "about:blank", "title": "failed", "status": 500}, correlation_id="corr-appinst-001", completed_at=datetime.fromisoformat("2026-06-19T12:01:00+00:00"), ) accept_use_case = AsyncMock() accept_use_case.accept.return_value = TerminateServiceResult( accepted_order_id=accepted_order_id ) complete_use_case = AsyncMock() complete_use_case.complete.side_effect = RuntimeError("adapter timeout") fail_use_case = AsyncMock() fail_use_case.fail_after_accept.return_value = failed_event use_case_factory = MagicMock(side_effect=[accept_use_case, complete_use_case, fail_use_case]) publisher = AsyncMock() coordinator = TerminateServiceCommandCoordinator( session_maker=session_maker, # type: ignore[arg-type] use_case_factory=use_case_factory, publisher=publisher, ) await coordinator.handle(_valid_envelope()) session_maker.contexts[1].session.rollback.assert_awaited_once() session_maker.contexts[2].session.commit.assert_awaited_once() fail_use_case.fail_after_accept.assert_awaited_once() assert publisher.publish.await_count == 2 subject, payload = publisher.publish.await_args_list[1].args assert subject == "event.srm.operation.completed" assert payload["status"] == "failed" async def test_handle_publishes_fallback_failed_event_when_fail_after_accept_raises() -> None: session_maker = FakeSessionMaker() accepted_order_id = UUID("aaaaaaaa-0000-4000-8000-000000000001") accept_use_case = AsyncMock() accept_use_case.accept.return_value = TerminateServiceResult( accepted_order_id=accepted_order_id, target_zone_id=UUID("642f6105-7015-4af1-a4d1-e1ecb8437abc"), ) complete_use_case = AsyncMock() complete_use_case.complete.side_effect = RuntimeError("adapter timeout") fail_use_case = AsyncMock() fail_use_case.fail_after_accept.side_effect = RuntimeError("database down") use_case_factory = MagicMock(side_effect=[accept_use_case, complete_use_case, fail_use_case]) publisher = AsyncMock() coordinator = TerminateServiceCommandCoordinator( session_maker=session_maker, # type: ignore[arg-type] use_case_factory=use_case_factory, publisher=publisher, ) await coordinator.handle(_valid_envelope()) session_maker.contexts[2].session.rollback.assert_awaited_once() assert publisher.publish.await_count == 2 subject, payload = publisher.publish.await_args_list[1].args assert subject == "event.srm.operation.completed" assert payload["status"] == "failed" assert payload["service_order_id"] == str(accepted_order_id) assert payload["instances"][0]["status"] == "failed" assert payload["instances"][0]["error"]["detail"] == "adapter timeout"
tests/unit/test_terminate_service_use_case.py +56 −0 Changes for tests/unit/test_terminate_service_use_case.py: 56 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -288,3 +288,59 @@ async def test_accept_replays_completed_event_when_completed_operation_order_exi "status": "completed", } ] async def test_fail_after_accept_marks_order_failed_and_reports_failed_instance() -> None: operation_id = uuid4() service_instance = _service_instance() service_order = _service_order(operation_id, target_service_instance_id=service_instance.id) service_orders = AsyncMock() service_orders.get_by_id.return_value = service_order completed_event = await _use_case( service_orders=service_orders, publisher=AsyncMock(), ).fail_after_accept( _command(operation_id, service_instance_id=service_instance.id), TerminateServiceResult( accepted_order_id=service_order.id, target_zone_id=service_instance.zone_id, ), RuntimeError("adapter timeout"), ) service_orders.update_state.assert_awaited_once() assert service_orders.update_state.await_args.args[:2] == ( service_order.id, ServiceOrderState.FAILED, ) assert service_orders.update_state.await_args.kwargs["failure_detail"]["detail"] == ( "adapter timeout" ) assert completed_event.status == "failed" assert completed_event.error is None assert len(completed_event.instances) == 1 assert completed_event.instances[0]["service_instance_id"] == str(service_instance.id) assert completed_event.instances[0]["status"] == "failed" assert completed_event.instances[0]["error"]["detail"] == "adapter timeout" async def test_fail_after_accept_without_zone_reports_top_level_error() -> None: operation_id = uuid4() service_order = _service_order(operation_id) service_orders = AsyncMock() service_orders.get_by_id.return_value = service_order completed_event = await _use_case( service_orders=service_orders, publisher=AsyncMock(), ).fail_after_accept( _command(operation_id), TerminateServiceResult(accepted_order_id=service_order.id), RuntimeError("adapter timeout"), ) assert completed_event.status == "failed" assert completed_event.instances == [] assert completed_event.error is not None assert completed_event.error["detail"] == "adapter timeout"