test: create pytest conftest with shared fixtures for backend tests

This commit is contained in:
2026-04-18 16:48:30 +00:00
parent cd1dd8ddff
commit be83262644
2 changed files with 156 additions and 25 deletions

View File

@@ -15,4 +15,5 @@ slowapi>=0.1.9
apscheduler>=3.10.1 apscheduler>=3.10.1
pytest>=8.0.0 pytest>=8.0.0
pytest-asyncio>=0.23.0 pytest-asyncio>=0.23.0
pytest-cov>=4.1.0
httpx>=0.27.0 httpx>=0.27.0

View File

@@ -1,14 +1,17 @@
import pytest import pytest
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
from sqlalchemy.pool import StaticPool from sqlalchemy.pool import StaticPool
from fastapi.testclient import TestClient
from unittest.mock import patch, MagicMock
from datetime import datetime, timezone
from backend.database import Base, get_db
from backend.main import app from backend.main import app
from backend.database import Base, get_db
from backend.models import User
from backend.config_manager import ConfigManager
from backend.auth import get_current_admin, TokenData from backend.auth import get_current_admin, TokenData
from datetime import datetime, timezone
# Use in-memory SQLite for tests # Use in-memory SQLite for tests
SQLALCHEMY_DATABASE_URL = "sqlite://" SQLALCHEMY_DATABASE_URL = "sqlite://"
@@ -20,8 +23,10 @@ engine = create_engine(
) )
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
@pytest.fixture(scope="function", autouse=True) @pytest.fixture(scope="function", autouse=True)
def mock_scheduler(): def mock_scheduler():
"""Shutdown scheduler before and after each test to avoid conflicts."""
from backend.scheduler import scheduler from backend.scheduler import scheduler
if scheduler.running: if scheduler.running:
scheduler.shutdown() scheduler.shutdown()
@@ -29,37 +34,162 @@ def mock_scheduler():
if scheduler.running: if scheduler.running:
scheduler.shutdown() scheduler.shutdown()
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def db(): def test_db():
"""Create in-memory SQLite database for tests."""
Base.metadata.create_all(bind=engine) Base.metadata.create_all(bind=engine)
session = TestingSessionLocal() session = TestingSessionLocal()
try: yield session
yield session session.close()
finally: Base.metadata.drop_all(bind=engine)
session.close()
Base.metadata.drop_all(bind=engine)
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def client(db): def test_client(test_db):
"""Create FastAPI test client with mocked database."""
def override_get_db(): def override_get_db():
try: try:
yield db yield test_db
finally: finally:
pass pass
# Mock admin user with valid TokenData
def override_get_current_admin():
return TokenData(
sub=1,
username="admin",
role="admin",
exp=datetime.now(timezone.utc)
)
app.dependency_overrides[get_db] = override_get_db app.dependency_overrides[get_db] = override_get_db
app.dependency_overrides[get_current_admin] = override_get_current_admin
client = TestClient(app)
with TestClient(app) as c: yield client
yield c
app.dependency_overrides.clear() app.dependency_overrides.clear()
@pytest.fixture(scope="function")
def mock_ldap():
"""Mock LDAP authentication."""
with patch("backend.auth.ldap3.Server") as mock_server, \
patch("backend.auth.ldap3.Connection") as mock_conn_class:
mock_conn = MagicMock()
mock_conn.bind.return_value = True
mock_conn.search.return_value = True
mock_conn.entries = [
MagicMock(entry_dn="uid=testuser,ou=people,dc=example,dc=com",
uid=["testuser"])
]
mock_conn_class.return_value = mock_conn
yield mock_conn
@pytest.fixture(scope="function")
def mock_gemini():
"""Mock Google Gemini AI extraction."""
with patch("backend.ai.gemini_extractor.generate_content") as mock_gen:
mock_response = MagicMock()
mock_response.text = '''{
"name": "Test Item",
"part_number": "PN-12345",
"category": "Electronics",
"quantity": 5
}'''
mock_gen.return_value = mock_response
yield mock_gen
@pytest.fixture(scope="function")
def mock_claude():
"""Mock Anthropic Claude AI extraction."""
with patch("backend.ai.claude_extractor.generate_content") as mock_gen:
mock_content = MagicMock()
mock_content.text = '''{
"name": "Test Item",
"part_number": "PN-12345",
"category": "Electronics",
"quantity": 5
}'''
mock_response = MagicMock()
mock_response.content = [mock_content]
mock_gen.return_value = mock_response
yield mock_gen
@pytest.fixture(scope="function")
def mock_config():
"""Mock ConfigManager."""
with patch.object(ConfigManager, "get_config") as mock_get:
mock_get.return_value = {
"ai_provider": "gemini",
"api_key": "test-key"
}
yield mock_get
@pytest.fixture(scope="function")
def admin_token(test_db):
"""Create a test admin user and return auth token."""
admin = User(
username="admin",
hashed_password="hashed_password",
role="admin",
origin="local"
)
test_db.add(admin)
test_db.commit()
test_db.refresh(admin)
# Return mocked admin token data
return TokenData(
sub=admin.id,
username="admin",
role="admin",
exp=datetime.now(timezone.utc)
)
@pytest.fixture(scope="function")
def user_token(test_db):
"""Create a test regular user and return auth token."""
user = User(
username="user",
hashed_password="hashed_password",
role="user",
origin="local"
)
test_db.add(user)
test_db.commit()
test_db.refresh(user)
# Return mocked user token data
return TokenData(
sub=user.id,
username="user",
role="user",
exp=datetime.now(timezone.utc)
)
@pytest.fixture(scope="function")
def admin_client(test_client, admin_token):
"""Create a test client authenticated as admin."""
def override_get_current_admin():
return admin_token
app.dependency_overrides[get_current_admin] = override_get_current_admin
yield test_client
app.dependency_overrides.pop(get_current_admin, None)
@pytest.fixture(scope="function")
def user_client(test_client, user_token):
"""Create a test client authenticated as regular user."""
from backend.auth import get_current_user
def override_get_current_user():
return user_token
app.dependency_overrides[get_current_user] = override_get_current_user
yield test_client
app.dependency_overrides.pop(get_current_user, None)