""" Integration Test Configuration - ServiceManagerWeb Fixtures y utilidades para tests de integración con BD real """ import pytest import asyncio from typing import AsyncGenerator, Generator from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker from sqlalchemy.pool import NullPool from httpx import AsyncClient import uuid from app.main import app from app.core.database import Base, get_db from app.core.security import SecurityUtils from app.models.tenant import Tenant, TenantStatus from app.models.user import User, UserRole from app.models.system import System from app.models.category import Category # Database URL para testing (usa la misma BD pero limpia después) TEST_DATABASE_URL = "postgresql+asyncpg://servicemanager:servicemanager123@localhost:5432/servicemanager_test" @pytest.fixture(scope="session") def event_loop() -> Generator: """Create event loop for async tests.""" policy = asyncio.get_event_loop_policy() loop = policy.new_event_loop() yield loop loop.close() @pytest.fixture(scope="session") async def test_engine(): """Create test database engine.""" engine = create_async_engine( TEST_DATABASE_URL, echo=False, poolclass=NullPool, # No pool para tests ) # Crear todas las tablas async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) yield engine # Limpiar después de todos los tests async with engine.begin() as conn: await conn.run_sync(Base.metadata.drop_all) await engine.dispose() @pytest.fixture async def db_session(test_engine) -> AsyncGenerator[AsyncSession, None]: """Create a fresh database session for each test.""" async_session = async_sessionmaker( test_engine, class_=AsyncSession, expire_on_commit=False ) async with async_session() as session: async with session.begin(): yield session # Rollback para limpiar después del test await session.rollback() @pytest.fixture async def client(db_session: AsyncSession) -> AsyncGenerator[AsyncClient, None]: """Create test client with overridden database dependency.""" async def override_get_db(): yield db_session app.dependency_overrides[get_db] = override_get_db async with AsyncClient(app=app, base_url="http://test") as ac: yield ac app.dependency_overrides.clear() # =================================== # FIXTURES DE DATOS DE TEST # =================================== @pytest.fixture async def test_tenant(db_session: AsyncSession) -> Tenant: """Create a test tenant.""" tenant = Tenant( name="Test Company", slug="test-company", domain="test.company.com", status=TenantStatus.ACTIVE, email="admin@test.company.com", phone="+1234567890" ) db_session.add(tenant) await db_session.commit() await db_session.refresh(tenant) return tenant @pytest.fixture async def test_tenant_2(db_session: AsyncSession) -> Tenant: """Create a second test tenant for multi-tenant tests.""" tenant = Tenant( name="Test Company 2", slug="test-company-2", domain="test2.company.com", status=TenantStatus.ACTIVE, email="admin@test2.company.com", phone="+9876543210" ) db_session.add(tenant) await db_session.commit() await db_session.refresh(tenant) return tenant @pytest.fixture async def test_admin_user(db_session: AsyncSession, test_tenant: Tenant) -> User: """Create a test admin user.""" user = User( tenant_id=test_tenant.id, email="admin@test.com", first_name="Admin", last_name="User", password_hash=SecurityUtils.hash_password("AdminPass123!"), role=UserRole.ADMIN, is_active=True, email_verified=True ) db_session.add(user) await db_session.commit() await db_session.refresh(user) return user @pytest.fixture async def test_agent_user(db_session: AsyncSession, test_tenant: Tenant) -> User: """Create a test agent user.""" user = User( tenant_id=test_tenant.id, email="agent@test.com", first_name="Agent", last_name="User", password_hash=SecurityUtils.hash_password("AgentPass123!"), role=UserRole.AGENT, is_active=True, email_verified=True ) db_session.add(user) await db_session.commit() await db_session.refresh(user) return user @pytest.fixture async def test_client_user(db_session: AsyncSession, test_tenant: Tenant) -> User: """Create a test client user.""" user = User( tenant_id=test_tenant.id, email="client@test.com", first_name="Client", last_name="User", password_hash=SecurityUtils.hash_password("ClientPass123!"), role=UserRole.CLIENT_USER, is_active=True, email_verified=True ) db_session.add(user) await db_session.commit() await db_session.refresh(user) return user @pytest.fixture async def test_system(db_session: AsyncSession, test_tenant: Tenant) -> System: """Create a test system.""" system = System( tenant_id=test_tenant.id, name="Test System", code="TEST-SYS", description="Test system for integration tests", is_active=True ) db_session.add(system) await db_session.commit() await db_session.refresh(system) return system @pytest.fixture async def test_category(db_session: AsyncSession, test_tenant: Tenant, test_system: System) -> Category: """Create a test category.""" category = Category( tenant_id=test_tenant.id, system_id=test_system.id, name="Test Category", code="TEST-CAT", description="Test category for integration tests", is_active=True ) db_session.add(category) await db_session.commit() await db_session.refresh(category) return category # =================================== # FIXTURES DE AUTENTICACIÓN # =================================== @pytest.fixture async def admin_token(client: AsyncClient, test_admin_user: User, test_tenant: Tenant) -> str: """Get authentication token for admin user.""" response = await client.post( "/v1/auth/login", json={ "email": "admin@test.com", "password": "AdminPass123!", "tenant_slug": test_tenant.slug } ) assert response.status_code == 200 data = response.json() return data["access_token"] @pytest.fixture async def agent_token(client: AsyncClient, test_agent_user: User, test_tenant: Tenant) -> str: """Get authentication token for agent user.""" response = await client.post( "/v1/auth/login", json={ "email": "agent@test.com", "password": "AgentPass123!", "tenant_slug": test_tenant.slug } ) assert response.status_code == 200 data = response.json() return data["access_token"] @pytest.fixture async def client_token(client: AsyncClient, test_client_user: User, test_tenant: Tenant) -> str: """Get authentication token for client user.""" response = await client.post( "/v1/auth/login", json={ "email": "client@test.com", "password": "ClientPass123!", "tenant_slug": test_tenant.slug } ) assert response.status_code == 200 data = response.json() return data["access_token"] @pytest.fixture def auth_headers_admin(admin_token: str) -> dict: """Get authorization headers for admin user.""" return {"Authorization": f"Bearer {admin_token}"} @pytest.fixture def auth_headers_agent(agent_token: str) -> dict: """Get authorization headers for agent user.""" return {"Authorization": f"Bearer {agent_token}"} @pytest.fixture def auth_headers_client(client_token: str) -> dict: """Get authorization headers for client user.""" return {"Authorization": f"Bearer {client_token}"}