"""Tests for RedisConversationStore. Uses fakeredis so no real Redis connection is required. All tests follow the same ConversationStore contract as InMemoryConversationStore. """ import pytest import fakeredis @pytest.fixture def redis_client(): """Return an in-process fake Redis client.""" return fakeredis.FakeRedis() @pytest.fixture def store(redis_client): """Return a RedisConversationStore backed by fake Redis.""" from app.infrastructure.session.redis_conversation_store import RedisConversationStore return RedisConversationStore(redis_client=redis_client, timeout_seconds=1800) def test_create_session_returns_session_with_id(store): """create_session() must return a ConversationSession with a non-empty session_id.""" session = store.create_session() assert session.session_id assert len(session.session_id) > 0 def test_get_session_returns_same_session(store): """get_session() must return the previously created session.""" session = store.create_session() fetched = store.get_session(session.session_id) assert fetched is not None assert fetched.session_id == session.session_id def test_get_session_returns_none_for_unknown_id(store): """get_session() must return None when the session_id does not exist.""" assert store.get_session("nonexistent-id") is None def test_save_message_appends_to_session(store): """save_message() must append a message and return the updated session.""" session = store.create_session() updated = store.save_message(session.session_id, role="user", content="Hello") assert updated is not None assert len(updated.messages) == 1 assert updated.messages[0].role == "user" assert updated.messages[0].content == "Hello" def test_save_message_persists_across_lookups(store): """Messages saved to a session must be visible in subsequent get_session calls.""" session = store.create_session() store.save_message(session.session_id, role="user", content="test") fetched = store.get_session(session.session_id) assert fetched is not None assert len(fetched.messages) == 1 def test_delete_session_removes_it(store): """delete_session() must return True and remove the session.""" session = store.create_session() assert store.delete_session(session.session_id) is True assert store.get_session(session.session_id) is None def test_delete_session_returns_false_for_unknown(store): """delete_session() must return False when the session does not exist.""" assert store.delete_session("ghost-id") is False def test_list_sessions_includes_created_session(store): """list_sessions() must include all active sessions.""" session = store.create_session() ids = [s["session_id"] for s in store.list_sessions()] assert session.session_id in ids def test_session_expires_after_ttl(redis_client): """Sessions must disappear after the TTL expires.""" from app.infrastructure.session.redis_conversation_store import RedisConversationStore store = RedisConversationStore(redis_client=redis_client, timeout_seconds=1) session = store.create_session() # Simulate TTL expiry by deleting the key directly (fakeredis expire(0) is a no-op). redis_client.delete(f"session:{session.session_id}") assert store.get_session(session.session_id) is None