fix(km): bound knowledge readbacks under pool pressure
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m19s
CD Pipeline / build-and-deploy (push) Successful in 4m56s
CD Pipeline / post-deploy-checks (push) Has been cancelled
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m19s
CD Pipeline / build-and-deploy (push) Successful in 4m56s
CD Pipeline / post-deploy-checks (push) Has been cancelled
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
from src.models.knowledge import (
|
||||
@@ -122,6 +124,30 @@ async def test_knowledge_list_entries_retries_transient_pool_timeout(monkeypatch
|
||||
assert response.operator_stage == "knowledge_readback_primary_retry_recovered"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_knowledge_list_entries_bounds_primary_timeout_to_source_backed(monkeypatch) -> None:
|
||||
service = KnowledgeService.__new__(KnowledgeService)
|
||||
calls = 0
|
||||
|
||||
async def never_finishes_primary(**_kwargs):
|
||||
nonlocal calls
|
||||
calls += 1
|
||||
await asyncio.Event().wait()
|
||||
|
||||
monkeypatch.setattr(service, "_list_entries_from_primary", never_finishes_primary)
|
||||
monkeypatch.setattr(knowledge_service_module, "_PRIMARY_KM_LIST_TIMEOUT_SECONDS", 0.01)
|
||||
monkeypatch.setattr(knowledge_service_module, "_PRIMARY_KM_LIST_RETRY_TIMEOUT_SECONDS", 0.01)
|
||||
monkeypatch.setattr(knowledge_service_module, "_PRIMARY_KM_RETRY_DELAY_SECONDS", 0)
|
||||
|
||||
response = await service.list_entries(limit=50)
|
||||
|
||||
assert calls == 2
|
||||
assert response.readback_status == "source_backed_degraded"
|
||||
assert response.primary_readback_ready is False
|
||||
assert response.degraded_reason_code == "primary_km_db_timeout_or_pool_exhausted"
|
||||
assert response.total == 13
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_knowledge_list_entries_keeps_primary_items_when_taxonomy_degrades(monkeypatch) -> None:
|
||||
primary_entry = KnowledgeEntry(
|
||||
@@ -247,3 +273,54 @@ async def test_knowledge_asset_taxonomy_fails_soft_when_readback_breaks(monkeypa
|
||||
"schedule",
|
||||
]
|
||||
assert all(row.count >= 1 for row in taxonomy)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_knowledge_side_readbacks_bound_timeouts_to_source_backed(monkeypatch) -> None:
|
||||
service = KnowledgeService.__new__(KnowledgeService)
|
||||
|
||||
async def never_finishes(*_args):
|
||||
await asyncio.Event().wait()
|
||||
|
||||
monkeypatch.setattr(knowledge_service_module, "_PRIMARY_KM_SIDE_READ_TIMEOUT_SECONDS", 0.01)
|
||||
monkeypatch.setattr(service, "_read_primary_categories", never_finishes)
|
||||
monkeypatch.setattr(service, "_read_primary_asset_taxonomy", never_finishes)
|
||||
monkeypatch.setattr(service, "_search_primary_entries", never_finishes)
|
||||
|
||||
categories = await service.get_categories()
|
||||
taxonomy = await service.get_asset_taxonomy()
|
||||
entries = await service.search("Telegram", limit=10)
|
||||
|
||||
assert [row.category for row in categories] == [
|
||||
"project",
|
||||
"product",
|
||||
"website",
|
||||
"service",
|
||||
"package",
|
||||
"tool",
|
||||
"log",
|
||||
"alert",
|
||||
"playbook",
|
||||
"rag",
|
||||
"mcp",
|
||||
"schedule",
|
||||
"general",
|
||||
]
|
||||
assert [row.key for row in taxonomy] == [
|
||||
"project",
|
||||
"product",
|
||||
"website",
|
||||
"service",
|
||||
"package",
|
||||
"tool",
|
||||
"log",
|
||||
"alert",
|
||||
"playbook",
|
||||
"rag",
|
||||
"mcp",
|
||||
"schedule",
|
||||
]
|
||||
assert {entry.id for entry in entries} >= {
|
||||
"source-backed-service-telegram-alert-receipts",
|
||||
"source-backed-alert-telegram-monitoring-coverage",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user