feat(awooop): search callback reply evidence
All checks were successful
Code Review / ai-code-review (push) Successful in 10s
CD Pipeline / tests (push) Successful in 1m8s
CD Pipeline / build-and-deploy (push) Successful in 3m44s
CD Pipeline / post-deploy-checks (push) Successful in 1m31s

This commit is contained in:
Your Name
2026-05-18 16:17:05 +08:00
parent e4e1244c0f
commit 08a75f4b5a
6 changed files with 548 additions and 7 deletions

View File

@@ -6,10 +6,14 @@ from uuid import UUID
import pytest
from fastapi import HTTPException
from src.api.v1.platform.operator_runs import ListRunsResponse
from src.api.v1.platform.operator_runs import (
ListCallbackRepliesResponse,
ListRunsResponse,
)
from src.services.platform_operator_service import (
_callback_reply_summary_matches_status,
_collect_run_incident_ids,
_callback_reply_event_item,
_legacy_mcp_timeline_status,
_legacy_mcp_timeline_summary,
_list_filter_context_limit,
@@ -22,6 +26,7 @@ from src.services.platform_operator_service import (
_run_callback_reply_summary,
_run_remediation_list_summary,
_timeline_sort_key,
_validate_callback_reply_action_filter,
_validate_callback_reply_status_filter,
)
@@ -276,6 +281,103 @@ def test_list_runs_response_preserves_callback_reply_summary() -> None:
assert dumped["runs"][0]["callback_reply_summary"]["needs_human"] is True
def test_callback_reply_event_item_surfaces_run_link_and_human_flag() -> None:
run_id = UUID("5c0306e0-591a-5445-9a33-80f499426b38")
message_id = UUID("56cdb6ad-46a4-48f5-9d3b-b1ac9c0b2e92")
item = _callback_reply_event_item({
"message_id": message_id,
"run_id": run_id,
"project_id": "awoooi",
"channel_type": "telegram",
"message_type": "error",
"send_status": "failed",
"send_error": "HTTP error: 400",
"provider_message_id": "telegram_callback_reply:failed",
"queued_at": datetime(2026, 5, 18, 7, 31, 37),
"sent_at": None,
"triggered_by_state": "callback_reply",
"content_preview": "無法取得歷史統計",
"run_state": "completed",
"agent_id": "legacy-telegram-gateway",
"run_created_at": datetime(2026, 5, 18, 7, 30, 0),
"callback_reply": {
"status": "callback_reply_failed",
"action": "history",
"incident_id": "INC-20260513-79ED5E",
"error": "HTTP error: 400",
},
})
assert item["status"] == "failed"
assert item["needs_human"] is True
assert item["action"] == "history"
assert item["incident_id"] == "INC-20260513-79ED5E"
assert item["event_at"] == datetime(2026, 5, 18, 7, 31, 37)
assert item["run_detail_href"] == (
"/awooop/runs/5c0306e0-591a-5445-9a33-80f499426b38?project_id=awoooi"
)
def test_list_callback_replies_response_preserves_callback_evidence() -> None:
run_id = UUID("5c0306e0-591a-5445-9a33-80f499426b38")
message_id = UUID("56cdb6ad-46a4-48f5-9d3b-b1ac9c0b2e92")
response = ListCallbackRepliesResponse.model_validate({
"items": [
{
"message_id": message_id,
"run_id": run_id,
"project_id": "awoooi",
"status": "fallback_sent",
"needs_human": False,
"action": "detail",
"incident_id": "INC-20260513-79ED5E",
"event_at": datetime(2026, 5, 18, 7, 31, 37),
"channel_type": "telegram",
"message_type": "final",
"send_status": "sent",
"send_error": None,
"provider_message_id": "123",
"triggered_by_state": "callback_reply",
"content_preview": "事件詳情",
"run_state": "completed",
"agent_id": "legacy-telegram-gateway",
"run_created_at": datetime(2026, 5, 18, 7, 30, 0),
"callback_reply": {
"status": "callback_reply_fallback_sent",
"action": "detail",
"incident_id": "INC-20260513-79ED5E",
},
"run_detail_href": (
"/awooop/runs/5c0306e0-591a-5445-9a33-80f499426b38"
"?project_id=awoooi"
),
}
],
"total": 1,
"page": 1,
"per_page": 20,
})
dumped = response.model_dump(mode="json")
assert dumped["items"][0]["status"] == "fallback_sent"
assert dumped["items"][0]["callback_reply"]["action"] == "detail"
assert dumped["items"][0]["run_detail_href"].endswith("project_id=awoooi")
def test_callback_reply_action_filter_normalizes_safe_actions() -> None:
assert _validate_callback_reply_action_filter(" History ") == "history"
assert _validate_callback_reply_action_filter("incident:detail-2") == (
"incident:detail-2"
)
assert _validate_callback_reply_action_filter("") is None
def test_callback_reply_action_filter_rejects_unsafe_values() -> None:
with pytest.raises(HTTPException):
_validate_callback_reply_action_filter("detail;drop")
def test_remediation_timeline_summary_surfaces_route_and_write_flags() -> None:
summary = _remediation_timeline_summary({
"incident_id": "INC-20260514-F85F21",