"""Tests for ConversationManager."""
import pytest
class TestConversationManager:
"""Tests for ConversationManager state management."""
def test_add_turn_appends_to_history(self):
"""Adding a turn appends to conversation history."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
cm.add_turn("user", "Hello")
cm.add_turn("assistant", "Hi there!")
assert len(cm.history) == 2
assert cm.history[0] == ("user", "Hello")
assert cm.history[1] == ("assistant", "Hi there!")
def test_add_turn_updates_token_counts_estimated(self):
"""Adding turns updates token estimates when no explicit count given."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
cm.add_turn("user", "Hello world") # 11 chars = ~2 tokens
cm.add_turn("assistant", "Hi there friend") # 15 chars = ~3 tokens
assert cm.tokens_in > 0
assert cm.tokens_out > 0
def test_add_turn_with_explicit_tokens(self):
"""Adding turns with explicit token count uses that count."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
cm.add_turn("user", "Hello", tokens=5)
cm.add_turn("assistant", "Hi!", tokens=10)
assert cm.tokens_in == 5
assert cm.tokens_out == 10
def test_get_recent_returns_last_n_turns(self):
"""get_recent returns the most recent N turns."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
for i in range(10):
cm.add_turn("user", f"message {i}")
recent = cm.get_recent(3)
assert len(recent) == 3
assert recent[0][1] == "message 7"
def test_get_recent_returns_all_if_less_than_count(self):
"""get_recent returns all turns if fewer than requested."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
cm.add_turn("user", "Hello")
cm.add_turn("assistant", "Hi!")
recent = cm.get_recent(10)
assert len(recent) == 2
def test_clear_resets_all_state(self):
"""clear() resets history and metrics."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
cm.add_turn("user", "Hello")
cm.set_latency(100)
cm.clear()
assert len(cm.history) == 0
assert cm.tokens_in == 0
assert cm.tokens_out == 0
assert cm.latency_ms is None
def test_to_llm_messages_format(self):
"""to_llm_messages returns correct format for LLM API."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
cm.add_turn("user", "Hello")
cm.add_turn("assistant", "Hi!")
messages = cm.to_llm_messages()
assert messages == [
{"role": "user", "content": "Hello"},
{"role": "assistant", "content": "Hi!"},
]
def test_last_user_text_property(self):
"""last_user_text returns most recent user message."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
cm.add_turn("user", "First")
cm.add_turn("assistant", "Response")
cm.add_turn("user", "Second")
assert cm.last_user_text == "Second"
def test_last_user_text_empty_when_no_user_messages(self):
"""last_user_text returns empty string when no user messages."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
cm.add_turn("assistant", "Hello!")
assert cm.last_user_text == ""
def test_last_response_property(self):
"""last_response returns most recent assistant message."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
cm.add_turn("user", "Hello")
cm.add_turn("assistant", "Hi!")
cm.add_turn("user", "How are you?")
assert cm.last_response == "Hi!"
def test_last_response_empty_when_no_assistant_messages(self):
"""last_response returns empty string when no assistant messages."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
cm.add_turn("user", "Hello")
assert cm.last_response == ""
def test_set_latency(self):
"""set_latency records latency value."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
cm.set_latency(250)
assert cm.latency_ms == 250
def test_turn_count_property(self):
"""turn_count returns number of conversation turns."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
assert cm.turn_count == 0
cm.add_turn("user", "Hello")
assert cm.turn_count == 1
cm.add_turn("assistant", "Hi!")
assert cm.turn_count == 2
def test_is_empty_property(self):
"""is_empty returns True for empty conversation."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
assert cm.is_empty is True
cm.add_turn("user", "Hello")
assert cm.is_empty is False
def test_tokens_accumulate_across_turns(self):
"""Token counts accumulate across multiple turns."""
from src.localvoicemode.state.conversation import ConversationManager
cm = ConversationManager()
cm.add_turn("user", "Hello", tokens=5)
cm.add_turn("assistant", "Hi!", tokens=3)
cm.add_turn("user", "How are you?", tokens=10)
cm.add_turn("assistant", "I'm good!", tokens=8)
assert cm.tokens_in == 15 # 5 + 10
assert cm.tokens_out == 11 # 3 + 8