# Test configuration for Job Forge import pytest import asyncio import asyncpg 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 from app.main import app from app.core.database import get_db, Base from app.core.security import create_access_token from app.models.user import User from app.models.application import Application # Test database URL TEST_DATABASE_URL = os.getenv( "TEST_DATABASE_URL", "postgresql+asyncpg://jobforge:jobforge123@localhost:5432/jobforge_test" ) # Test engine and session factory 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) # Enable RLS and create policies await conn.execute(""" ALTER TABLE applications ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS applications_user_isolation ON applications; CREATE POLICY applications_user_isolation ON applications FOR ALL TO authenticated USING (user_id = current_setting('app.current_user_id')::UUID); -- Create vector extension if needed CREATE EXTENSION IF NOT EXISTS vector; """) 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.""" from app.crud.user import create_user from app.schemas.user import UserCreate user_data = UserCreate( email="test@jobforge.com", password="testpassword123", first_name="Test", last_name="User" ) user = await create_user(test_db, user_data) await test_db.commit() return user @pytest.fixture def test_user_token(test_user): """Create a JWT token for test user.""" token_data = {"sub": str(test_user.id), "email": test_user.email} return create_access_token(data=token_data) @pytest.fixture async def test_application(test_db: AsyncSession, test_user): """Create a test job application.""" from app.crud.application import create_application from app.schemas.application import ApplicationCreate app_data = ApplicationCreate( company_name="Test Corp", role_title="Software Developer", job_description="Python developer position with FastAPI experience", status="draft" ) application = await create_application(test_db, app_data, test_user.id) await test_db.commit() return application @pytest.fixture def mock_claude_service(): """Mock Claude AI service.""" mock = AsyncMock() mock.generate_cover_letter.return_value = """ Dear Hiring Manager, I am writing to express my strong interest in the Software Developer position at Test Corp. With my experience in Python development and FastAPI expertise, I am confident I would be a valuable addition to your team. Thank you for your consideration. Sincerely, Test User """ return mock @pytest.fixture def mock_openai_service(): """Mock OpenAI service.""" mock = AsyncMock() mock.create_embedding.return_value = [0.1] * 1536 # Mock embedding vector mock.test_connection.return_value = True return mock @pytest.fixture async def multiple_test_users(test_db: AsyncSession): """Create multiple test users for isolation testing.""" from app.crud.user import create_user from app.schemas.user import UserCreate users = [] for i in range(3): user_data = UserCreate( email=f"user{i}@test.com", password="password123", first_name=f"User{i}", last_name="Test" ) user = await create_user(test_db, user_data) users.append(user) await test_db.commit() return users @pytest.fixture async def applications_for_users(test_db: AsyncSession, multiple_test_users): """Create applications for multiple users to test isolation.""" from app.crud.application import create_application from app.schemas.application import ApplicationCreate all_applications = [] for i, user in enumerate(multiple_test_users): for j in range(2): # 2 applications per user app_data = ApplicationCreate( company_name=f"Company{i}-{j}", role_title=f"Role{i}-{j}", job_description=f"Job description for user {i}, application {j}", status="draft" ) application = await create_application(test_db, app_data, user.id) all_applications.append(application) await test_db.commit() return all_applications # 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 application_data(company_name: str = None, **kwargs): """Create application test data.""" return { "company_name": company_name or "Test Company", "role_title": "Software Developer", "job_description": "Python developer position", "status": "draft", **kwargs } @staticmethod def ai_response(): """Create mock AI response.""" return """ Dear Hiring Manager, I am excited to apply for this position. My background in software development and passion for technology make me an ideal candidate. Best regards, Test User """ # Database utilities for testing async def create_test_user_and_token(db: AsyncSession, email: str): """Helper to create a user and return auth token.""" from app.crud.user import create_user from app.schemas.user import UserCreate user_data = UserCreate( email=email, password="password123", first_name="Test", last_name="User" ) user = await create_user(db, user_data) await db.commit() token_data = {"sub": str(user.id), "email": user.email} token = create_access_token(data=token_data) return user, token async def set_rls_context(db: AsyncSession, user_id: str): """Set RLS context for testing multi-tenancy.""" await db.execute(f"SET app.current_user_id = '{user_id}'") # Performance testing helpers @pytest.fixture def benchmark_db_operations(): """Benchmark database operations.""" import time class BenchmarkContext: def __init__(self): self.start_time = None self.end_time = None def __enter__(self): self.start_time = time.time() return self def __exit__(self, *args): self.end_time = time.time() @property def duration(self): return self.end_time - self.start_time if self.end_time else None return BenchmarkContext