Files
syndarix/backend/tests/models/memory/test_models.py
Felipe Cardoso 74b8c65741 fix(tests): move memory model tests to avoid import conflicts
Moved tests/unit/models/memory/ to tests/models/memory/ to avoid
Python import path conflicts when pytest collects all tests.

The conflict was caused by tests/models/ and tests/unit/models/ both
having __init__.py files, causing Python to confuse app.models.memory
imports.

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

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

250 lines
7.8 KiB
Python

# tests/unit/models/memory/test_models.py
"""Unit tests for memory database models."""
from datetime import UTC, datetime, timedelta
import pytest
from app.models.memory import (
ConsolidationStatus,
ConsolidationType,
Episode,
EpisodeOutcome,
Fact,
MemoryConsolidationLog,
Procedure,
ScopeType,
WorkingMemory,
)
class TestWorkingMemoryModel:
"""Tests for WorkingMemory model."""
def test_tablename(self) -> None:
"""Test table name is correct."""
assert WorkingMemory.__tablename__ == "working_memory"
def test_has_required_columns(self) -> None:
"""Test all required columns exist."""
columns = WorkingMemory.__table__.columns
assert "id" in columns
assert "scope_type" in columns
assert "scope_id" in columns
assert "key" in columns
assert "value" in columns
assert "expires_at" in columns
assert "created_at" in columns
assert "updated_at" in columns
def test_has_unique_constraint(self) -> None:
"""Test unique constraint on scope+key."""
indexes = {idx.name: idx for idx in WorkingMemory.__table__.indexes}
assert "ix_working_memory_scope_key" in indexes
assert indexes["ix_working_memory_scope_key"].unique
class TestEpisodeModel:
"""Tests for Episode model."""
def test_tablename(self) -> None:
"""Test table name is correct."""
assert Episode.__tablename__ == "episodes"
def test_has_required_columns(self) -> None:
"""Test all required columns exist."""
columns = Episode.__table__.columns
required = [
"id",
"project_id",
"agent_instance_id",
"agent_type_id",
"session_id",
"task_type",
"task_description",
"actions",
"context_summary",
"outcome",
"outcome_details",
"duration_seconds",
"tokens_used",
"lessons_learned",
"importance_score",
"embedding",
"occurred_at",
"created_at",
"updated_at",
]
for col in required:
assert col in columns, f"Missing column: {col}"
def test_has_foreign_keys(self) -> None:
"""Test foreign key relationships exist."""
columns = Episode.__table__.columns
assert columns["project_id"].foreign_keys
assert columns["agent_instance_id"].foreign_keys
assert columns["agent_type_id"].foreign_keys
def test_has_relationships(self) -> None:
"""Test ORM relationships exist."""
mapper = Episode.__mapper__
assert "project" in mapper.relationships
assert "agent_instance" in mapper.relationships
assert "agent_type" in mapper.relationships
class TestFactModel:
"""Tests for Fact model."""
def test_tablename(self) -> None:
"""Test table name is correct."""
assert Fact.__tablename__ == "facts"
def test_has_required_columns(self) -> None:
"""Test all required columns exist."""
columns = Fact.__table__.columns
required = [
"id",
"project_id",
"subject",
"predicate",
"object",
"confidence",
"source_episode_ids",
"first_learned",
"last_reinforced",
"reinforcement_count",
"embedding",
"created_at",
"updated_at",
]
for col in required:
assert col in columns, f"Missing column: {col}"
def test_project_id_nullable(self) -> None:
"""Test project_id is nullable for global facts."""
columns = Fact.__table__.columns
assert columns["project_id"].nullable
class TestProcedureModel:
"""Tests for Procedure model."""
def test_tablename(self) -> None:
"""Test table name is correct."""
assert Procedure.__tablename__ == "procedures"
def test_has_required_columns(self) -> None:
"""Test all required columns exist."""
columns = Procedure.__table__.columns
required = [
"id",
"project_id",
"agent_type_id",
"name",
"trigger_pattern",
"steps",
"success_count",
"failure_count",
"last_used",
"embedding",
"created_at",
"updated_at",
]
for col in required:
assert col in columns, f"Missing column: {col}"
def test_success_rate_property(self) -> None:
"""Test success_rate calculated property."""
proc = Procedure()
proc.success_count = 8
proc.failure_count = 2
assert proc.success_rate == 0.8
def test_success_rate_zero_total(self) -> None:
"""Test success_rate with zero total uses."""
proc = Procedure()
proc.success_count = 0
proc.failure_count = 0
assert proc.success_rate == 0.0
def test_total_uses_property(self) -> None:
"""Test total_uses calculated property."""
proc = Procedure()
proc.success_count = 5
proc.failure_count = 3
assert proc.total_uses == 8
class TestMemoryConsolidationLogModel:
"""Tests for MemoryConsolidationLog model."""
def test_tablename(self) -> None:
"""Test table name is correct."""
assert MemoryConsolidationLog.__tablename__ == "memory_consolidation_log"
def test_has_required_columns(self) -> None:
"""Test all required columns exist."""
columns = MemoryConsolidationLog.__table__.columns
required = [
"id",
"consolidation_type",
"source_count",
"result_count",
"started_at",
"completed_at",
"status",
"error",
"created_at",
"updated_at",
]
for col in required:
assert col in columns, f"Missing column: {col}"
def test_duration_seconds_property_completed(self) -> None:
"""Test duration_seconds with completed job."""
log = MemoryConsolidationLog()
log.started_at = datetime.now(UTC)
log.completed_at = log.started_at + timedelta(seconds=10)
assert log.duration_seconds == pytest.approx(10.0)
def test_duration_seconds_property_incomplete(self) -> None:
"""Test duration_seconds with incomplete job."""
log = MemoryConsolidationLog()
log.started_at = datetime.now(UTC)
log.completed_at = None
assert log.duration_seconds is None
def test_default_status(self) -> None:
"""Test default status is PENDING."""
columns = MemoryConsolidationLog.__table__.columns
assert columns["status"].default.arg == ConsolidationStatus.PENDING
class TestModelExports:
"""Tests for model package exports."""
def test_all_models_exported(self) -> None:
"""Test all models are exported from package."""
from app.models.memory import (
Episode,
Fact,
MemoryConsolidationLog,
Procedure,
WorkingMemory,
)
# Verify these are the actual classes
assert Episode.__tablename__ == "episodes"
assert Fact.__tablename__ == "facts"
assert Procedure.__tablename__ == "procedures"
assert WorkingMemory.__tablename__ == "working_memory"
assert MemoryConsolidationLog.__tablename__ == "memory_consolidation_log"
def test_enums_exported(self) -> None:
"""Test all enums are exported."""
assert ScopeType.GLOBAL.value == "global"
assert EpisodeOutcome.SUCCESS.value == "success"
assert ConsolidationType.WORKING_TO_EPISODIC.value == "working_to_episodic"
assert ConsolidationStatus.PENDING.value == "pending"