ProfillyBot / tests /test_document_processor_exclusion.py
MinhDS's picture
Deploy ProfillyBot: Gradio ZeroGPU + CV RAG (Qwen2.5-3B)
0828c2c verified
Raw
History Blame Contribute Delete
9.76 kB
"""Tests for main document exclusion in document processing."""
import copy
import pytest
from src.config_loader import get_config
from src.document_processor import DocumentProcessor
@pytest.fixture(autouse=True)
def cleanup_config():
"""Ensure config is properly restored after each test in this module."""
config = get_config()
# Save original get method
original_get = config.get
# Save original config
original_config = copy.deepcopy(config._config)
yield
# Restore original get method and config
config.get = original_get
config._config = original_config
class TestMainDocumentExclusion:
"""Test that main document is excluded from vector store processing."""
def test_main_document_excluded_from_processing(self, tmp_path):
"""Test that main document file is skipped during directory processing."""
# Create test documents directory
docs_dir = tmp_path / "documents"
docs_dir.mkdir()
# Create main document
main_doc = docs_dir / "main_profile.md"
main_doc.write_text("# Main Profile\n\nThis should be excluded.")
# Create other documents
other_doc1 = docs_dir / "resume.md"
other_doc1.write_text("# Resume\n\nThis should be included.")
other_doc2 = docs_dir / "projects.md"
other_doc2.write_text("# Projects\n\nThis should also be included.")
# Create processor and mock config
processor = DocumentProcessor()
original_get = processor.config.get
def mock_get(key, default=None):
if key == "main_document.path":
return str(main_doc)
if key == "main_document.exclude_from_index":
return True
return original_get(key, default)
processor.config.get = mock_get
# Process directory
documents = processor.process_directory(docs_dir)
# Get all source files from processed documents
processed_files = {doc.metadata.get("source", "") for doc in documents}
# Main document should NOT be in processed files
assert str(main_doc) not in processed_files
# Other documents SHOULD be processed
assert any("resume.md" in source for source in processed_files)
assert any("projects.md" in source for source in processed_files)
def test_main_document_included_when_exclude_from_index_false(self, tmp_path):
"""Test main document is indexed when exclude_from_index is false."""
docs_dir = tmp_path / "documents"
docs_dir.mkdir()
main_doc = docs_dir / "main_profile.md"
main_doc.write_text("# Main Profile\n\nShould be indexed.")
processor = DocumentProcessor()
original_get = processor.config.get
def mock_get(key, default=None):
if key == "main_document.path":
return str(main_doc)
if key == "main_document.exclude_from_index":
return False
return original_get(key, default)
processor.config.get = mock_get
documents = processor.process_directory(docs_dir)
processed_files = {doc.metadata.get("source", "") for doc in documents}
assert any("main_profile.md" in source for source in processed_files)
def test_all_documents_processed_when_main_doc_disabled(self, tmp_path):
"""Test that all documents are processed when main doc feature is disabled."""
# Create test documents directory
docs_dir = tmp_path / "documents"
docs_dir.mkdir()
# Create documents including what would be the main document
main_doc = docs_dir / "main_profile.md"
main_doc.write_text("# Main Profile\n\nContent here.")
other_doc = docs_dir / "resume.md"
other_doc.write_text("# Resume\n\nContent here.")
# Create processor with main doc disabled
processor = DocumentProcessor()
original_get = processor.config.get
def mock_get(key, default=None):
if key == "main_document.path":
return "" # Empty path = disabled
return original_get(key, default)
processor.config.get = mock_get
# Process directory
documents = processor.process_directory(docs_dir)
# Get all source files
processed_files = {doc.metadata.get("source", "") for doc in documents}
# ALL documents should be processed (including main_profile.md)
assert any("main_profile.md" in source for source in processed_files)
assert any("resume.md" in source for source in processed_files)
def test_main_document_exclusion_case_insensitive_paths(self, tmp_path):
"""Test that path comparison works regardless of path format."""
docs_dir = tmp_path / "documents"
docs_dir.mkdir()
# Create main document
main_doc = docs_dir / "main_profile.md"
main_doc.write_text("# Main Profile\n\nExclude me.")
other_doc = docs_dir / "other.md"
other_doc.write_text("# Other\n\nInclude me.")
processor = DocumentProcessor()
original_get = processor.config.get
def mock_get(key, default=None):
if key == "main_document.path":
# Return path with different format but same file
return str(main_doc.resolve())
if key == "main_document.exclude_from_index":
return True
return original_get(key, default)
processor.config.get = mock_get
# Process directory
documents = processor.process_directory(docs_dir)
# Get all source files
processed_files = {doc.metadata.get("source", "") for doc in documents}
# Main document should be excluded
assert str(main_doc) not in processed_files
assert str(main_doc.resolve()) not in processed_files
# Other document should be included
assert len(documents) > 0
assert any("other.md" in source for source in processed_files)
def test_subdirectory_main_document_excluded(self, tmp_path):
"""Test that main document in subdirectory is excluded."""
docs_dir = tmp_path / "documents"
docs_dir.mkdir()
# Create subdirectory
subdir = docs_dir / "profiles"
subdir.mkdir()
# Create main document in subdirectory
main_doc = subdir / "main_profile.md"
main_doc.write_text("# Main Profile\n\nExclude me.")
# Create other document
other_doc = docs_dir / "resume.md"
other_doc.write_text("# Resume\n\nInclude me.")
processor = DocumentProcessor()
original_get = processor.config.get
def mock_get(key, default=None):
if key == "main_document.path":
return str(main_doc)
if key == "main_document.exclude_from_index":
return True
return original_get(key, default)
processor.config.get = mock_get
# Process directory (recursively)
documents = processor.process_directory(docs_dir)
# Get all source files
processed_files = {doc.metadata.get("source", "") for doc in documents}
# Main document should be excluded
assert str(main_doc) not in processed_files
# Other document should be included
assert any("resume.md" in source for source in processed_files)
def test_no_documents_after_main_doc_exclusion(self, tmp_path):
"""Test behavior when only main document exists."""
docs_dir = tmp_path / "documents"
docs_dir.mkdir()
# Create only the main document
main_doc = docs_dir / "main_profile.md"
main_doc.write_text("# Main Profile\n\nOnly document.")
processor = DocumentProcessor()
original_get = processor.config.get
def mock_get(key, default=None):
if key == "main_document.path":
return str(main_doc)
if key == "main_document.exclude_from_index":
return True
return original_get(key, default)
processor.config.get = mock_get
# Process directory
documents = processor.process_directory(docs_dir)
# Should have no documents (only main doc was excluded)
assert len(documents) == 0
def test_main_document_with_different_extension_not_confused(self, tmp_path):
"""Test that only exact main document file is excluded."""
docs_dir = tmp_path / "documents"
docs_dir.mkdir()
# Create main document (PDF)
main_doc = docs_dir / "main_profile.pdf"
main_doc.write_text("PDF content")
# Create similar named file (MD) - should NOT be excluded
similar_doc = docs_dir / "main_profile.md"
similar_doc.write_text("# Main Profile\n\nInclude me.")
processor = DocumentProcessor()
original_get = processor.config.get
def mock_get(key, default=None):
if key == "main_document.path":
return str(main_doc) # Only PDF is main doc
if key == "main_document.exclude_from_index":
return True
return original_get(key, default)
processor.config.get = mock_get
# Process directory
documents = processor.process_directory(docs_dir)
# Get all source files
processed_files = {doc.metadata.get("source", "") for doc in documents}
# MD file should be included (different extension)
assert any("main_profile.md" in source for source in processed_files)
# PDF should be excluded
# Note: PDF loading might fail with text content, but it should be skipped anyway