"""Unit tests for session persistence.""" import json import time from pathlib import Path import pytest from app.agent.context import SessionContext from app.models.config import AgentConfig, AppConfig, LLMConfig, SessionConfig from app.services.session import SessionManager @pytest.fixture def tmp_workspace(tmp_path: Path) -> Path: return tmp_path / "workspace" @pytest.fixture def session_config(tmp_workspace: Path) -> SessionConfig: return SessionConfig( session_dir=tmp_workspace / ".sneakycode" / "sessions", auto_save=True, max_session_age_hours=72, offer_resume=True, ) @pytest.fixture def config(tmp_workspace: Path) -> AppConfig: return AppConfig( llm=LLMConfig(model="test-model", endpoint="http://localhost:11434"), agent=AgentConfig(workspace_root=tmp_workspace), ) @pytest.fixture def ctx(config: AppConfig) -> SessionContext: return SessionContext(config) @pytest.fixture def session_mgr(session_config: SessionConfig, tmp_workspace: Path) -> SessionManager: tmp_workspace.mkdir(parents=True, exist_ok=True) return SessionManager(session_config, tmp_workspace, "test-model") class TestSessionPersistence: def test_save_creates_file(self, session_mgr: SessionManager, ctx: SessionContext, session_config: SessionConfig) -> None: """Saving a session creates a JSON file in the session directory.""" ctx.add_message("user", "Hello") ctx.add_message("assistant", "Hi there!") path = session_mgr.save(ctx) assert path.exists() assert path.suffix == ".json" data = json.loads(path.read_text()) assert data["model"] == "test-model" assert len(data["messages"]) == 2 def test_load_latest_returns_newest(self, session_mgr: SessionManager, ctx: SessionContext, session_config: SessionConfig) -> None: """load_latest returns the most recently modified session.""" ctx.add_message("user", "First session") session_mgr.save(ctx) # Create a second session manager (simulates a new startup) mgr2 = SessionManager(session_config, session_mgr._workspace_root, "test-model") ctx.add_message("assistant", "Second response") path2 = mgr2.save(ctx) loaded = mgr2.load_latest() assert loaded is not None assert loaded.session_id == mgr2._session_id assert len(loaded.messages) == 2 def test_restore_populates_context(self, session_mgr: SessionManager, ctx: SessionContext, config: AppConfig) -> None: """Restoring a session populates the context with saved messages.""" ctx.add_message("user", "Hello") ctx.add_message("assistant", "World") session_mgr.save(ctx) # Load and restore into a fresh context fresh_ctx = SessionContext(config) loaded = session_mgr.load_latest() assert loaded is not None session_mgr.restore(loaded, fresh_ctx) history = fresh_ctx.get_history() assert len(history) == 2 assert history[0].role == "user" assert history[0].content == "Hello" assert history[1].role == "assistant" assert history[1].content == "World" def test_cleanup_removes_old_files(self, session_config: SessionConfig, tmp_workspace: Path) -> None: """cleanup_old deletes files older than max_session_age_hours.""" tmp_workspace.mkdir(parents=True, exist_ok=True) # Create session config with very short max age short_config = SessionConfig( session_dir=session_config.session_dir, max_session_age_hours=0, # 0 hours = everything is old ) mgr = SessionManager(short_config, tmp_workspace, "test-model") # Create a session file manually with old timestamp session_dir = tmp_workspace / short_config.session_dir session_dir.mkdir(parents=True, exist_ok=True) old_file = session_dir / "old_session.json" old_file.write_text('{"version": 1}') # Set mtime to the past import os old_time = time.time() - 3600 # 1 hour ago os.utime(old_file, (old_time, old_time)) deleted = mgr.cleanup_old() assert deleted == 1 assert not old_file.exists()