Sweep coordinator (Step 16): - SweepCoordinator runs all 8 agents in parallel with 60s per-agent / 300s total timeout - Aggregates findings, actions, errors into SweepCoordinatorResult - Registered in FastAPI lifespan; triggered via POST /sweep Structured logging (Step 18): - logging_utils/structured.py: JSONFormatter emitting ts/level/logger/msg + custom fields - log_directive_event() for structured directive lifecycle logging - push_to_loki() async Loki push (graceful no-op if LOKI_URL unset) - configure_logging() replaces root handler at startup Tests (Steps 17+19): - conftest.py: mock_odoo, mock_pool, mock_llm fixtures - test_tool_validator.py: 9 tests covering validation, coercion, hallucination stripping - test_llm_router.py: 6 tests covering local/cloud/hybrid modes and HIPAA enforcement - test_peer_bus.py: 6 tests covering registration, timeout, depth, circular detection - test_finance_agent.py: 10 tests covering all 6 steps + sweep + peer request - test_memory_manager.py: 3 tests covering context build + hard cap enforcement - test_dispatch_router.py: 3 tests covering dispatch, rate limit, health endpoint - test_odoo_client.py: 4 tests covering search_read, write result, unlink warning - test_e2e_dispatch.py: 2 E2E tests - full dispatch cycle + peer bus communication README (Step 20): architecture diagram, privacy modes, quick start, env vars, structure Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
70 lines
3.0 KiB
Python
70 lines
3.0 KiB
Python
import pytest
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_conv_store():
|
|
store = MagicMock()
|
|
store.get = AsyncMock(return_value=[
|
|
{'role': 'user', 'content': 'What are my overdue invoices?'},
|
|
{'role': 'assistant', 'content': 'You have 3 overdue invoices.'},
|
|
])
|
|
store.append = AsyncMock()
|
|
store.count = AsyncMock(return_value=2)
|
|
store.prune_old = AsyncMock()
|
|
return store
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_op_store():
|
|
store = MagicMock()
|
|
store.get_recent = AsyncMock(return_value=[])
|
|
store.store = AsyncMock()
|
|
store.prune_expired = AsyncMock()
|
|
return store
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_know_store():
|
|
store = MagicMock()
|
|
store.get_client_profile = AsyncMock(return_value={})
|
|
store.upsert = AsyncMock()
|
|
store.get = AsyncMock(return_value=None)
|
|
return store
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_build_context_returns_master_context(mock_pool, mock_conv_store,
|
|
mock_op_store, mock_know_store):
|
|
with patch('agent_service.memory.memory_manager.ConversationStore', return_value=mock_conv_store), \
|
|
patch('agent_service.memory.memory_manager.OperationalStore', return_value=mock_op_store), \
|
|
patch('agent_service.memory.memory_manager.KnowledgeStore', return_value=mock_know_store):
|
|
from agent_service.memory.memory_manager import MemoryManager
|
|
mgr = MemoryManager(pool=mock_pool, llm=None)
|
|
ctx = await mgr.build_context(user_id='1', intent_hint='finance')
|
|
assert ctx is not None
|
|
assert hasattr(ctx, 'conversation')
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_append_message(mock_pool, mock_conv_store, mock_op_store, mock_know_store):
|
|
with patch('agent_service.memory.memory_manager.ConversationStore', return_value=mock_conv_store), \
|
|
patch('agent_service.memory.memory_manager.OperationalStore', return_value=mock_op_store), \
|
|
patch('agent_service.memory.memory_manager.KnowledgeStore', return_value=mock_know_store):
|
|
from agent_service.memory.memory_manager import MemoryManager
|
|
mgr = MemoryManager(pool=mock_pool, llm=None)
|
|
await mgr.append_message(user_id='1', role='user', content='Hello')
|
|
mock_conv_store.append.assert_awaited_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_hard_cap_enforced(mock_pool, mock_conv_store, mock_op_store, mock_know_store):
|
|
mock_conv_store.count = AsyncMock(return_value=201)
|
|
with patch('agent_service.memory.memory_manager.ConversationStore', return_value=mock_conv_store), \
|
|
patch('agent_service.memory.memory_manager.OperationalStore', return_value=mock_op_store), \
|
|
patch('agent_service.memory.memory_manager.KnowledgeStore', return_value=mock_know_store):
|
|
from agent_service.memory.memory_manager import MemoryManager
|
|
mgr = MemoryManager(pool=mock_pool, llm=None)
|
|
await mgr.append_message(user_id='1', role='user', content='test')
|
|
mock_conv_store.prune_old.assert_awaited()
|