"""Tests for SessionCompressor."""
from unittest.mock import Mock
import pytest
from session.compressor import SessionCompressor
def test_compress_with_llm(compressor, sample_messages):
"""Test compression with LLM available."""
overview, abstract = compressor.compress(sample_messages)
assert overview == "Test session overview"
assert abstract == "Test abstract"
compressor.llm.complete_json.assert_called_once()
def test_compress_empty_messages(compressor_no_llm):
"""Test compression with empty message list."""
overview, abstract = compressor_no_llm.compress([])
assert overview == ""
assert abstract == "Empty session"
def test_compress_fallback_no_llm(compressor_no_llm, sample_messages):
"""Test fallback compression without LLM."""
overview, abstract = compressor_no_llm.compress(sample_messages)
assert overview is not None
assert len(overview) > 0
assert "4 messages" in overview
assert abstract is not None
assert len(abstract) <= 100
def test_compress_fallback_abstract_length(compressor_no_llm):
"""Test that fallback abstract is truncated to 100 chars."""
long_messages = [
{"role": "user", "content": "x" * 200},
{"role": "assistant", "content": "y" * 200},
]
overview, abstract = compressor_no_llm.compress(long_messages)
assert len(abstract) <= 100
def test_compress_llm_error_fallback(compressor, sample_messages):
"""Test that LLM errors fall back to simple compression."""
compressor.llm.complete_json.side_effect = Exception("LLM failed")
overview, abstract = compressor.compress(sample_messages)
assert overview is not None
assert len(overview) > 0
assert abstract is not None
assert len(abstract) <= 100
def test_compress_llm_error_fallback_keeps_previous_abstract_only(compressor, sample_messages):
"""LLM failure should keep continuity without nesting full previous overviews."""
compressor.llm.complete_json.side_effect = Exception("LLM failed")
overview, abstract = compressor.compress(
sample_messages,
prev_overview="Previous project decisions",
prev_abstract="Prior task summary",
)
assert "Previous abstract: Prior task summary" in overview
assert "Previous overview:" not in overview
assert "Previous project decisions" not in overview
assert "Prior task summary" in abstract
assert len(abstract) <= 100
def test_compress_fallback_keeps_previous_abstract_without_overview(compressor_no_llm, sample_messages):
"""Fallback should preserve prior context even when only an abstract exists."""
overview, abstract = compressor_no_llm.compress(
sample_messages,
prev_abstract="Only previous abstract",
)
assert "Previous abstract: Only previous abstract" in overview
assert "Only previous abstract" in abstract
assert len(abstract) <= 100
def test_compress_llm_abstract_truncation(compressor, sample_messages):
"""Test that LLM abstract is truncated if too long."""
compressor.llm.complete_json.return_value = {
"overview": "Test overview",
"abstract": "x" * 150,
}
overview, abstract = compressor.compress(sample_messages)
assert abstract == "x" * 100
def test_compress_fallback_role_counts(compressor_no_llm):
"""Test that fallback compression counts roles correctly."""
messages = [
{"role": "user", "content": "Q1"},
{"role": "assistant", "content": "A1"},
{"role": "user", "content": "Q2"},
{"role": "assistant", "content": "A2"},
{"role": "user", "content": "Q3"},
]
overview, abstract = compressor_no_llm.compress(messages)
assert "3 from user" in overview
assert "2 from assistant" in overview
def test_compress_fallback_first_message_preview(compressor_no_llm):
"""Test that fallback compression includes first message preview."""
messages = [
{"role": "user", "content": "This is a very long first message that should be truncated"},
{"role": "assistant", "content": "Response"},
]
overview, abstract = compressor_no_llm.compress(messages)
assert "Started with:" in overview
assert "This is a very long first message" in overview
def test_compress_fallback_abstract_from_conversation(compressor_no_llm):
"""Test that fallback abstract derives from conversation."""
messages = [
{"role": "user", "content": "How do I write Python code?"},
{"role": "assistant", "content": "Here's how you write Python code..."},
]
overview, abstract = compressor_no_llm.compress(messages)
assert "Python" in abstract or "write" in abstract
def test_compress_only_user_messages(compressor_no_llm):
"""Test compression with only user messages (no assistant)."""
messages = [
{"role": "user", "content": "Question 1"},
{"role": "user", "content": "Question 2"},
]
overview, abstract = compressor_no_llm.compress(messages)
assert "2 messages" in overview
assert abstract is not None
def test_compress_only_assistant_messages(compressor_no_llm):
"""Test compression with only assistant messages (no user)."""
messages = [
{"role": "assistant", "content": "Response 1"},
{"role": "assistant", "content": "Response 2"},
]
overview, abstract = compressor_no_llm.compress(messages)
assert "2 messages" in overview
assert abstract is not None