""" Tests for MemoryReinforcement (M.2) Tests: trigger(), auto_reinforce(). """ import pytest from datetime import UTC, datetime, timedelta from unittest.mock import MagicMock from app.services.memory.reinforcement import MemoryReinforcement def create_mock_memory( frequency_count: int = 0, last_accessed_at=None, last_recalled_at=None, decay_score: float = 1.0, importance_level: str = "medium", ): """Create a mock UserMemory for testing.""" memory = MagicMock() memory.frequency_count = frequency_count memory.last_accessed_at = last_accessed_at memory.last_recalled_at = last_recalled_at memory.decay_score = decay_score memory.importance_level = importance_level return memory class TestMemoryReinforcementTrigger: """Test trigger() method - called on memory recall.""" def test_trigger_increments_frequency(self): """trigger() increments frequency_count by 1.""" reinforcement = MemoryReinforcement() memory = create_mock_memory(frequency_count=5) result = reinforcement.trigger(memory) assert result.frequency_count == 6 def test_trigger_frequency_capped_at_max(self): """trigger() caps frequency_count at MAX_FREQUENCY (10).""" reinforcement = MemoryReinforcement() memory = create_mock_memory(frequency_count=10) result = reinforcement.trigger(memory) assert result.frequency_count == 10 def test_trigger_updates_last_accessed_at(self): """trigger() updates last_accessed_at to now.""" reinforcement = MemoryReinforcement() old_time = datetime.now(UTC) - timedelta(days=10) memory = create_mock_memory(last_accessed_at=old_time) before = datetime.now(UTC) result = reinforcement.trigger(memory) after = datetime.now(UTC) assert before <= result.last_accessed_at <= after def test_trigger_updates_last_recalled_at(self): """trigger() updates last_recalled_at to now.""" reinforcement = MemoryReinforcement() memory = create_mock_memory() before = datetime.now(UTC) result = reinforcement.trigger(memory) after = datetime.now(UTC) assert before <= result.last_recalled_at <= after def test_trigger_boosts_decay_score(self): """trigger() boosts decay_score by 0.1 (capped at 0.95).""" reinforcement = MemoryReinforcement() memory = create_mock_memory(decay_score=0.5) result = reinforcement.trigger(memory) assert result.decay_score > 0.5 assert result.decay_score <= 0.95 def test_trigger_decay_score_capped_at_095(self): """trigger() decay_score boost is capped at 0.95.""" reinforcement = MemoryReinforcement() memory = create_mock_memory(decay_score=0.95) result = reinforcement.trigger(memory) assert result.decay_score == 0.95 def test_trigger_from_zero_frequency(self): """trigger() works from frequency_count = 0.""" reinforcement = MemoryReinforcement() memory = create_mock_memory(frequency_count=0) result = reinforcement.trigger(memory) assert result.frequency_count == 1 def test_trigger_returns_same_memory_object(self): """trigger() returns the same memory object (modified in place).""" reinforcement = MemoryReinforcement() memory = create_mock_memory() result = reinforcement.trigger(memory) assert result is memory class TestMemoryReinforcementAutoReinforce: """Test auto_reinforce() method - weekly maintenance for high-importance memories.""" def test_auto_reinforce_skips_non_high_importance(self): """auto_reinforce() skips memories that are not high importance.""" reinforcement = MemoryReinforcement() memory_low = create_mock_memory(importance_level="low", frequency_count=5) memory_medium = create_mock_memory(importance_level="medium", frequency_count=5) result = reinforcement.auto_reinforce([memory_low, memory_medium]) assert len(result) == 0 def test_auto_reinforce_includes_high_importance(self): """auto_reinforce() includes high-importance memories.""" reinforcement = MemoryReinforcement() memory_high = create_mock_memory(importance_level="high", frequency_count=5) result = reinforcement.auto_reinforce([memory_high]) assert len(result) == 1 assert result[0] is memory_high def test_auto_reinforce_skips_max_frequency(self): """auto_reinforce() skips high-importance memories already at MAX_FREQUENCY.""" reinforcement = MemoryReinforcement() memory = create_mock_memory(importance_level="high", frequency_count=10) result = reinforcement.auto_reinforce([memory]) assert len(result) == 0 def test_auto_reinforce_boosts_frequency(self): """auto_reinforce() applies 10% boost to frequency_count.""" reinforcement = MemoryReinforcement() memory = create_mock_memory(importance_level="high", frequency_count=5) result = reinforcement.auto_reinforce([memory]) # 5 * 1.1 + 1 = 6.5 → int = 6 assert result[0].frequency_count == 6 def test_auto_reinforce_frequency_capped(self): """auto_reinforce() caps frequency at MAX_FREQUENCY.""" reinforcement = MemoryReinforcement() memory = create_mock_memory(importance_level="high", frequency_count=9) result = reinforcement.auto_reinforce([memory]) assert result[0].frequency_count == 10 def test_auto_reinforce_improves_decay_score(self): """auto_reinforce() improves decay_score by 5%.""" reinforcement = MemoryReinforcement() memory = create_mock_memory(importance_level="high", frequency_count=5, decay_score=0.5) result = reinforcement.auto_reinforce([memory]) assert result[0].decay_score > 0.5 assert result[0].decay_score == pytest.approx(0.525, abs=0.001) def test_auto_reinforce_updates_last_accessed(self): """auto_reinforce() updates last_accessed_at to now.""" reinforcement = MemoryReinforcement() old_time = datetime.now(UTC) - timedelta(days=30) memory = create_mock_memory( importance_level="high", frequency_count=5, last_accessed_at=old_time ) before = datetime.now(UTC) result = reinforcement.auto_reinforce([memory]) after = datetime.now(UTC) assert before <= result[0].last_accessed_at <= after def test_auto_reinforce_empty_list(self): """auto_reinforce() handles empty list gracefully.""" reinforcement = MemoryReinforcement() result = reinforcement.auto_reinforce([]) assert result == [] def test_auto_reinforce_mixed_memories(self): """auto_reinforce() processes only high-importance, leaves others untouched.""" reinforcement = MemoryReinforcement() memory_high = create_mock_memory(importance_level="high", frequency_count=5) memory_low = create_mock_memory(importance_level="low", frequency_count=5) memory_medium = create_mock_memory(importance_level="medium", frequency_count=5) result = reinforcement.auto_reinforce([memory_high, memory_low, memory_medium]) assert len(result) == 1 assert result[0] is memory_high # Others should not be modified assert memory_low.frequency_count == 5 assert memory_medium.frequency_count == 5 if __name__ == "__main__": pytest.main([__file__, "-v"])