Files
tatlock/tests/core/test_memory_service.py
T
jpmschweitzerandClaude 78066fab1b style: apply ruff's automatic fixes and formatter
Mechanical only, and separated from the judgment calls that follow so the
reviewable changes are not buried in a 98-file whitespace diff.

227 automatic fixes: 60 blank lines carrying whitespace, 60 unsorted import
blocks, 34 Optional[X] to X | None, 28 unused imports, 16 deprecated typing
imports, 12 datetime.timezone.utc to datetime.UTC, and assorted smaller
modernisations. Then `ruff format` over src and tests: 98 files reformatted,
35 already conforming.

No file among the unused-import findings defines __all__ or is an __init__.py,
so nothing here removes a re-export.

`make test`: 658 passed, unchanged from HEAD.

Two things observed while verifying, neither addressed here:

`pytest tests/` cannot collect — tests/e2e/test_orchestration_e2e.py uses an
`e2e` marker that is not registered, and the config is strict about markers.
This fails identically at HEAD, so it predates this change; `make test` passes
because it ignores tests/e2e, tests/integration and tests/contracts.

test_tatlock_tool_call_logging_calculator is flaky. It failed once in a full run
with these changes and passed on the next, passes in isolation with them, and
fails in isolation at HEAD. It is order- or timing-dependent, not a regression
from this commit — established by running the full suite both ways rather than
by reasoning about which change could have caused it.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-11 17:25:18 +02:00

281 lines
10 KiB
Python

"""
Tests for the memory service (direct access layer).
"""
from unittest.mock import AsyncMock, patch
import pytest
from src.core.memory_service import (
MemoryRecord,
MemoryService,
MemoryType,
memory_service,
)
@pytest.mark.unit
class TestMemoryType:
"""Tests for MemoryType enum."""
def test_user_profile_type(self):
"""Test user_profile type exists."""
assert MemoryType.USER_PROFILE.value == "user_profile"
def test_preference_type(self):
"""Test preference type exists."""
assert MemoryType.PREFERENCE.value == "preference"
def test_learned_fact_type(self):
"""Test learned_fact type exists."""
assert MemoryType.LEARNED_FACT.value == "learned_fact"
@pytest.mark.unit
class TestMemoryRecord:
"""Tests for MemoryRecord model."""
def test_create_minimal_record(self):
"""Test creating record with minimal fields."""
record = MemoryRecord(
id="test_1",
type=MemoryType.USER_PROFILE,
key="location",
value="Amsterdam",
)
assert record.id == "test_1"
assert record.type == MemoryType.USER_PROFILE
assert record.key == "location"
assert record.value == "Amsterdam"
assert record.importance == 0.5 # Default
assert record.source == "explicit" # Default
def test_create_full_record(self):
"""Test creating record with all fields."""
record = MemoryRecord(
id="test_2",
type=MemoryType.LEARNED_FACT,
key="car",
value="Tesla Model 3",
keywords=["car", "vehicle", "tesla"],
importance=0.8,
source="conversation",
)
assert record.keywords == ["car", "vehicle", "tesla"]
assert record.importance == 0.8
assert record.source == "conversation"
@pytest.mark.unit
class TestMemoryServiceInit:
"""Tests for MemoryService initialization."""
def test_service_has_lazy_clients(self):
"""Test service initializes with lazy client loading."""
service = MemoryService()
assert service._qdrant is None
assert service._embedding is None
assert service._cache is None
def test_global_instance_exists(self):
"""Test global memory_service instance exists."""
assert memory_service is not None
assert isinstance(memory_service, MemoryService)
@pytest.mark.unit
class TestMemoryServiceProfileMethods:
"""Tests for profile-related methods."""
@pytest.mark.asyncio
async def test_get_profile_uses_context(self):
"""Test get_profile uses request context for user."""
service = MemoryService()
with patch.object(service, "_get_memory", new_callable=AsyncMock) as mock_get:
mock_get.return_value = "Amsterdam"
with patch("src.core.memory_service.get_user", return_value="testuser"):
result = await service.get_profile("location")
mock_get.assert_called_once_with("testuser", MemoryType.USER_PROFILE, "location")
assert result == "Amsterdam"
@pytest.mark.asyncio
async def test_get_profile_explicit_user(self):
"""Test get_profile with explicit user parameter."""
service = MemoryService()
with patch.object(service, "_get_memory", new_callable=AsyncMock) as mock_get:
mock_get.return_value = "Berlin"
result = await service.get_profile("location", user="otheruser")
mock_get.assert_called_once_with("otheruser", MemoryType.USER_PROFILE, "location")
assert result == "Berlin"
@pytest.mark.asyncio
async def test_set_profile_high_importance(self):
"""Test set_profile uses high importance (0.9)."""
service = MemoryService()
with patch.object(service, "_set_memory", new_callable=AsyncMock) as mock_set:
mock_set.return_value = True
with patch("src.core.memory_service.get_user", return_value="testuser"):
result = await service.set_profile("timezone", "Europe/Amsterdam")
call_kwargs = mock_set.call_args[1]
assert call_kwargs["importance"] == 0.9
assert result is True
@pytest.mark.unit
class TestMemoryServicePreferenceMethods:
"""Tests for preference-related methods."""
@pytest.mark.asyncio
async def test_get_preference(self):
"""Test get_preference retrieves correctly."""
service = MemoryService()
with patch.object(service, "_get_memory", new_callable=AsyncMock) as mock_get:
mock_get.return_value = "celsius"
with patch("src.core.memory_service.get_user", return_value="testuser"):
result = await service.get_preference("temperature_unit")
mock_get.assert_called_once_with("testuser", MemoryType.PREFERENCE, "temperature_unit")
assert result == "celsius"
@pytest.mark.asyncio
async def test_set_preference_medium_importance(self):
"""Test set_preference uses medium importance (0.7)."""
service = MemoryService()
with patch.object(service, "_set_memory", new_callable=AsyncMock) as mock_set:
mock_set.return_value = True
with patch("src.core.memory_service.get_user", return_value="testuser"):
result = await service.set_preference("theme", "dark")
call_kwargs = mock_set.call_args[1]
assert call_kwargs["importance"] == 0.7
@pytest.mark.unit
class TestMemoryServiceFactMethods:
"""Tests for fact-related methods."""
@pytest.mark.asyncio
async def test_store_fact_default_importance(self):
"""Test store_fact uses default importance (0.5)."""
service = MemoryService()
with patch.object(service, "_set_memory", new_callable=AsyncMock) as mock_set:
mock_set.return_value = True
with patch("src.core.memory_service.get_user", return_value="testuser"):
result = await service.store_fact("car", "Tesla Model 3")
call_kwargs = mock_set.call_args[1]
assert call_kwargs["importance"] == 0.5
@pytest.mark.asyncio
async def test_store_fact_custom_importance(self):
"""Test store_fact with custom importance."""
service = MemoryService()
with patch.object(service, "_set_memory", new_callable=AsyncMock) as mock_set:
mock_set.return_value = True
with patch("src.core.memory_service.get_user", return_value="testuser"):
result = await service.store_fact(
"employer",
"Acme Corp",
importance=0.8,
)
call_kwargs = mock_set.call_args[1]
assert call_kwargs["importance"] == 0.8
@pytest.mark.asyncio
async def test_get_fact(self):
"""Test get_fact retrieves correctly."""
service = MemoryService()
with patch.object(service, "_get_memory", new_callable=AsyncMock) as mock_get:
mock_get.return_value = "Tesla Model 3"
with patch("src.core.memory_service.get_user", return_value="testuser"):
result = await service.get_fact("car")
mock_get.assert_called_once_with("testuser", MemoryType.LEARNED_FACT, "car")
assert result == "Tesla Model 3"
@pytest.mark.unit
class TestMemoryServicePrefetch:
"""Tests for prefetch_context method."""
@pytest.mark.asyncio
async def test_prefetch_default_keys(self):
"""Test prefetch with default profile keys."""
service = MemoryService()
with patch.object(service, "get_profile", new_callable=AsyncMock) as mock_profile:
with patch.object(service, "get_all_preferences", new_callable=AsyncMock) as mock_prefs:
mock_profile.side_effect = [
"Amsterdam", # location
"Europe/Amsterdam", # timezone
"John", # name
]
mock_prefs.return_value = {"temperature_unit": "celsius"}
with patch("src.core.memory_service.get_user", return_value="testuser"):
result = await service.prefetch_context()
assert result["profile"]["location"] == "Amsterdam"
assert result["profile"]["timezone"] == "Europe/Amsterdam"
assert result["profile"]["name"] == "John"
assert result["preferences"]["temperature_unit"] == "celsius"
@pytest.mark.asyncio
async def test_prefetch_specific_keys(self):
"""Test prefetch with specific profile keys."""
service = MemoryService()
with patch.object(service, "get_profile", new_callable=AsyncMock) as mock_profile:
with patch.object(service, "get_all_preferences", new_callable=AsyncMock) as mock_prefs:
mock_profile.return_value = "Amsterdam"
mock_prefs.return_value = {}
with patch("src.core.memory_service.get_user", return_value="testuser"):
result = await service.prefetch_context(
profile_keys=["location"],
include_preferences=False,
)
# Should only fetch location
mock_profile.assert_called_once()
mock_prefs.assert_not_called()
@pytest.mark.asyncio
async def test_prefetch_no_profile(self):
"""Test prefetch without profile data."""
service = MemoryService()
with patch.object(service, "get_profile", new_callable=AsyncMock) as mock_profile:
with patch.object(service, "get_all_preferences", new_callable=AsyncMock) as mock_prefs:
mock_prefs.return_value = {"theme": "dark"}
with patch("src.core.memory_service.get_user", return_value="testuser"):
result = await service.prefetch_context(include_profile=False)
mock_profile.assert_not_called()
assert "profile" not in result
assert result["preferences"]["theme"] == "dark"