Merge pull request #162 from christianlouis/copilot/refactor-get-db-module

refactor: consolidate get_db into single module
This commit is contained in:
Christian Krakau-Louis
2026-02-09 17:02:30 +01:00
committed by GitHub
5 changed files with 42 additions and 80 deletions
-10
View File
@@ -9,21 +9,11 @@ from pathlib import Path
from fastapi import HTTPException, status from fastapi import HTTPException, status
from app.config import settings from app.config import settings
from app.database import SessionLocal
# Set up logging # Set up logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_db():
"""Database dependency injection for routes"""
db = SessionLocal()
try:
yield db
finally:
db.close()
def resolve_file_path(file_path: str, subfolder: str = None) -> str: def resolve_file_path(file_path: str, subfolder: str = None) -> str:
""" """
Resolves a file path to an absolute path with path traversal protection. Resolves a file path to an absolute path with path traversal protection.
+1 -1
View File
@@ -12,9 +12,9 @@ from fastapi import APIRouter, Depends, File, HTTPException, Query, Request, Upl
from sqlalchemy import asc, desc, or_ from sqlalchemy import asc, desc, or_
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.api.common import get_db
from app.auth import require_login from app.auth import require_login
from app.config import settings from app.config import settings
from app.database import get_db
from app.models import FileRecord, ProcessingLog from app.models import FileRecord, ProcessingLog
from app.tasks.convert_to_pdf import convert_to_pdf from app.tasks.convert_to_pdf import convert_to_pdf
from app.tasks.process_document import process_document from app.tasks.process_document import process_document
+1 -1
View File
@@ -9,8 +9,8 @@ from fastapi import APIRouter, Depends, HTTPException, Query, Request
from sqlalchemy import desc from sqlalchemy import desc
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.api.common import get_db
from app.auth import require_login from app.auth import require_login
from app.database import get_db
from app.models import FileRecord, ProcessingLog from app.models import FileRecord, ProcessingLog
# Set up logging # Set up logging
+1 -12
View File
@@ -11,7 +11,7 @@ from sqlalchemy.orm import Session # noqa: F401
from app.auth import require_login # noqa: F401 from app.auth import require_login # noqa: F401
from app.config import settings from app.config import settings
from app.database import SessionLocal from app.database import get_db # noqa: F401
# Set up Jinja2 templates # Set up Jinja2 templates
templates_dir = Path(__file__).parent.parent.parent / "frontend" / "templates" templates_dir = Path(__file__).parent.parent.parent / "frontend" / "templates"
@@ -39,14 +39,3 @@ templates.TemplateResponse = template_response_with_version
# Set up logging # Set up logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_db():
"""
Dependency to get a database session.
"""
db = SessionLocal()
try:
yield db
finally:
db.close()
+39 -56
View File
@@ -1,10 +1,12 @@
""" """
Pytest configuration and shared fixtures for DocuElevate tests. Pytest configuration and shared fixtures for DocuElevate tests.
""" """
import os import os
import tempfile import tempfile
import pytest
from typing import Generator from typing import Generator
import pytest
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
@@ -22,10 +24,11 @@ os.environ["WORKDIR"] = "/tmp"
os.environ["AUTH_ENABLED"] = "False" os.environ["AUTH_ENABLED"] = "False"
os.environ["SESSION_SECRET"] = "test_secret_key_for_testing_must_be_at_least_32_characters_long" os.environ["SESSION_SECRET"] = "test_secret_key_for_testing_must_be_at_least_32_characters_long"
from app.database import Base, get_db from app.database import Base # noqa: E402
from app.main import app as fastapi_app from app.main import app as fastapi_app # noqa: E402
# Import models to register them with SQLAlchemy Base # Import models to register them with SQLAlchemy Base
from app.models import DocumentMetadata, FileRecord, ProcessingLog from app.models import DocumentMetadata, FileRecord, ProcessingLog # noqa: F401, E402
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
@@ -44,14 +47,14 @@ def db_session():
connect_args={"check_same_thread": False}, connect_args={"check_same_thread": False},
poolclass=StaticPool, poolclass=StaticPool,
) )
# Create all tables # Create all tables
Base.metadata.create_all(bind=engine) Base.metadata.create_all(bind=engine)
# Create a session # Create a session
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
session = TestingSessionLocal() session = TestingSessionLocal()
try: try:
yield session yield session
finally: finally:
@@ -62,27 +65,24 @@ def db_session():
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def client(db_session) -> TestClient: def client(db_session) -> TestClient:
"""Create a test client with a fresh database.""" """Create a test client with a fresh database."""
# Import all the different get_db functions used across the app # Import the canonical get_db function
from app.api.common import get_db as api_get_db from app.database import get_db
from app.views.base import get_db as views_get_db
# Override the get_db dependency to use our test database # Override the get_db dependency to use our test database
def override_get_db(): def override_get_db():
try: try:
yield db_session yield db_session
finally: finally:
pass pass
# Override all variants of get_db # Override the single canonical get_db dependency
fastapi_app.dependency_overrides[get_db] = override_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 # Use base_url to satisfy TrustedHostMiddleware
with TestClient(fastapi_app, base_url="http://localhost") as test_client: with TestClient(fastapi_app, base_url="http://localhost") as test_client:
yield test_client yield test_client
# Clean up # Clean up
fastapi_app.dependency_overrides.clear() fastapi_app.dependency_overrides.clear()
@@ -91,7 +91,7 @@ def client(db_session) -> TestClient:
def sample_pdf_path(test_workdir) -> str: def sample_pdf_path(test_workdir) -> str:
"""Create a sample PDF file for testing.""" """Create a sample PDF file for testing."""
pdf_path = os.path.join(test_workdir, "test.pdf") pdf_path = os.path.join(test_workdir, "test.pdf")
# Create a minimal valid PDF # Create a minimal valid PDF
pdf_content = b"""%PDF-1.4 pdf_content = b"""%PDF-1.4
1 0 obj 1 0 obj
@@ -129,10 +129,10 @@ startxref
197 197
%%EOF %%EOF
""" """
with open(pdf_path, 'wb') as f: with open(pdf_path, "wb") as f:
f.write(pdf_content) f.write(pdf_content)
return pdf_path return pdf_path
@@ -140,10 +140,10 @@ startxref
def sample_text_file(test_workdir) -> str: def sample_text_file(test_workdir) -> str:
"""Create a sample text file for testing.""" """Create a sample text file for testing."""
text_path = os.path.join(test_workdir, "test.txt") text_path = os.path.join(test_workdir, "test.txt")
with open(text_path, 'w') as f: with open(text_path, "w") as f:
f.write("This is a test document.\nWith multiple lines.\n") f.write("This is a test document.\nWith multiple lines.\n")
return text_path return text_path
@@ -151,46 +151,29 @@ def sample_text_file(test_workdir) -> str:
def mock_openai_response(): def mock_openai_response():
"""Mock OpenAI API response for testing.""" """Mock OpenAI API response for testing."""
return { return {
"choices": [{ "choices": [
"message": { {
"content": '{"document_type": "invoice", "summary": "Test invoice", "tags": ["test", "invoice"]}' "message": {
"content": '{"document_type": "invoice", "summary": "Test invoice", "tags": ["test", "invoice"]}'
}
} }
}] ]
} }
@pytest.fixture @pytest.fixture
def mock_azure_response(): def mock_azure_response():
"""Mock Azure Document Intelligence API response for testing.""" """Mock Azure Document Intelligence API response for testing."""
return { return {"analyzeResult": {"content": "Test document content extracted by OCR", "pages": [{"pageNumber": 1}]}}
"analyzeResult": {
"content": "Test document content extracted by OCR",
"pages": [{"pageNumber": 1}]
}
}
# Markers for categorizing tests # Markers for categorizing tests
def pytest_configure(config): def pytest_configure(config):
"""Configure custom pytest markers.""" """Configure custom pytest markers."""
config.addinivalue_line( config.addinivalue_line("markers", "unit: Unit tests for individual functions/methods")
"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( config.addinivalue_line("markers", "security: Security-related tests")
"markers", "integration: Integration tests for API endpoints and workflows" config.addinivalue_line("markers", "requires_external: Tests requiring external services")
) config.addinivalue_line("markers", "requires_db: Tests requiring database")
config.addinivalue_line( config.addinivalue_line("markers", "requires_redis: Tests requiring Redis")
"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"
)