@@ -198,6 +198,126 @@ async def test_time_based_cleanup_integration(self):
198198 f"✅ After time-based cleanup: { len (records_after )} records (within retention)"
199199 )
200200
201+ @pytest .mark .asyncio
202+ async def test_lru_session_cleanup_lifecycle (self ):
203+ """Test LRU session cleanup: keeps most recently used sessions."""
204+ # Create provider with small session limit for testing
205+ db = SQLiteProvider (max_session_records = 20 )
206+
207+ # Override session limit for testing (normally 2000)
208+ db ._cleanup_service .max_total_sessions = 3
209+
210+ print ("\n 🔄 TESTING LRU SESSION CLEANUP LIFECYCLE" )
211+ print ("=" * 60 )
212+
213+ # PHASE 1: Create sessions with different activity patterns
214+ print ("📝 PHASE 1: Creating sessions with different activity patterns..." )
215+
216+ # Session A: Created first, but will be most recently used
217+ await db .save_conversation ("session_A" , "tool1" , "input1" , "output1" )
218+ print (" Session A: Created (oldest creation time)" )
219+
220+ # Session B: Created second
221+ await db .save_conversation ("session_B" , "tool1" , "input1" , "output1" )
222+ print (" Session B: Created" )
223+
224+ # Session C: Created third
225+ await db .save_conversation ("session_C" , "tool1" , "input1" , "output1" )
226+ print (" Session C: Created" )
227+
228+ # Session D: Created fourth
229+ await db .save_conversation ("session_D" , "tool1" , "input1" , "output1" )
230+ print (" Session D: Created" )
231+
232+ # Session E: Created fifth
233+ await db .save_conversation ("session_E" , "tool1" , "input1" , "output1" )
234+ print (" Session E: Created" )
235+
236+ # Add recent activity to Session A (making it most recently used)
237+ await db .save_conversation ("session_A" , "tool2" , "recent_input" , "recent_output" )
238+ print (" Session A: Updated with recent activity (most recently used)" )
239+
240+ # Verify all sessions exist
241+ current_count = db ._cleanup_service .get_session_count ()
242+ assert current_count == 5 , f"Expected 5 sessions, got { current_count } "
243+ print (f"✅ Phase 1: { current_count } sessions created successfully" )
244+
245+ # PHASE 2: Test LRU detection
246+ print ("\n 🔍 PHASE 2: Testing LRU detection..." )
247+
248+ # Get least recently used sessions (should be B, C - oldest last activity)
249+ lru_sessions = db ._cleanup_service .get_least_recently_used_sessions (2 )
250+ print (f" LRU sessions to remove: { lru_sessions } " )
251+
252+ # Should be sessions B and C (oldest last activity)
253+ assert "session_B" in lru_sessions , "Session B should be LRU"
254+ assert "session_C" in lru_sessions , "Session C should be LRU"
255+ assert "session_A" not in lru_sessions , "Session A should NOT be LRU (recent activity)"
256+ print ("✅ Phase 2: LRU detection working correctly" )
257+
258+ # PHASE 3: Trigger LRU cleanup
259+ print ("\n 🧹 PHASE 3: Triggering LRU session cleanup..." )
260+
261+ # Force cleanup by mocking old cleanup time
262+ old_time = datetime .utcnow () - timedelta (days = 2 )
263+ db ._cleanup_service .last_session_cleanup_time = old_time
264+
265+ # Trigger cleanup
266+ deleted_count = db ._cleanup_service .cleanup_excess_sessions ()
267+ print (f" Deleted { deleted_count } records" )
268+
269+ # Verify session count is now at limit
270+ final_count = db ._cleanup_service .get_session_count ()
271+ assert final_count == 3 , f"Expected 3 sessions after cleanup, got { final_count } "
272+ print (f"✅ Phase 3: Session count reduced to { final_count } " )
273+
274+ # PHASE 4: Verify which sessions remain
275+ print ("\n 🔍 PHASE 4: Verifying remaining sessions..." )
276+
277+ sessions_to_check = ["session_A" , "session_B" , "session_C" , "session_D" , "session_E" ]
278+ remaining_sessions = []
279+ deleted_sessions = []
280+
281+ for session_id in sessions_to_check :
282+ records = await db .get_session_conversations (session_id )
283+ if records :
284+ remaining_sessions .append (session_id )
285+ last_activity = max (r .timestamp for r in records )
286+ print (f" ✅ { session_id } : { len (records )} records, last activity: { last_activity } " )
287+ else :
288+ deleted_sessions .append (session_id )
289+ print (f" ❌ { session_id } : DELETED (was least recently used)" )
290+
291+ # Verify correct sessions were kept/deleted
292+ assert "session_A" in remaining_sessions , "Session A should remain (most recent activity)"
293+ assert "session_D" in remaining_sessions , "Session D should remain (recent creation)"
294+ assert "session_E" in remaining_sessions , "Session E should remain (most recent creation)"
295+ assert "session_B" in deleted_sessions , "Session B should be deleted (LRU)"
296+ assert "session_C" in deleted_sessions , "Session C should be deleted (LRU)"
297+
298+ print ("✅ Phase 4: LRU cleanup preserved active sessions correctly" )
299+
300+ # PHASE 5: Verify Session A has both records (original + recent activity)
301+ print ("\n 📊 PHASE 5: Verifying session record preservation..." )
302+
303+ session_a_records = await db .get_session_conversations ("session_A" )
304+ assert len (session_a_records ) == 2 , f"Session A should have 2 records, got { len (session_a_records )} "
305+
306+ # Check that both records exist
307+ sources = [r .source for r in session_a_records ]
308+ assert "tool1" in sources , "Session A should have original tool1 record"
309+ assert "tool2" in sources , "Session A should have recent tool2 record"
310+
311+ print (f" Session A has { len (session_a_records )} records: { sources } " )
312+ print ("✅ Phase 5: Session record preservation working correctly" )
313+
314+ print ("\n 🎯 LRU CLEANUP SUMMARY:" )
315+ print (" - Sessions B, C deleted (least recently used)" )
316+ print (" - Sessions A, D, E preserved (most recently used)" )
317+ print (" - Session A preserved despite being oldest (recent activity)" )
318+ print (" - LRU provides better UX than FIFO!" )
319+ print ("✅ LRU session cleanup lifecycle test PASSED!" )
320+
201321 print ("✅ Time-based cleanup integration working correctly" )
202322
203323 @pytest .mark .asyncio
@@ -266,6 +386,7 @@ async def run_tests():
266386 await test_instance .test_save_retrieve_fifo_cleanup_lifecycle ()
267387 await test_instance .test_multiple_sessions_isolation ()
268388 await test_instance .test_time_based_cleanup_integration ()
389+ await test_instance .test_lru_session_cleanup_lifecycle ()
269390 await test_instance .test_edge_cases_and_error_handling ()
270391 print ("\n 🎉 ALL CONVERSATION HISTORY LIFECYCLE TESTS PASSED!" )
271392
0 commit comments