Files
fastapi-toolsets/tests/conftest.py
d3vyce 73fae04333 chore: cleanup before v1 (#73)
* chore: move dependencies module to the project root

* chore:update README

* chore: clean conftest

* chore: remove old code + comment

* fix: uv.lock dependencies
2026-02-19 11:49:57 +01:00

233 lines
5.6 KiB
Python

"""Shared pytest fixtures for fastapi-utils tests."""
import os
import uuid
import pytest
from pydantic import BaseModel
from sqlalchemy import Column, ForeignKey, String, Table, Uuid
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from fastapi_toolsets.crud import CrudFactory
DATABASE_URL = os.getenv(
key="DATABASE_URL",
default="postgresql+asyncpg://postgres:postgres@localhost:5432/postgres",
)
class Base(DeclarativeBase):
"""Base class for test models."""
pass
class Role(Base):
"""Test role model."""
__tablename__ = "roles"
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
name: Mapped[str] = mapped_column(String(50), unique=True)
users: Mapped[list["User"]] = relationship(back_populates="role")
class User(Base):
"""Test user model."""
__tablename__ = "users"
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
username: Mapped[str] = mapped_column(String(50), unique=True)
email: Mapped[str] = mapped_column(String(100), unique=True)
is_active: Mapped[bool] = mapped_column(default=True)
role_id: Mapped[uuid.UUID | None] = mapped_column(
ForeignKey("roles.id"), nullable=True
)
role: Mapped[Role | None] = relationship(back_populates="users")
class Tag(Base):
"""Test tag model."""
__tablename__ = "tags"
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
name: Mapped[str] = mapped_column(String(50), unique=True)
post_tags = Table(
"post_tags",
Base.metadata,
Column(
"post_id", Uuid, ForeignKey("posts.id", ondelete="CASCADE"), primary_key=True
),
Column("tag_id", Uuid, ForeignKey("tags.id", ondelete="CASCADE"), primary_key=True),
)
class Post(Base):
"""Test post model."""
__tablename__ = "posts"
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
title: Mapped[str] = mapped_column(String(200))
content: Mapped[str] = mapped_column(String(1000), default="")
is_published: Mapped[bool] = mapped_column(default=False)
author_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("users.id"))
tags: Mapped[list[Tag]] = relationship(secondary=post_tags)
class RoleCreate(BaseModel):
"""Schema for creating a role."""
id: uuid.UUID | None = None
name: str
class RoleUpdate(BaseModel):
"""Schema for updating a role."""
name: str | None = None
class UserCreate(BaseModel):
"""Schema for creating a user."""
id: uuid.UUID | None = None
username: str
email: str
is_active: bool = True
role_id: uuid.UUID | None = None
class UserUpdate(BaseModel):
"""Schema for updating a user."""
username: str | None = None
email: str | None = None
is_active: bool | None = None
role_id: uuid.UUID | None = None
class TagCreate(BaseModel):
"""Schema for creating a tag."""
id: uuid.UUID | None = None
name: str
class PostCreate(BaseModel):
"""Schema for creating a post."""
id: uuid.UUID | None = None
title: str
content: str = ""
is_published: bool = False
author_id: uuid.UUID
class PostUpdate(BaseModel):
"""Schema for updating a post."""
title: str | None = None
content: str | None = None
is_published: bool | None = None
class PostM2MCreate(BaseModel):
"""Schema for creating a post with M2M tag IDs."""
id: uuid.UUID | None = None
title: str
content: str = ""
is_published: bool = False
author_id: uuid.UUID
tag_ids: list[uuid.UUID] = []
class PostM2MUpdate(BaseModel):
"""Schema for updating a post with M2M tag IDs."""
title: str | None = None
content: str | None = None
is_published: bool | None = None
tag_ids: list[uuid.UUID] | None = None
RoleCrud = CrudFactory(Role)
UserCrud = CrudFactory(User)
PostCrud = CrudFactory(Post)
TagCrud = CrudFactory(Tag)
PostM2MCrud = CrudFactory(Post, m2m_fields={"tag_ids": Post.tags})
@pytest.fixture
def anyio_backend():
"""Use asyncio for async tests."""
return "asyncio"
@pytest.fixture(scope="function")
async def engine():
"""Create a PostgreSQL test database engine."""
engine = create_async_engine(DATABASE_URL, echo=False)
yield engine
await engine.dispose()
@pytest.fixture(scope="function")
async def db_session(engine):
"""Create a test database session with tables.
Creates all tables before the test and drops them after.
Each test gets a clean database state.
"""
# Create tables
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
# Create session
session_factory = async_sessionmaker(engine, expire_on_commit=False)
session = session_factory()
try:
yield session
finally:
await session.close()
# Drop tables after test
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
@pytest.fixture
def sample_role_data() -> RoleCreate:
"""Sample role creation data."""
return RoleCreate(name="admin")
@pytest.fixture
def sample_user_data() -> UserCreate:
"""Sample user creation data."""
return UserCreate(
username="testuser",
email="test@example.com",
is_active=True,
)
@pytest.fixture
def sample_post_data() -> PostCreate:
"""Sample post creation data."""
return PostCreate(
title="Test Post",
content="Test content",
is_published=True,
author_id=uuid.uuid4(),
)