Files
job-forge/tests/conftest.py
2025-08-02 20:51:59 -04:00

188 lines
5.7 KiB
Python

"""
Updated test configuration for Job Forge that matches the actual project structure
"""
import pytest
import asyncio
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
from fastapi.testclient import TestClient
from httpx import AsyncClient
import os
from typing import AsyncGenerator
from unittest.mock import AsyncMock, Mock
import uuid
# Fix import paths to match actual structure
from src.backend.main import app
from src.backend.core.database import get_db, Base
from src.backend.models.user import User
from src.backend.api.auth import create_access_token, hash_password
# Test database URL (use a separate test database)
TEST_DATABASE_URL = os.getenv(
"TEST_DATABASE_URL",
"postgresql+asyncpg://jobforge_user:jobforge_password@localhost:5432/jobforge_test"
)
# Test engine
test_engine = create_async_engine(TEST_DATABASE_URL, echo=False)
TestSessionLocal = sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False)
@pytest.fixture(scope="session")
def event_loop():
"""Create an instance of the default event loop for the test session."""
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()
@pytest.fixture(scope="session")
async def setup_test_db():
"""Set up test database tables."""
# Create all tables
async with test_engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await conn.run_sync(Base.metadata.create_all)
yield
# Cleanup
async with test_engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
@pytest.fixture
async def test_db(setup_test_db) -> AsyncGenerator[AsyncSession, None]:
"""Create a test database session."""
async with TestSessionLocal() as session:
try:
yield session
finally:
await session.rollback()
@pytest.fixture
def override_get_db(test_db: AsyncSession):
"""Override the get_db dependency for testing."""
def _override_get_db():
return test_db
app.dependency_overrides[get_db] = _override_get_db
yield
app.dependency_overrides.clear()
@pytest.fixture
def test_client(override_get_db):
"""Create a test client."""
with TestClient(app) as client:
yield client
@pytest.fixture
async def async_client(override_get_db):
"""Create an async test client."""
async with AsyncClient(app=app, base_url="http://test") as client:
yield client
@pytest.fixture
async def test_user(test_db: AsyncSession):
"""Create a test user."""
user = User(
email="test@jobforge.com",
password_hash=hash_password("testpassword123"),
full_name="Test User"
)
test_db.add(user)
await test_db.commit()
await test_db.refresh(user)
return user
@pytest.fixture
def test_user_token(test_user):
"""Create a JWT token for test user."""
token_data = {"sub": str(test_user.id)}
return create_access_token(data=token_data)
@pytest.fixture
def mock_ai_service():
"""Mock AI service for testing."""
mock = Mock()
mock.generate_cover_letter = AsyncMock(return_value={
"content": "Dear Hiring Manager,\n\nI am writing to express my interest...\n\nBest regards,\nTest User",
"model_used": "mock-ai",
"prompt": "Mock prompt for testing"
})
mock.generate_resume_optimization = AsyncMock(return_value={
"content": "Optimized Resume\n\nTest User\nSoftware Engineer\n\nExperience optimized for target role...",
"model_used": "mock-ai",
"prompt": "Mock resume optimization prompt"
})
return mock
@pytest.fixture
async def multiple_test_users(test_db: AsyncSession):
"""Create multiple test users for testing."""
users = []
for i in range(3):
user = User(
email=f"user{i}@test.com",
password_hash=hash_password("password123"),
full_name=f"User {i} Test"
)
test_db.add(user)
users.append(user)
await test_db.commit()
for user in users:
await test_db.refresh(user)
return users
# Test data factories
class TestDataFactory:
"""Factory for creating test data."""
@staticmethod
def user_data(email: str = None, **kwargs):
"""Create user test data."""
return {
"email": email or "test@example.com",
"password": "testpassword123",
"first_name": "Test",
"last_name": "User",
**kwargs
}
@staticmethod
def cover_letter_request(**kwargs):
"""Create cover letter request data."""
return {
"job_description": "We are looking for a Software Engineer with Python experience",
"company_name": "Test Company",
"role_title": "Software Engineer",
**kwargs
}
@staticmethod
def resume_optimization_request(**kwargs):
"""Create resume optimization request data."""
return {
"current_resume": "John Doe\nSoftware Engineer\n\nExperience:\n- Python development\n- Web applications",
"job_description": "Senior Python Developer role requiring FastAPI experience",
"role_title": "Senior Python Developer",
**kwargs
}
# Helper functions
async def create_test_user_and_token(db: AsyncSession, email: str):
"""Helper to create a user and return auth token."""
user = User(
email=email,
password_hash=hash_password("password123"),
full_name="Test User"
)
db.add(user)
await db.commit()
await db.refresh(user)
token_data = {"sub": str(user.id)}
token = create_access_token(data=token_data)
return user, token