test: create pytest conftest with shared fixtures for backend tests
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user