Files
awoooi/apps/api/tests/test_approval_execution_no_action.py
Your Name 16775bb4fa
All checks were successful
CD Pipeline / tests (push) Successful in 1m20s
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / build-and-deploy (push) Successful in 7m44s
CD Pipeline / post-deploy-checks (push) Successful in 2m49s
feat(adr100): bridge playbook authoring approvals
2026-06-01 20:49:28 +08:00

209 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from src.services.approval_execution import ApprovalExecutionService
@pytest.mark.asyncio
async def test_playbook_authoring_record_only_approval_does_not_resolve_incident(monkeypatch):
approval = SimpleNamespace(
id="approval-playbook-authoring-1",
action="PLAYBOOK_AUTHORING_RECORD_ONLY: promote diagnostic playbook",
incident_id="INC-TEST-PLAYBOOK",
metadata={
"approval_kind": "adr100_playbook_authoring",
"execution_kind": "playbook_authoring_record_only",
"execution_authorized": False,
"work_item_id": "verification:INC-TEST-PLAYBOOK:are-1",
"playbook_id": "PB-1",
},
)
incident_service = SimpleNamespace(resolve_incident=AsyncMock())
update_execution_status = AsyncMock()
timeline_add_event = AsyncMock()
alert_completed = AsyncMock(return_value=None)
monkeypatch.setattr(
"src.services.approval_execution.get_approval_service",
lambda: SimpleNamespace(update_execution_status=update_execution_status),
)
monkeypatch.setattr(
"src.services.approval_execution.get_timeline_service",
lambda: SimpleNamespace(add_event=timeline_add_event),
)
monkeypatch.setattr(
"src.services.approval_execution.parse_operation_from_action",
lambda _: SimpleNamespace(
operation_type=None, resource_name=None, namespace=None
),
)
monkeypatch.setattr(
"src.services.incident_service.get_incident_service",
lambda: incident_service,
)
monkeypatch.setattr(
"src.services.approval_execution.ApprovalExecutionService._push_execution_result_to_alert",
AsyncMock(return_value=None),
)
monkeypatch.setattr(
"src.services.approval_execution.ApprovalExecutionService._log_aol_completed",
AsyncMock(return_value=None),
)
monkeypatch.setattr(
"src.services.approval_execution.ApprovalExecutionService._log_alert_execution_started",
AsyncMock(return_value=None),
)
monkeypatch.setattr(
"src.services.approval_execution.ApprovalExecutionService._log_alert_execution_completed",
alert_completed,
)
result = await ApprovalExecutionService().execute_approved_action(approval)
assert result is True
update_execution_status.assert_awaited_once_with(
approval.id,
success=True,
execution_kind="playbook_authoring_record_only",
repair_executed=False,
repair_attempted=False,
)
assert "no runtime repair" in timeline_add_event.await_args.kwargs["title"]
assert alert_completed.await_args.kwargs["execution_kind"] == (
"playbook_authoring_record_only"
)
incident_service.resolve_incident.assert_not_awaited()
@pytest.mark.asyncio
async def test_no_action_execution_resolves_incident_once(monkeypatch):
# Arrange
approval = SimpleNamespace(
id="approval-noaction-1",
action="NO_ACTION: 先做觀察",
incident_id="INC-TEST-001",
)
incident_service = SimpleNamespace(resolve_incident=AsyncMock())
update_execution_status = AsyncMock()
timeline_add_event = AsyncMock()
alert_completed = AsyncMock(return_value=None)
monkeypatch.setattr(
"src.services.approval_execution.get_approval_service",
lambda: SimpleNamespace(update_execution_status=update_execution_status),
)
monkeypatch.setattr(
"src.services.approval_execution.get_timeline_service",
lambda: SimpleNamespace(add_event=timeline_add_event),
)
monkeypatch.setattr(
"src.services.approval_execution.parse_operation_from_action",
lambda _: SimpleNamespace(
operation_type=None, resource_name=None, namespace=None
),
)
monkeypatch.setattr(
"src.services.incident_service.get_incident_service",
lambda: incident_service,
)
monkeypatch.setattr(
"src.services.approval_execution.ApprovalExecutionService._push_execution_result_to_alert",
AsyncMock(return_value=None),
)
monkeypatch.setattr(
"src.services.approval_execution.ApprovalExecutionService._log_aol_completed",
AsyncMock(return_value=None),
)
monkeypatch.setattr(
"src.services.approval_execution.ApprovalExecutionService._log_alert_execution_started",
AsyncMock(return_value=None),
)
monkeypatch.setattr(
"src.services.approval_execution.ApprovalExecutionService._log_alert_execution_completed",
alert_completed,
)
# Act
result = await ApprovalExecutionService().execute_approved_action(approval)
# Assert
assert result is True
update_execution_status.assert_awaited_once_with(
approval.id,
success=True,
execution_kind="no_action",
repair_executed=False,
repair_attempted=False,
)
assert "未執行修復" in timeline_add_event.await_args.kwargs["title"]
assert alert_completed.await_args.kwargs["execution_kind"] == "no_action"
assert alert_completed.await_args.kwargs["output"]["repair_executed"] is False
incident_service.resolve_incident.assert_awaited_once_with("INC-TEST-001")
@pytest.mark.asyncio
async def test_no_action_execution_returns_true_when_resolve_raises(monkeypatch):
"""resolve_incident 拋錯時NO_ACTION 仍須 return True。
契約NO_ACTION 是「純觀察類」成功完成line 207-208 註解明說避免污染
auto_execute KPI。resolve 失敗只該 warning log不該讓 result 退化成 False。
"""
approval = SimpleNamespace(
id="approval-noaction-2",
action="NO_ACTION: 觀察",
incident_id="INC-TEST-002",
)
incident_service = SimpleNamespace(
resolve_incident=AsyncMock(side_effect=RuntimeError("redis down"))
)
update_execution_status = AsyncMock()
monkeypatch.setattr(
"src.services.approval_execution.get_approval_service",
lambda: SimpleNamespace(update_execution_status=update_execution_status),
)
monkeypatch.setattr(
"src.services.approval_execution.get_timeline_service",
lambda: SimpleNamespace(add_event=AsyncMock()),
)
monkeypatch.setattr(
"src.services.approval_execution.parse_operation_from_action",
lambda _: SimpleNamespace(
operation_type=None, resource_name=None, namespace=None
),
)
monkeypatch.setattr(
"src.services.incident_service.get_incident_service",
lambda: incident_service,
)
monkeypatch.setattr(
"src.services.approval_execution.ApprovalExecutionService._push_execution_result_to_alert",
AsyncMock(return_value=None),
)
monkeypatch.setattr(
"src.services.approval_execution.ApprovalExecutionService._log_aol_completed",
AsyncMock(return_value=None),
)
monkeypatch.setattr(
"src.services.approval_execution.ApprovalExecutionService._log_alert_execution_started",
AsyncMock(return_value=None),
)
monkeypatch.setattr(
"src.services.approval_execution.ApprovalExecutionService._log_alert_execution_completed",
AsyncMock(return_value=None),
)
result = await ApprovalExecutionService().execute_approved_action(approval)
assert result is True
update_execution_status.assert_awaited_once_with(
approval.id,
success=True,
execution_kind="no_action",
repair_executed=False,
repair_attempted=False,
)
incident_service.resolve_incident.assert_awaited_once_with("INC-TEST-002")