diff --git a/tests/chat/test_chat_app.py b/tests/chat/test_chat_app.py index d0f38eab..dcfd572a 100644 --- a/tests/chat/test_chat_app.py +++ b/tests/chat/test_chat_app.py @@ -21,6 +21,15 @@ def test_chat_command(): mock_chat.assert_called_once() +def test_run_chat_creates_app_and_runs(temp_db_path: Path): + """Test run_chat() creates a ChatApp and calls run().""" + with patch("haiku.rag.chat.app.ChatApp.run") as mock_run: + from haiku.rag.chat import run_chat + + run_chat(db_path=temp_db_path) + mock_run.assert_called_once() + + def _make_mock_client(): """Create a mock HaikuRAG client.""" mock_client = AsyncMock() diff --git a/tests/skills/test_rag.py b/tests/skills/test_rag.py index deafd57a..1819537c 100644 --- a/tests/skills/test_rag.py +++ b/tests/skills/test_rag.py @@ -337,6 +337,48 @@ class TestAskTool: # State should store the original question, not the augmented one assert state.qa_history[-1].question == query_text + async def test_ask_embeds_prior_qa_on_demand(self, rag_db, monkeypatch): + from haiku.rag.skills.rag import RAGState, create_skill + from tests.skills.conftest import VECTOR_DIM + + captured_questions = [] + + async def mock_ask(self, question, **kwargs): + captured_questions.append(question) + return ("Answer about AI.", []) + + monkeypatch.setattr(HaikuRAG, "ask", mock_ask) + + skill = create_skill(db_path=rag_db) + ask = _get_tool(skill, "ask") + + # Use the same question text for the prior QA entry and query so + # their fake embeddings are identical (cosine similarity = 1.0). + prior_question = "Tell me about AI" + query_text = prior_question + + # Leave question_embedding=None to exercise the lazy embedding path + state = RAGState( + qa_history=[ + QAHistoryEntry( + question=prior_question, + answer="AI is the simulation of human intelligence by machines.", + question_embedding=None, + ), + ] + ) + ctx = _make_ctx(state) + await ask(ctx, question=query_text) + + # The lazy embedding should have populated question_embedding + assert state.qa_history[0].question_embedding is not None + assert len(state.qa_history[0].question_embedding) == VECTOR_DIM + + # The augmented question should include prior context + assert len(captured_questions) == 1 + assert "Context from prior questions" in captured_questions[0] + assert prior_question in captured_questions[0] + async def test_ask_no_prior_qa_context_when_irrelevant(self, rag_db, monkeypatch): from haiku.rag.skills.rag import RAGState, create_skill from tests.skills.conftest import VECTOR_DIM diff --git a/tests/test_utils.py b/tests/test_utils.py index f486d120..fd3ad83d 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -579,6 +579,14 @@ def test_build_prompt_with_preamble(): # --- is_up_to_date tests --- +def test_cosine_similarity_zero_norm(): + from haiku.rag.utils import cosine_similarity + + assert cosine_similarity([0, 0, 0], [1, 2, 3]) == 0.0 + assert cosine_similarity([1, 2, 3], [0, 0, 0]) == 0.0 + assert cosine_similarity([0, 0], [0, 0]) == 0.0 + + @pytest.mark.asyncio async def test_is_up_to_date(monkeypatch): from unittest.mock import AsyncMock, MagicMock