""" 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