local-rag-chatbot / tests /test_empty_vectorstore.py
Julien Simon
fix: Address code review security and quality issues
1521cd2
Raw
History Blame Contribute Delete
6.58 kB
"""Tests for empty vectorstore and initial state scenarios."""
from unittest.mock import MagicMock, Mock, patch
import pytest
from qa_chain import QAChainWrapper, create_qa_chain
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.documents import Document
from ui.handlers import create_stream_chat_response
@pytest.fixture
def empty_vectorstore():
"""Create an empty vectorstore mock."""
mock_vs = MagicMock()
mock_vs.get.return_value = {
"documents": [],
"metadatas": [],
"ids": [],
}
mock_retriever = MagicMock()
mock_retriever.invoke.return_value = [] # No documents
mock_vs.as_retriever.return_value = mock_retriever
return mock_vs
@patch("qa_chain.create_llm")
@patch("qa_chain.format_chat_history")
def test_rag_query_with_empty_vectorstore(mock_format_history, mock_create_llm, empty_vectorstore):
"""Test RAG query when vectorstore is empty."""
prompt = ChatPromptTemplate.from_template("Test: {question}")
qa_chain = QAChainWrapper(empty_vectorstore, prompt)
mock_format_history.return_value = ""
mock_llm = MagicMock()
mock_chunk = MagicMock()
mock_chunk.content = "I don't have any documents to search."
mock_llm.stream.return_value = [mock_chunk]
mock_create_llm.return_value = mock_llm
mock_chain = MagicMock()
mock_chain.stream.return_value = [mock_chunk]
qa_chain._prompt.__or__ = MagicMock(return_value=mock_chain)
inputs = {
"question": "What is RAG?",
"chat_history": [],
}
results = list(qa_chain.stream(inputs))
# Should handle empty vectorstore gracefully
assert len(results) > 0
# Should have empty source documents
assert len(results[0]["source_documents"]) == 0
# Context should be empty
context = "\n\n".join(doc.page_content for doc in results[0]["source_documents"])
assert context == ""
@patch("qa_chain.create_llm")
@patch("qa_chain.format_chat_history")
def test_rag_query_with_no_retrieved_documents(mock_format_history, mock_create_llm, mock_vectorstore):
"""Test RAG query when retrieval returns no documents."""
prompt = ChatPromptTemplate.from_template("Test: {question}")
qa_chain = QAChainWrapper(mock_vectorstore, prompt)
mock_format_history.return_value = ""
mock_llm = MagicMock()
mock_chunk = MagicMock()
mock_chunk.content = "No relevant documents found."
mock_llm.stream.return_value = [mock_chunk]
mock_create_llm.return_value = mock_llm
# Retriever returns empty list
mock_retriever = MagicMock()
mock_retriever.invoke.return_value = [] # No documents retrieved
qa_chain._retriever = mock_retriever
mock_chain = MagicMock()
mock_chain.stream.return_value = [mock_chunk]
qa_chain._prompt.__or__ = MagicMock(return_value=mock_chain)
inputs = {
"question": "What is RAG?",
"chat_history": [],
}
results = list(qa_chain.stream(inputs))
# Should handle no documents gracefully
assert len(results) > 0
assert len(results[0]["source_documents"]) == 0
def test_initialize_chain_with_empty_vectorstore():
"""Test chain initialization with empty vectorstore."""
from ui.app import initialize_chain
with patch("ui.app.create_embeddings") as mock_embeddings, \
patch("ui.app.load_or_create_vectorstore") as mock_load_vs, \
patch("ui.app.create_qa_chain") as mock_create_chain:
mock_embeddings.return_value = MagicMock()
mock_vectorstore = MagicMock()
mock_vectorstore.get.return_value = {
"metadatas": [], # Empty metadatas
}
mock_load_vs.return_value = mock_vectorstore
mock_chain = MagicMock()
mock_create_chain.return_value = mock_chain
chain, sources = initialize_chain()
assert chain == mock_chain
assert sources == [] # Should return empty list
@patch("ui.handlers.create_llm")
def test_handlers_with_empty_vectorstore(mock_create_llm, empty_vectorstore):
"""Test handlers with empty vectorstore."""
mock_qa_chain = MagicMock()
mock_qa_chain.stream.return_value = [
{
"chunk": "No documents available.",
"source_documents": [],
"docs_with_scores": None,
"rewritten_query": None,
"hybrid_scores": None,
}
]
stream_fn = create_stream_chat_response(mock_qa_chain)
results = list(
stream_fn(
"What is RAG?",
[],
"RAG",
doc_filter=None,
search_type="mmr",
)
)
assert len(results) > 0
# Should handle empty results gracefully
assert len(results[0][1]) == 0 # source_documents is empty
@patch("retrievers.BM25Okapi")
def test_hybrid_search_with_empty_documents(mock_bm25, empty_vectorstore):
"""Test hybrid search with empty document collection."""
from retrievers import HybridRetriever
empty_vectorstore.get.return_value = {
"documents": [],
"metadatas": [],
}
empty_vectorstore.similarity_search_with_score.return_value = []
retriever = HybridRetriever(empty_vectorstore)
results = retriever.hybrid_search("test query", k=5)
# Should return empty list
assert results == []
@patch("vectorstore.Chroma")
@patch("vectorstore.get_pdf_files")
def test_create_new_vectorstore_no_pdfs(mock_get_pdfs, mock_chroma):
"""Test creating new vectorstore when no PDFs exist."""
from vectorstore import create_new_vectorstore
mock_get_pdfs.return_value = [] # No PDF files
with pytest.raises(FileNotFoundError):
create_new_vectorstore(MagicMock())
def test_rerank_with_empty_documents(mock_vectorstore):
"""Test re-ranking with empty document list."""
from qa_chain import QAChainWrapper
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("Test: {question}")
qa_chain_wrapper = QAChainWrapper(mock_vectorstore, prompt)
result = qa_chain_wrapper.rerank_documents("query", [], top_k=5)
# Should return empty list
assert result == []
def test_hybrid_retriever_with_empty_collection(empty_vectorstore):
"""Test hybrid retriever initialization with empty collection."""
from retrievers import HybridRetriever
retriever = HybridRetriever(empty_vectorstore)
retriever._build_bm25_index()
# BM25 should be None when no documents
assert retriever._bm25 is None
assert retriever._documents == []