Files
syndarix/backend/tests/unit/services/memory/test_exceptions.py
Felipe Cardoso 085a748929 feat(memory): #87 project setup & core architecture
Implements Sub-Issue #87 of Issue #62 (Agent Memory System).

Core infrastructure:
- memory/types.py: Type definitions for all memory types (Working, Episodic,
  Semantic, Procedural) with enums for MemoryType, ScopeLevel, Outcome
- memory/config.py: MemorySettings with MEM_ env prefix, thread-safe singleton
- memory/exceptions.py: Comprehensive exception hierarchy for memory operations
- memory/manager.py: MemoryManager facade with placeholder methods

Directory structure:
- working/: Working memory (Redis/in-memory) - to be implemented in #89
- episodic/: Episodic memory (experiences) - to be implemented in #90
- semantic/: Semantic memory (facts) - to be implemented in #91
- procedural/: Procedural memory (skills) - to be implemented in #92
- scoping/: Scope management - to be implemented in #93
- indexing/: Vector indexing - to be implemented in #94
- consolidation/: Memory consolidation - to be implemented in #95

Tests: 71 unit tests for config, types, and exceptions
Docs: Comprehensive implementation plan at docs/architecture/memory-system-plan.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 01:27:36 +01:00

326 lines
9.5 KiB
Python

"""
Tests for Memory System Exceptions.
"""
from uuid import uuid4
import pytest
from app.services.memory.exceptions import (
CheckpointError,
EmbeddingError,
MemoryCapacityError,
MemoryConflictError,
MemoryConsolidationError,
MemoryError,
MemoryExpiredError,
MemoryNotFoundError,
MemoryRetrievalError,
MemoryScopeError,
MemorySerializationError,
MemoryStorageError,
)
class TestMemoryError:
"""Tests for base MemoryError class."""
def test_basic_error(self) -> None:
"""Test creating a basic memory error."""
error = MemoryError("Something went wrong")
assert str(error) == "Something went wrong"
assert error.message == "Something went wrong"
assert error.memory_type is None
assert error.scope_type is None
assert error.scope_id is None
assert error.details == {}
def test_error_with_context(self) -> None:
"""Test creating an error with context."""
error = MemoryError(
"Operation failed",
memory_type="episodic",
scope_type="project",
scope_id="proj-123",
details={"operation": "search"},
)
assert error.memory_type == "episodic"
assert error.scope_type == "project"
assert error.scope_id == "proj-123"
assert error.details == {"operation": "search"}
def test_error_inheritance(self) -> None:
"""Test that MemoryError inherits from Exception."""
error = MemoryError("test")
assert isinstance(error, Exception)
class TestMemoryNotFoundError:
"""Tests for MemoryNotFoundError class."""
def test_default_message(self) -> None:
"""Test default error message."""
error = MemoryNotFoundError()
assert error.message == "Memory not found"
def test_with_memory_id(self) -> None:
"""Test error with memory ID."""
memory_id = uuid4()
error = MemoryNotFoundError(
f"Memory {memory_id} not found",
memory_id=memory_id,
)
assert error.memory_id == memory_id
def test_with_key(self) -> None:
"""Test error with key."""
error = MemoryNotFoundError(
"Key not found",
key="my_key",
)
assert error.key == "my_key"
class TestMemoryCapacityError:
"""Tests for MemoryCapacityError class."""
def test_default_message(self) -> None:
"""Test default error message."""
error = MemoryCapacityError()
assert error.message == "Memory capacity exceeded"
def test_with_sizes(self) -> None:
"""Test error with size information."""
error = MemoryCapacityError(
"Working memory full",
current_size=1048576,
max_size=1000000,
item_count=500,
)
assert error.current_size == 1048576
assert error.max_size == 1000000
assert error.item_count == 500
class TestMemoryExpiredError:
"""Tests for MemoryExpiredError class."""
def test_default_message(self) -> None:
"""Test default error message."""
error = MemoryExpiredError()
assert error.message == "Memory has expired"
def test_with_expiry_info(self) -> None:
"""Test error with expiry information."""
error = MemoryExpiredError(
"Key expired",
key="session_data",
expired_at="2025-01-05T00:00:00Z",
)
assert error.key == "session_data"
assert error.expired_at == "2025-01-05T00:00:00Z"
class TestMemoryStorageError:
"""Tests for MemoryStorageError class."""
def test_default_message(self) -> None:
"""Test default error message."""
error = MemoryStorageError()
assert error.message == "Memory storage operation failed"
def test_with_operation_info(self) -> None:
"""Test error with operation information."""
error = MemoryStorageError(
"Redis write failed",
operation="set",
backend="redis",
)
assert error.operation == "set"
assert error.backend == "redis"
class TestMemorySerializationError:
"""Tests for MemorySerializationError class."""
def test_default_message(self) -> None:
"""Test default error message."""
error = MemorySerializationError()
assert error.message == "Memory serialization failed"
def test_with_content_type(self) -> None:
"""Test error with content type."""
error = MemorySerializationError(
"Cannot serialize function",
content_type="function",
)
assert error.content_type == "function"
class TestMemoryScopeError:
"""Tests for MemoryScopeError class."""
def test_default_message(self) -> None:
"""Test default error message."""
error = MemoryScopeError()
assert error.message == "Memory scope error"
def test_with_scope_info(self) -> None:
"""Test error with scope information."""
error = MemoryScopeError(
"Scope access denied",
requested_scope="global",
allowed_scopes=["project", "session"],
)
assert error.requested_scope == "global"
assert error.allowed_scopes == ["project", "session"]
class TestMemoryConsolidationError:
"""Tests for MemoryConsolidationError class."""
def test_default_message(self) -> None:
"""Test default error message."""
error = MemoryConsolidationError()
assert error.message == "Memory consolidation failed"
def test_with_consolidation_info(self) -> None:
"""Test error with consolidation information."""
error = MemoryConsolidationError(
"Transfer failed",
source_type="working",
target_type="episodic",
items_processed=50,
)
assert error.source_type == "working"
assert error.target_type == "episodic"
assert error.items_processed == 50
class TestMemoryRetrievalError:
"""Tests for MemoryRetrievalError class."""
def test_default_message(self) -> None:
"""Test default error message."""
error = MemoryRetrievalError()
assert error.message == "Memory retrieval failed"
def test_with_query_info(self) -> None:
"""Test error with query information."""
error = MemoryRetrievalError(
"Search timeout",
query="complex search query",
retrieval_type="semantic",
)
assert error.query == "complex search query"
assert error.retrieval_type == "semantic"
class TestEmbeddingError:
"""Tests for EmbeddingError class."""
def test_default_message(self) -> None:
"""Test default error message."""
error = EmbeddingError()
assert error.message == "Embedding generation failed"
def test_with_embedding_info(self) -> None:
"""Test error with embedding information."""
error = EmbeddingError(
"Content too long",
content_length=100000,
model="text-embedding-3-small",
)
assert error.content_length == 100000
assert error.model == "text-embedding-3-small"
class TestCheckpointError:
"""Tests for CheckpointError class."""
def test_default_message(self) -> None:
"""Test default error message."""
error = CheckpointError()
assert error.message == "Checkpoint operation failed"
def test_with_checkpoint_info(self) -> None:
"""Test error with checkpoint information."""
error = CheckpointError(
"Restore failed",
checkpoint_id="chk-123",
operation="restore",
)
assert error.checkpoint_id == "chk-123"
assert error.operation == "restore"
class TestMemoryConflictError:
"""Tests for MemoryConflictError class."""
def test_default_message(self) -> None:
"""Test default error message."""
error = MemoryConflictError()
assert error.message == "Memory conflict detected"
def test_with_conflict_info(self) -> None:
"""Test error with conflict information."""
id1 = uuid4()
id2 = uuid4()
error = MemoryConflictError(
"Contradictory facts detected",
conflicting_ids=[id1, id2],
conflict_type="semantic",
)
assert len(error.conflicting_ids) == 2
assert error.conflict_type == "semantic"
class TestExceptionHierarchy:
"""Tests for exception inheritance hierarchy."""
def test_all_exceptions_inherit_from_memory_error(self) -> None:
"""Test that all exceptions inherit from MemoryError."""
exceptions = [
MemoryNotFoundError(),
MemoryCapacityError(),
MemoryExpiredError(),
MemoryStorageError(),
MemorySerializationError(),
MemoryScopeError(),
MemoryConsolidationError(),
MemoryRetrievalError(),
EmbeddingError(),
CheckpointError(),
MemoryConflictError(),
]
for exc in exceptions:
assert isinstance(exc, MemoryError)
assert isinstance(exc, Exception)
def test_can_catch_base_error(self) -> None:
"""Test that catching MemoryError catches all subclasses."""
exceptions = [
MemoryNotFoundError("not found"),
MemoryCapacityError("capacity"),
MemoryStorageError("storage"),
]
for exc in exceptions:
with pytest.raises(MemoryError):
raise exc