""" Pytest configuration and fixtures for local-context7 tests. This module provides: - Mocks for external dependencies (Qdrant, FastEmbed) - Database fixtures for SQLite operations - Common test utilities """ from unittest.mock import MagicMock, patch import pytest import os import json from pathlib import Path from backend.app.db import init_db, upsert_library, insert_document_chunk, get_chunks_for_library, list_libraries, clear_library_documents, get_connection # ============================================================================= # FIXTURES # ============================================================================= @pytest.fixture(scope="function") def test_database(): """ Create a fresh SQLite database for testing. Yields: Database connection with tables initialized """ # Use an in-memory or temporary file database db_path = Path(__file__).parent.parent / "backend" / "data" / "test_db.sqlite" # Ensure data directory exists db_path.parent.mkdir(parents=True, exist_ok=True) # Remove existing test DB if present if db_path.exists(): db_path.unlink() # Initialize database with tables result = init_db() assert result["success"], f"Failed to initialize test DB: {result.get('error')}" yield # Cleanup: remove test database after tests if db_path.exists(): db_path.unlink() @pytest.fixture(scope="function") def sample_text(): """Sample text for chunking tests.""" return """# Introduction This is the introduction section. ## Background Background information goes here to make this longer and test chunking. This paragraph has more content about the background topic. ### Details Specific details about the background are provided in this subsection. More details follow here to ensure we have enough text to properly test heading preservation. ## Conclusion The conclusion wraps up everything nicely.""" # ============================================================================= # MOCKS # ============================================================================= @pytest.fixture def mock_embedding_model(): """ Mock FastEmbed model that returns dummy vectors. This avoids needing to download and load the actual embedding model. Returns 384-dimensional zero vectors for any input. """ mock_model = MagicMock() # Mock embed method - returns list of lists with float values def mock_embed(texts): return [ [0.0] * 384 # Zero vector placeholder for _ in texts ] mock_model.embed = mock_embed return mock_model @pytest.fixture def mock_qdrant_client(): """ Mock Qdrant client that returns empty or test results. Allows testing search logic without needing a running Qdrant server. """ mock_client = MagicMock() # Mock search method def mock_search(collection_name, query_vector, limit=10, search_filter=None): # Return empty list (simulating no results) return [] mock_client.search = mock_search # Mock delete_collection for cleanup mock_client.delete_collection = MagicMock(return_value=True) return mock_client @pytest.fixture def mock_embedding_model_batch(): """ Batch embedding model mock that returns deterministic fake vectors. Returns slightly different vectors for different input lengths/first chars, allowing tests to verify vector retrieval if needed. """ def hash_text(text): # Simple hash-based pseudo-random vector generation text_hash = hash(text) % 1000000 return [(hash_text(text) / 1000000 + (i * 0.001)) for i in range(384)] mock_model = MagicMock() mock_model.embed = lambda texts: [hash_text(t) for t in texts] return mock_model # ============================================================================= # SETUP TEARDOWN FIXTURES # ============================================================================= @pytest.fixture(autouse=True) def clear_test_database(test_database): """ Clear test database before and after each test function. Note: This fixture runs the teardown (cleanup) AFTER the test, so we manually clear at the end of the yield context. The db_path is cleaned up by the test_database fixture's yield block. """ pass # Cleanup handled in test_database fixture @pytest.fixture def empty_vector(): """Empty/dummy embedding vector for tests.""" return [0.0] * 384 @pytest.fixture def fake_embeddings(sample_text): """Fake embedding vectors for sample text.""" def hash_text(text): return [(hash(text) + len(text)) % 1000 / 10000 for _ in range(384)] return [hash_text(s) for s in sample_text.split("\n\n") if s.strip()] # ============================================================================= # UTILITY FUNCTIONS # ============================================================================= @pytest.fixture def temp_file(tmp_path): """Create a temporary file and yield its path.""" test_file = tmp_path / "test.txt" return test_file # Register custom marker for slow tests (if needed) def pytest_configure(config): config.addinivalue_line("markers", "slow: marks tests as slow (deselect with '-m \"not slow\"')") def pytest_runtest_setup(item): """Add custom markers if needed.""" pass