Files
gh-christianlouis-docuelevate/tests/conftest.py
T
copilot-swe-agent[bot] b1005bdac3 Add comprehensive file upload tests
- Created test_file_upload.py with 19 tests covering all aspects of file upload functionality
- Tests include: valid file uploads (PDF, images, office docs, text, CSV)
- Invalid file handling (oversized, executables, empty files, no file)
- Security tests (path traversal, special characters)
- Error handling (disk write failures, celery failures)
- Filename handling (UUID generation, no extension)
- MIME type detection
- Modified conftest.py to mock Celery module to prevent Redis connection during tests
- All 19 tests passing

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-07 19:39:25 +00:00

204 lines
5.3 KiB
Python

"""
Pytest configuration and shared fixtures for DocuElevate tests.
"""
import os
import tempfile
import pytest
import sys
from typing import Generator
from unittest.mock import Mock, MagicMock
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool
# Set test environment variables before importing app
os.environ["DATABASE_URL"] = "sqlite:///:memory:"
os.environ["REDIS_URL"] = "redis://localhost:6379/1"
os.environ["OPENAI_API_KEY"] = "test-key"
os.environ["AZURE_AI_KEY"] = "test-key"
os.environ["AZURE_REGION"] = "test"
os.environ["AZURE_ENDPOINT"] = "https://test.cognitiveservices.azure.com/"
os.environ["GOTENBERG_URL"] = "http://localhost:3000"
os.environ["WORKDIR"] = "/tmp"
os.environ["AUTH_ENABLED"] = "False"
os.environ["SESSION_SECRET"] = "test_secret_key_for_testing_must_be_at_least_32_characters_long"
# Mock Celery before any app imports to prevent Redis connection attempts
mock_celery = MagicMock()
sys.modules['celery'] = mock_celery
sys.modules['celery.signals'] = MagicMock()
from app.database import Base, get_db
from app.main import app as fastapi_app
# Import models to register them with SQLAlchemy Base
from app.models import DocumentMetadata, FileRecord, ProcessingLog
@pytest.fixture(scope="session")
def test_workdir() -> Generator[str, None, None]:
"""Create a temporary work directory for tests."""
with tempfile.TemporaryDirectory() as tmpdir:
yield tmpdir
@pytest.fixture(scope="function")
def db_session():
"""Create a fresh database session for each test."""
# Create an in-memory SQLite database
engine = create_engine(
"sqlite:///:memory:",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
# Create all tables
Base.metadata.create_all(bind=engine)
# Create a session
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
session = TestingSessionLocal()
try:
yield session
finally:
session.close()
Base.metadata.drop_all(bind=engine)
@pytest.fixture(scope="function")
def client(db_session) -> TestClient:
"""Create a test client with a fresh database."""
# Import all the different get_db functions used across the app
from app.api.common import get_db as api_get_db
from app.views.base import get_db as views_get_db
# Override the get_db dependency to use our test database
def override_get_db():
try:
yield db_session
finally:
pass
# Override all variants of get_db
fastapi_app.dependency_overrides[get_db] = override_get_db
fastapi_app.dependency_overrides[api_get_db] = override_get_db
fastapi_app.dependency_overrides[views_get_db] = override_get_db
# Use base_url to satisfy TrustedHostMiddleware
with TestClient(fastapi_app, base_url="http://localhost") as test_client:
yield test_client
# Clean up
fastapi_app.dependency_overrides.clear()
@pytest.fixture
def sample_pdf_path(test_workdir) -> str:
"""Create a sample PDF file for testing."""
pdf_path = os.path.join(test_workdir, "test.pdf")
# Create a minimal valid PDF
pdf_content = b"""%PDF-1.4
1 0 obj
<<
/Type /Catalog
/Pages 2 0 R
>>
endobj
2 0 obj
<<
/Type /Pages
/Kids [3 0 R]
/Count 1
>>
endobj
3 0 obj
<<
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 612 792]
>>
endobj
xref
0 4
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
trailer
<<
/Size 4
/Root 1 0 R
>>
startxref
197
%%EOF
"""
with open(pdf_path, 'wb') as f:
f.write(pdf_content)
return pdf_path
@pytest.fixture
def sample_text_file(test_workdir) -> str:
"""Create a sample text file for testing."""
text_path = os.path.join(test_workdir, "test.txt")
with open(text_path, 'w') as f:
f.write("This is a test document.\nWith multiple lines.\n")
return text_path
@pytest.fixture
def mock_openai_response():
"""Mock OpenAI API response for testing."""
return {
"choices": [{
"message": {
"content": '{"document_type": "invoice", "summary": "Test invoice", "tags": ["test", "invoice"]}'
}
}]
}
@pytest.fixture
def mock_azure_response():
"""Mock Azure Document Intelligence API response for testing."""
return {
"analyzeResult": {
"content": "Test document content extracted by OCR",
"pages": [{"pageNumber": 1}]
}
}
# Markers for categorizing tests
def pytest_configure(config):
"""Configure custom pytest markers."""
config.addinivalue_line(
"markers", "unit: Unit tests for individual functions/methods"
)
config.addinivalue_line(
"markers", "integration: Integration tests for API endpoints and workflows"
)
config.addinivalue_line(
"markers", "slow: Tests that take significant time to run"
)
config.addinivalue_line(
"markers", "security: Security-related tests"
)
config.addinivalue_line(
"markers", "requires_external: Tests requiring external services"
)
config.addinivalue_line(
"markers", "requires_db: Tests requiring database"
)
config.addinivalue_line(
"markers", "requires_redis: Tests requiring Redis"
)