pdf_qa_bot / test_main.py
yash-080706's picture
Upload 17 files
9aebc0e verified
Raw
History Blame Contribute Delete
11.4 kB
import sys
from unittest.mock import MagicMock
import multiprocessing
# Prevent downloading/loading Hugging Face embeddings during testing by mocking the class
import langchain_community.embeddings
langchain_community.embeddings.HuggingFaceEmbeddings = MagicMock()
import pytest
from fastapi.testclient import TestClient
from main import (
app,
detect_question_intent,
sanitize_upload_filename,
concise_excerpt,
split_sentences,
clean_sentence,
query_keywords,
tokenize_text,
build_answer_from_documents,
INSUFFICIENT_CONTEXT_MESSAGE,
passes_evidence_gate,
document_dedupe_key,
citation_source_for_document,
internal_token_valid,
normalize_session_id,
get_session_dir,
_extract_pdf_text_worker,
)
import secrets as _secrets
def is_authorized_session_update(session: dict, provided_secret) -> bool:
"""Replicate the session-secret check from the endpoint (moved inline upstream)."""
expected = (session.get("session_secret") or "").strip()
candidate = (provided_secret or "").strip()
if not expected or not candidate:
return False
return _secrets.compare_digest(candidate, expected)
def test_session_secret_authorizes_only_matching_secret():
session = {"session_secret": "expected-secret"}
assert is_authorized_session_update(session, "expected-secret") is True
assert is_authorized_session_update(session, "wrong-secret") is False
assert is_authorized_session_update(session, None) is False
assert is_authorized_session_update({}, "expected-secret") is False
def test_detect_question_intent():
assert detect_question_intent("What is this document about?") == "overview"
assert detect_question_intent("What are these documents about?") == "overview"
assert detect_question_intent("Explain the connection between X and Y") == "relationship"
assert detect_question_intent("How does X compare to Y?") == "comparison"
assert detect_question_intent("Compare the performance of model A and B") == "comparison"
assert detect_question_intent("What is the revenue in 2023?") == "factual"
assert detect_question_intent("Who is the CEO of the company?") == "factual"
def test_sanitize_upload_filename_valid():
assert sanitize_upload_filename("test.pdf") == "test.pdf"
assert sanitize_upload_filename("path/to/my_document.PDF") == "my_document.PDF"
assert sanitize_upload_filename("C:\\Users\\file-name_123.pdf") == "file-name_123.pdf"
def test_sanitize_upload_filename_invalid():
with pytest.raises(ValueError, match="Missing PDF file path"):
sanitize_upload_filename("")
with pytest.raises(ValueError, match="Missing PDF file path"):
sanitize_upload_filename(" ")
with pytest.raises(ValueError, match="Only PDF files are allowed"):
sanitize_upload_filename("test.txt")
with pytest.raises(ValueError, match="Uploaded filename contains unsupported characters"):
sanitize_upload_filename("test$file.pdf")
def test_internal_token_valid_allows_when_unset():
assert internal_token_valid(None, "") is True
assert internal_token_valid("", "") is True
def test_internal_token_valid_rejects_missing_when_set():
assert internal_token_valid(None, "secret") is False
assert internal_token_valid("", "secret") is False
assert internal_token_valid(" ", "secret") is False
def test_internal_token_valid_accepts_exact_match():
assert internal_token_valid("secret", "secret") is True
def test_internal_auth_middleware_protects_validate_session_write():
import main as main_module
original_token = main_module.INTERNAL_RAG_TOKEN
main_module.INTERNAL_RAG_TOKEN = "test-secret"
try:
client = TestClient(app)
response = client.post("/validate-session-write")
assert response.status_code == 403
assert response.json()["error"] == "Forbidden"
finally:
main_module.INTERNAL_RAG_TOKEN = original_token
def test_normalize_session_id_rejects_invalid_values():
with pytest.raises(ValueError, match="Missing session id"):
normalize_session_id("")
with pytest.raises(ValueError):
normalize_session_id("not-a-uuid")
def test_get_session_dir_requires_uuid_session_id():
with pytest.raises(ValueError):
get_session_dir("../escape")
def test_normalize_session_id_returns_canonical_uuid():
normalized = normalize_session_id("550E8400-E29B-41D4-A716-446655440000")
assert normalized == "550e8400-e29b-41d4-a716-446655440000"
def test_extract_pdf_text_worker_enforces_page_limit(tmp_path):
from pypdf import PdfWriter
pdf_path = tmp_path / "hello.pdf"
writer = PdfWriter()
writer.add_blank_page(width=300, height=144)
with pdf_path.open("wb") as fp:
writer.write(fp)
# Use a local queue and call the worker directly (no subprocess) to validate limit logic.
q = multiprocessing.Queue(maxsize=1)
_extract_pdf_text_worker(str(pdf_path), max_pages=0, max_chars=1000, out_queue=q)
result = q.get(timeout=2)
assert result["ok"] is False
assert "too many pages" in result["error"].lower()
def test_concise_excerpt():
text = "This is a very long sentence that we want to abbreviate cleanly."
assert concise_excerpt(text, max_chars=20) == "This is a very long..."
assert concise_excerpt(text, max_chars=100) == text
def test_split_sentences():
text = "First sentence! Second sentence. Third one?"
sentences = split_sentences(text)
assert len(sentences) == 3
assert sentences[0] == "First sentence!"
assert sentences[1] == "Second sentence."
assert sentences[2] == "Third one?"
def test_clean_sentence():
assert clean_sentence(" - Clean this sentence ") == "Clean this sentence"
assert clean_sentence("* clean me ") == "clean me"
def test_query_keywords():
# Stopwords like "what", "is", "this", "about" are filtered out
# Only tokens with length > 2 are kept
assert query_keywords("What is this document about revenue?") == {"revenue"}
assert query_keywords("accuracy of model") == {"model", "accuracy"}
def test_empty_query_handling():
query = ""
assert query.strip() == ""
def test_invalid_query_type():
query = None
assert query is None
def test_context_document_presence():
docs = ["sample pdf content", "rag pipeline notes"]
assert len(docs) > 0
def test_answer_response_structure():
response = {
"answer": "Sample answer",
"sources": ["doc1.pdf"]
}
assert "answer" in response
assert isinstance(response["sources"], list)
def test_db_connection_placeholder():
db_status = True
assert db_status is True
def test_query_routing_logic():
query = "Summarize this PDF"
if "summarize" in query.lower():
route = "summarizer"
else:
route = "qa"
assert route == "summarizer"
def test_crawler_response_structure():
crawler_output = {
"url": "https://example.com",
"content": "Sample crawled content",
"status": 200
}
assert "url" in crawler_output
assert "content" in crawler_output
assert crawler_output["status"] == 200
def test_crawler_empty_content():
crawler_output = {
"url": "https://example.com",
"content": "",
"status": 200
}
assert crawler_output["content"] == ""
def test_crawler_failed_status():
crawler_output = {
"url": "https://example.com",
"content": None,
"status": 500
}
assert crawler_output["status"] >= 400
def test_retry_logic_placeholder():
retries = 3
success = True
for _ in range(retries):
success = True
assert success is True
def test_document_extraction_consistency():
extracted_chunks = [
"chunk one",
"chunk two",
"chunk three"
]
assert len(extracted_chunks) == 3
assert all(isinstance(chunk, str) for chunk in extracted_chunks)
def test_crawler_metadata_preservation():
metadata = {
"source": "sample.pdf",
"page": 1
}
assert metadata["source"] == "sample.pdf"
assert metadata["page"] == 1
def test_empty_document_handling():
extracted_text = ""
assert extracted_text == ""
def test_unstructured_pdf_ingestion_mock():
mock_document = {
"filename": "research.pdf",
"content": "This is extracted PDF content"
}
assert "pdf" in mock_document["filename"]
assert len(mock_document["content"]) > 0
class DummyDocument:
def __init__(self, content, filename="doc.pdf", page=0):
self.page_content = content
self.metadata = {
"filename": filename,
"page": page,
"document_id": filename,
}
def test_evidence_gate_refuses_when_overlap_missing():
docs = [DummyDocument("this is unrelated content", filename="a.pdf", page=0)]
assert passes_evidence_gate("What is the revenue?", docs, best_score=0.1, intent="factual") is False
def test_evidence_gate_allows_when_overlap_and_score_good():
docs = [DummyDocument("Revenue for 2023 was 10 million.", filename="a.pdf", page=0)]
assert passes_evidence_gate("What is the revenue for 2023?", docs, best_score=0.2, intent="factual") is True
def test_build_answer_includes_citations_for_grounded_answer():
doc = DummyDocument("Revenue for 2023 was 10 million.", filename="a.pdf", page=0)
source_id_by_key = {document_dedupe_key(doc): 1}
answer = build_answer_from_documents(
"What is the revenue for 2023?",
[doc],
"factual",
source_id_by_key=source_id_by_key,
)
assert "Source 1" in answer or "Sources 1" in answer
def test_build_answer_refuses_when_unanswerable():
doc = DummyDocument("This document is about hiring policies.", filename="a.pdf", page=0)
source_id_by_key = {document_dedupe_key(doc): 1}
answer = build_answer_from_documents(
"What is the revenue for 2023?",
[doc],
"factual",
source_id_by_key=source_id_by_key,
)
assert answer == INSUFFICIENT_CONTEXT_MESSAGE
def test_citation_source_for_document_preserves_jump_metadata():
doc = DummyDocument("Internship duration is 6 weeks. More details follow.", filename="policy.pdf", page=11)
doc.metadata["chunk_index"] = 4
source = citation_source_for_document(doc, 0)
assert source["document"] == "policy.pdf"
assert source["page"] == 12
assert source["chunk_index"] == 4
assert source["text"].startswith("Internship duration")
assert source["preview"].startswith("Internship duration")
def test_citation_source_for_document_handles_missing_metadata():
doc = DummyDocument("Useful supporting text.", filename="", page=0)
doc.metadata = {}
source = citation_source_for_document(doc, 2)
assert source["document"] == "uploaded document"
assert source["page"] is None
assert source["chunk_index"] == 2