from collections.abc import Generator import pytest from fastapi.testclient import TestClient from sqlalchemy import create_engine, event, select from sqlalchemy.orm import Session, sessionmaker from sqlalchemy.pool import StaticPool from app.core.database import Base, get_db from app.core.security import hash_password from app.main import app from app.models import LeadSource, LeadType, Pipeline, Stage, Tenant, User TEST_PASSWORD = "StrongPassword123!" engine = create_engine( "sqlite+pysqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool, ) TestingSession = sessionmaker(bind=engine, autoflush=False, expire_on_commit=False) @event.listens_for(engine, "connect") def enable_sqlite_foreign_keys(connection, _record) -> None: cursor = connection.cursor() cursor.execute("PRAGMA foreign_keys=ON") cursor.close() @pytest.fixture() def db() -> Generator[Session, None, None]: Base.metadata.create_all(bind=engine) database = TestingSession() try: seed_test_workspace(database) yield database finally: database.close() Base.metadata.drop_all(bind=engine) def seed_test_workspace(db: Session) -> None: tenant = Tenant(slug="maskan", name="Maskan Technologies") db.add(tenant) db.flush() owner = User( tenant_id=tenant.id, email="owner@example.com", full_name="Test Owner", password_hash=hash_password(TEST_PASSWORD), role="owner", ) viewer = User( tenant_id=tenant.id, email="viewer@example.com", full_name="Test Viewer", password_hash=hash_password(TEST_PASSWORD), role="viewer", ) db.add_all([owner, viewer]) pipeline = Pipeline(tenant_id=tenant.id, name="Sales Pipeline", is_default=True) db.add(pipeline) db.flush() db.add_all( [ Stage( tenant_id=tenant.id, pipeline_id=pipeline.id, name="New", position=1, probability=10, color="#64748b", ), Stage( tenant_id=tenant.id, pipeline_id=pipeline.id, name="Qualified", position=2, probability=40, color="#2563eb", ), LeadSource(tenant_id=tenant.id, name="LinkedIn"), LeadType(tenant_id=tenant.id, name="New Business"), ], ) db.commit() @pytest.fixture() def client(db: Session) -> Generator[TestClient, None, None]: def override_db(): yield db app.dependency_overrides[get_db] = override_db with TestClient(app) as test_client: yield test_client app.dependency_overrides.clear() def login(client: TestClient, email: str = "owner@example.com") -> dict[str, str]: response = client.post( "/api/v1/auth/login", json={ "workspace": "maskan", "email": email, "password": TEST_PASSWORD, }, ) assert response.status_code == 200, response.text return {"Authorization": f"Bearer {response.json()['access_token']}"} @pytest.fixture() def auth_headers(client: TestClient) -> dict[str, str]: return login(client) @pytest.fixture() def viewer_headers(client: TestClient) -> dict[str, str]: return login(client, "viewer@example.com") @pytest.fixture() def pipeline_ids(db: Session) -> tuple[str, str, str]: pipeline = db.scalar(select(Pipeline).where(Pipeline.is_default.is_(True))) stages = db.scalars( select(Stage).where(Stage.pipeline_id == pipeline.id).order_by(Stage.position), ).all() return pipeline.id, stages[0].id, stages[1].id