import pytest import factory from httpx import ASGITransport, AsyncClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker import web.models # noqa: F401 — ensure all tables are registered on Base.metadata from web.auth.password import hash_password from web.database import Base, get_db from web.main import app from web.models.user import User, UserRoleEnum, UserStatusEnum # ── Database fixtures ───────────────────────────────────────────────────────── @pytest.fixture(scope="session") def engine(): eng = create_engine( "sqlite:///:memory:", echo=False, connect_args={"check_same_thread": False}, ) Base.metadata.create_all(eng) yield eng Base.metadata.drop_all(eng) @pytest.fixture def db_session(engine): connection = engine.connect() transaction = connection.begin() Session = sessionmaker(bind=connection) session = Session() yield session session.close() transaction.rollback() connection.close() @pytest.fixture def override_db(db_session): """Override FastAPI's get_db dependency with the transactional test session.""" app.dependency_overrides[get_db] = lambda: db_session yield db_session app.dependency_overrides.clear() # ── HTTP client ─────────────────────────────────────────────────────────────── @pytest.fixture async def client(override_db): async with AsyncClient( transport=ASGITransport(app=app), base_url="http://test", ) as c: yield c # ── Factories ───────────────────────────────────────────────────────────────── class UserFactory(factory.alchemy.SQLAlchemyModelFactory): class Meta: model = User sqlalchemy_session_persistence = "commit" first_name = factory.Faker("first_name") last_name = factory.Faker("last_name") email = factory.Sequence(lambda n: f"user{n}@test.com") phone = factory.Sequence(lambda n: f"+7900{n:07d}") password_hash = factory.LazyFunction(lambda: hash_password("testpass123")) status = UserStatusEnum.active is_email_confirmed = True role = UserRoleEnum.user @pytest.fixture def user_factory(db_session): UserFactory._meta.sqlalchemy_session = db_session return UserFactory @pytest.fixture def active_user(user_factory): return user_factory.create() @pytest.fixture def admin_user(user_factory): return user_factory.create(role=UserRoleEnum.admin) @pytest.fixture def system_user(user_factory): return user_factory.create(role=UserRoleEnum.system) @pytest.fixture def pending_user(user_factory): return user_factory.create( status=UserStatusEnum.pending, is_email_confirmed=False, password_hash=None, )