fix(governance): fail soft work-items readbacks
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m14s
CD Pipeline / build-and-deploy (push) Successful in 4m45s
CD Pipeline / post-deploy-checks (push) Successful in 1m55s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m14s
CD Pipeline / build-and-deploy (push) Successful in 4m45s
CD Pipeline / post-deploy-checks (push) Successful in 1m55s
This commit is contained in:
@@ -65,6 +65,7 @@ from src.services.governance_query_service import (
|
||||
query_km_review_draft_dedupe,
|
||||
query_km_stale_candidates,
|
||||
)
|
||||
from src.utils.timezone import now_taipei
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -164,12 +165,29 @@ async def get_governance_queue(
|
||||
page=page,
|
||||
size=size,
|
||||
)
|
||||
return await query_governance_queue(
|
||||
dispatch_status=dispatch_status,
|
||||
event_types=event_type,
|
||||
page=page,
|
||||
size=size,
|
||||
)
|
||||
try:
|
||||
return await query_governance_queue(
|
||||
dispatch_status=dispatch_status,
|
||||
event_types=event_type,
|
||||
page=page,
|
||||
size=size,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"governance_queue_readback_degraded",
|
||||
dispatch_status=dispatch_status,
|
||||
event_type=event_type,
|
||||
page=page,
|
||||
size=size,
|
||||
error_type=type(exc).__name__,
|
||||
)
|
||||
return GovernanceQueueResponse(
|
||||
items=[],
|
||||
total=0,
|
||||
page=page,
|
||||
size=size,
|
||||
table_pending=True,
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -190,7 +208,21 @@ async def get_km_review_draft_dedupe(
|
||||
owner_action,不自動 archive、不自動 approve/publish KM。
|
||||
"""
|
||||
logger.debug("km_review_draft_dedupe_request", limit=limit)
|
||||
return await query_km_review_draft_dedupe(limit=limit)
|
||||
try:
|
||||
return await query_km_review_draft_dedupe(limit=limit)
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"km_review_draft_dedupe_readback_degraded",
|
||||
limit=limit,
|
||||
error_type=type(exc).__name__,
|
||||
)
|
||||
return KnowledgeReviewDraftDedupeResponse(
|
||||
total_review_drafts=0,
|
||||
event_group_total=0,
|
||||
duplicate_draft_total=0,
|
||||
groups=[],
|
||||
generated_at=now_taipei(),
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
||||
@@ -401,6 +401,25 @@ class TestQueueEndpoint:
|
||||
assert len(data["items"]) == 1
|
||||
assert data["items"][0]["dispatch_status"] == "pending"
|
||||
|
||||
def test_readback_exception_returns_degraded_queue(self, client):
|
||||
"""Work Items 背景讀取失敗時應 fail-soft,不讓前台收到 500."""
|
||||
with patch(
|
||||
"src.api.v1.ai_governance.query_governance_queue",
|
||||
new=AsyncMock(side_effect=RuntimeError("temporary readback failure")),
|
||||
):
|
||||
r = client.get(
|
||||
"/api/v1/ai/governance/queue?dispatch_status=all"
|
||||
"&event_type=knowledge_degradation&size=20"
|
||||
)
|
||||
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert data["items"] == []
|
||||
assert data["total"] == 0
|
||||
assert data["page"] == 1
|
||||
assert data["size"] == 20
|
||||
assert data["table_pending"] is True
|
||||
|
||||
def test_invalid_dispatch_status_rejected(self, client):
|
||||
"""非法 dispatch_status 應被拒絕(422)."""
|
||||
r = client.get("/api/v1/ai/governance/queue?dispatch_status=unknown")
|
||||
@@ -579,6 +598,23 @@ class TestKmReviewDraftDedupe:
|
||||
)
|
||||
assert data["groups"][0]["archive_history"][0]["archived_count"] == 2
|
||||
|
||||
def test_endpoint_readback_exception_returns_empty_plan(self, client):
|
||||
"""KM dedupe 背景讀取失敗時應 fail-soft,不讓 Work Items 收到 500."""
|
||||
with patch(
|
||||
"src.api.v1.ai_governance.query_km_review_draft_dedupe",
|
||||
new=AsyncMock(side_effect=RuntimeError("temporary dedupe read failure")),
|
||||
):
|
||||
r = client.get("/api/v1/ai/governance/km-review-drafts/dedupe?limit=100")
|
||||
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert data["schema_version"] == "km_review_draft_dedupe_v1"
|
||||
assert data["total_review_drafts"] == 0
|
||||
assert data["event_group_total"] == 0
|
||||
assert data["duplicate_draft_total"] == 0
|
||||
assert data["groups"] == []
|
||||
assert data["generated_at"]
|
||||
|
||||
def test_governance_event_tag_extraction(self):
|
||||
assert _extract_governance_event_id_from_tags([
|
||||
"agent:Hermes",
|
||||
|
||||
Reference in New Issue
Block a user