fix(api): record approval gate timeline events
This commit is contained in:
60
apps/api/tests/test_approval_timeline_event.py
Normal file
60
apps/api/tests/test_approval_timeline_event.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
from src.models.approval import ApprovalStatus, RiskLevel
|
||||
from src.services.approval_db import build_approval_created_timeline_event
|
||||
|
||||
|
||||
def test_pending_approval_timeline_event_exposes_operator_gate() -> None:
|
||||
record = SimpleNamespace(
|
||||
id="approval-pending-1",
|
||||
action="kubectl rollout restart deployment/api",
|
||||
status=ApprovalStatus.PENDING,
|
||||
risk_level=RiskLevel.MEDIUM,
|
||||
required_signatures=1,
|
||||
current_signatures=0,
|
||||
requested_by="openclaw",
|
||||
incident_id="INC-20260604-TEST01",
|
||||
)
|
||||
|
||||
event = build_approval_created_timeline_event(record)
|
||||
|
||||
assert event.event_type == "human"
|
||||
assert event.status == "warning"
|
||||
assert event.title == "Approval gate waiting for human decision"
|
||||
assert event.actor == "openclaw"
|
||||
assert event.actor_role == "approval_gate"
|
||||
assert event.risk_level == "medium"
|
||||
assert event.approval_id == "approval-pending-1"
|
||||
assert event.incident_id == "INC-20260604-TEST01"
|
||||
assert "stage=approval_required" in event.description
|
||||
assert "next_action=operator_approve_or_reject" in event.description
|
||||
assert "blocked_reason=waiting_for_required_signatures" in event.description
|
||||
assert "auto_or_manual=manual" in event.description
|
||||
assert "needs_human=yes" in event.description
|
||||
assert "signatures=0/1" in event.description
|
||||
|
||||
|
||||
def test_low_risk_auto_approved_timeline_event_exposes_auto_gate() -> None:
|
||||
record = SimpleNamespace(
|
||||
id="approval-auto-1",
|
||||
action="collect read-only diagnostics",
|
||||
status=ApprovalStatus.APPROVED,
|
||||
risk_level=RiskLevel.LOW,
|
||||
required_signatures=0,
|
||||
current_signatures=0,
|
||||
requested_by="openclaw",
|
||||
incident_id="INC-20260604-TEST02",
|
||||
)
|
||||
|
||||
event = build_approval_created_timeline_event(record)
|
||||
|
||||
assert event.event_type == "human"
|
||||
assert event.status == "success"
|
||||
assert event.title == "Approval gate passed"
|
||||
assert event.risk_level == "low"
|
||||
assert "stage=approval_auto_approved" in event.description
|
||||
assert "next_action=execute_or_verify" in event.description
|
||||
assert "blocked_reason=none" in event.description
|
||||
assert "auto_or_manual=auto" in event.description
|
||||
assert "needs_human=no" in event.description
|
||||
assert "signatures=0/0" in event.description
|
||||
@@ -421,6 +421,32 @@ def test_reconciliation_blocks_open_incident_after_no_action_approval() -> None:
|
||||
assert "timeline_missing_for_approval" in codes
|
||||
|
||||
|
||||
def test_reconciliation_accepts_approval_with_raw_timeline_event() -> None:
|
||||
reconciliation = build_incident_reconciliation(
|
||||
incident={"incident_id": "INC-1", "status": "INVESTIGATING"},
|
||||
approvals=[
|
||||
{
|
||||
"id": "approval-1",
|
||||
"status": "PENDING",
|
||||
"action": "kubectl rollout restart deployment/api",
|
||||
}
|
||||
],
|
||||
evidence_rows=[],
|
||||
automation_ops=[],
|
||||
timeline_events=[
|
||||
{
|
||||
"event_type": "human",
|
||||
"status": "warning",
|
||||
"approval_id": "approval-1",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
codes = {row["code"] for row in reconciliation["mismatches"]}
|
||||
assert "timeline_missing_for_approval" not in codes
|
||||
assert reconciliation["facts"]["timeline_events"] == 1
|
||||
|
||||
|
||||
def test_reconciliation_counts_auto_repair_execution_as_real_execution() -> None:
|
||||
reconciliation = build_incident_reconciliation(
|
||||
incident={"incident_id": "INC-2", "status": "INVESTIGATING"},
|
||||
|
||||
Reference in New Issue
Block a user