rag-chatbot / tests /test_chatbot.py
Mobiworks's picture
Initial commit: RAG chatbot
d456104
Raw
History Blame Contribute Delete
4.19 kB
"""
test_chatbot.py
---------------
Unit tests for app/chatbot.py
All heavy components (VectorStore, Retriever, LLMHandler) are mocked
so tests run instantly without GPU or model downloads.
"""
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from langchain.schema import Document
from app.chatbot import Chatbot, ChatResponse
from components.vector_store import VectorStore
from components.retriever import Retriever
from components.llm_handler import LLMHandler
# ── Helpers ───────────────────────────────────────────────────────────────────
def make_mock_retriever(docs=None):
retriever = MagicMock(spec=Retriever)
docs = docs or []
retriever.retrieve.return_value = [
(Document(page_content=d, metadata={"source": "doc.txt"}), 0.8)
for d in docs
]
return retriever
def make_mock_llm(answer="This is the answer."):
llm = MagicMock(spec=LLMHandler)
llm.generate.return_value = answer
return llm
def make_mock_store(is_ready=True):
store = MagicMock(spec=VectorStore)
store.is_ready = is_ready
return store
# ── Tests ─────────────────────────────────────────────────────────────────────
class TestChatbot:
def test_returns_chat_response_object(self):
store = make_mock_store()
retriever = make_mock_retriever(["Some context about the topic."])
llm = make_mock_llm("The answer is 42.")
bot = Chatbot(store, retriever=retriever, llm=llm)
resp = bot.chat("What is the answer?")
assert isinstance(resp, ChatResponse)
def test_answer_comes_from_llm(self):
store = make_mock_store()
retriever = make_mock_retriever(["context"])
llm = make_mock_llm("Specific LLM answer here.")
bot = Chatbot(store, retriever=retriever, llm=llm)
resp = bot.chat("question")
assert resp.answer == "Specific LLM answer here."
def test_sources_are_populated(self):
store = make_mock_store()
retriever = make_mock_retriever(["context chunk"])
llm = make_mock_llm("answer")
bot = Chatbot(store, retriever=retriever, llm=llm)
resp = bot.chat("question")
assert len(resp.sources) >= 1
assert "doc.txt" in resp.sources[0]
def test_empty_query_returns_prompt_message(self):
store = make_mock_store()
bot = Chatbot(store, retriever=make_mock_retriever(), llm=make_mock_llm())
resp = bot.chat("")
assert "please enter" in resp.answer.lower()
def test_store_not_ready_returns_warning(self):
store = make_mock_store(is_ready=False)
retriever = make_mock_retriever()
llm = make_mock_llm()
bot = Chatbot(store, retriever=retriever, llm=llm)
resp = bot.chat("Any question")
assert "no documents" in resp.answer.lower() or "ingest" in resp.answer.lower()
def test_no_retrieved_docs_returns_fallback(self):
store = make_mock_store()
retriever = make_mock_retriever([]) # empty retrieval
llm = make_mock_llm()
bot = Chatbot(store, retriever=retriever, llm=llm)
resp = bot.chat("unanswerable question")
assert "couldn't find" in resp.answer.lower() or "no relevant" in resp.answer.lower()
assert resp.sources == []
def test_query_whitespace_is_stripped(self):
store = make_mock_store()
retriever = make_mock_retriever(["context"])
llm = make_mock_llm("answer")
bot = Chatbot(store, retriever=retriever, llm=llm)
resp = bot.chat(" padded query ")
assert retriever.retrieve.called
called_query = retriever.retrieve.call_args[0][0]
assert called_query == "padded query"