Files
roboco/tests/unit/gateway/test_institutional_memory_timeout.py
T

80 lines
3.0 KiB
Python
Raw Normal View History

"""``_institutional_memory``'s RAG search is bounded by
``institutional_memory_timeout_seconds`` — a saturated Ollama embedder must
never eat the whole verb timeout budget and 504 a claim. See
``roboco.services.gateway.choreographer._impl.Choreographer._institutional_memory``.
"""
from __future__ import annotations
import asyncio
from unittest.mock import AsyncMock, MagicMock
from uuid import uuid4
import pytest
from roboco.config import Settings
from roboco.services.gateway.choreographer import Choreographer
_DEFAULT_TIMEOUT = 8.0
_OVERRIDE_TIMEOUT = 3.5
def _choreographer(*, similar_memory: AsyncMock) -> Choreographer:
repo = AsyncMock()
repo.similar_memory = similar_memory
task_svc = AsyncMock()
task_svc.agent_for.return_value = MagicMock(role="developer")
choreo = object.__new__(Choreographer)
choreo._deps = MagicMock(evidence_repo=repo, task=task_svc)
return choreo
def _task() -> MagicMock:
return MagicMock(
id=uuid4(), title="Add retry backoff", task_type=MagicMock(value="code")
)
class TestInstitutionalMemoryTimeout:
@pytest.mark.asyncio
async def test_slow_search_times_out_without_raising(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setattr("roboco.config.settings.org_memory_enabled", True)
monkeypatch.setattr(
"roboco.config.settings.institutional_memory_timeout_seconds", 0.01
)
async def _slow(**_kwargs: object) -> dict[str, object]:
await asyncio.sleep(5)
return {"items": [], "status": "ok"}
choreo = _choreographer(similar_memory=AsyncMock(side_effect=_slow))
result = await choreo._institutional_memory(uuid4(), _task())
assert result == {"status": "timeout", "lessons": []}
@pytest.mark.asyncio
async def test_fast_search_flows_through_unchanged(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setattr("roboco.config.settings.org_memory_enabled", True)
monkeypatch.setattr(
"roboco.config.settings.institutional_memory_timeout_seconds", 8.0
)
lesson = {"kind": "learning", "summary": "s", "source": "src", "score": 0.9}
similar_memory = AsyncMock(return_value={"items": [lesson], "status": "ok"})
choreo = _choreographer(similar_memory=similar_memory)
result = await choreo._institutional_memory(uuid4(), _task())
assert result == {"status": "ok", "lessons": [lesson]}
class TestInstitutionalMemoryTimeoutConfig:
def test_default_is_eight_seconds(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv("ROBOCO_INSTITUTIONAL_MEMORY_TIMEOUT_SECONDS", raising=False)
assert Settings().institutional_memory_timeout_seconds == _DEFAULT_TIMEOUT
def test_reads_env_override(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv(
"ROBOCO_INSTITUTIONAL_MEMORY_TIMEOUT_SECONDS", str(_OVERRIDE_TIMEOUT)
)
assert Settings().institutional_memory_timeout_seconds == _OVERRIDE_TIMEOUT