Add tests for authentication and utilities, and update dependencies
- Created a new test package for services and added tests for AuthService. - Implemented tests for user registration, login, and token creation. - Added a new test package for utilities and included tests for password and JWT utilities. - Updated `uv.lock` to include new dependencies: bcrypt, email-validator, pyjwt, and pytest-asyncio.
This commit is contained in:
212
tests/conftest.py
Normal file
212
tests/conftest.py
Normal file
@@ -0,0 +1,212 @@
|
||||
"""Test configuration and fixtures."""
|
||||
|
||||
import asyncio
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from fastapi import FastAPI
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlmodel import SQLModel, select
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.main import create_app
|
||||
from app.models.plan import Plan
|
||||
from app.models.user import User
|
||||
from app.utils.auth import JWTUtils, PasswordUtils
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def event_loop() -> Any:
|
||||
"""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_asyncio.fixture(scope="session")
|
||||
async def test_engine() -> Any:
|
||||
"""Create a test database engine."""
|
||||
# Use in-memory SQLite database for tests
|
||||
engine = create_async_engine(
|
||||
"sqlite+aiosqlite:///:memory:",
|
||||
echo=False,
|
||||
)
|
||||
|
||||
# Create all tables
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(SQLModel.metadata.create_all)
|
||||
|
||||
yield engine
|
||||
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_session(test_engine: Any) -> AsyncGenerator[AsyncSession, None]:
|
||||
"""Create a test database session."""
|
||||
connection = await test_engine.connect()
|
||||
transaction = await connection.begin()
|
||||
|
||||
session = AsyncSession(bind=connection)
|
||||
|
||||
try:
|
||||
yield session
|
||||
finally:
|
||||
await session.close()
|
||||
await transaction.rollback()
|
||||
await connection.close()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_app(test_session: AsyncSession) -> FastAPI:
|
||||
"""Create a test FastAPI application."""
|
||||
app = create_app()
|
||||
|
||||
# Override the database dependency
|
||||
async def override_get_db() -> AsyncGenerator[AsyncSession, None]:
|
||||
yield test_session
|
||||
|
||||
app.dependency_overrides[get_db] = override_get_db
|
||||
|
||||
return app
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_client(test_app: FastAPI) -> AsyncGenerator[AsyncClient, None]:
|
||||
"""Create a test HTTP client."""
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=test_app),
|
||||
base_url="http://test",
|
||||
) as client:
|
||||
yield client
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_plan(test_session: AsyncSession) -> Plan:
|
||||
"""Create a test plan."""
|
||||
# Check if plan already exists in this session
|
||||
existing_plan = await test_session.exec(select(Plan).where(Plan.code == "free"))
|
||||
plan = existing_plan.first()
|
||||
|
||||
if not plan:
|
||||
plan = Plan(
|
||||
code="free",
|
||||
name="Free Plan",
|
||||
description="Test free plan",
|
||||
credits=100,
|
||||
max_credits=100,
|
||||
)
|
||||
test_session.add(plan)
|
||||
await test_session.commit()
|
||||
await test_session.refresh(plan)
|
||||
|
||||
return plan
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_pro_plan(test_session: AsyncSession) -> Plan:
|
||||
"""Create a test pro plan."""
|
||||
# Check if plan already exists in this session
|
||||
existing_plan = await test_session.exec(select(Plan).where(Plan.code == "pro"))
|
||||
plan = existing_plan.first()
|
||||
|
||||
if not plan:
|
||||
plan = Plan(
|
||||
code="pro",
|
||||
name="Pro Plan",
|
||||
description="Test pro plan",
|
||||
credits=300,
|
||||
max_credits=300,
|
||||
)
|
||||
test_session.add(plan)
|
||||
await test_session.commit()
|
||||
await test_session.refresh(plan)
|
||||
|
||||
return plan
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_user(test_session: AsyncSession, test_plan: Plan) -> User:
|
||||
"""Create a test user."""
|
||||
user = User(
|
||||
email="test@example.com",
|
||||
name="Test User",
|
||||
password_hash=PasswordUtils.hash_password("testpassword123"),
|
||||
role="user",
|
||||
is_active=True,
|
||||
plan_id=test_plan.id,
|
||||
credits=100,
|
||||
)
|
||||
test_session.add(user)
|
||||
await test_session.commit()
|
||||
await test_session.refresh(user)
|
||||
return user
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def admin_user(test_session: AsyncSession, test_plan: Plan) -> User:
|
||||
"""Create a test admin user."""
|
||||
user = User(
|
||||
email="admin@example.com",
|
||||
name="Admin User",
|
||||
password_hash=PasswordUtils.hash_password("adminpassword123"),
|
||||
role="admin",
|
||||
is_active=True,
|
||||
plan_id=test_plan.id,
|
||||
credits=1000,
|
||||
)
|
||||
test_session.add(user)
|
||||
await test_session.commit()
|
||||
await test_session.refresh(user)
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_user_data() -> dict[str, Any]:
|
||||
"""Test user registration data."""
|
||||
return {
|
||||
"email": "newuser@example.com",
|
||||
"password": "newpassword123",
|
||||
"name": "New User",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_login_data() -> dict[str, str]:
|
||||
"""Test user login data."""
|
||||
return {
|
||||
"email": "test@example.com",
|
||||
"password": "testpassword123",
|
||||
}
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def auth_headers(test_user: User) -> dict[str, str]:
|
||||
"""Create authentication headers with JWT token."""
|
||||
token_data = {
|
||||
"sub": str(test_user.id),
|
||||
"email": test_user.email,
|
||||
"role": test_user.role,
|
||||
}
|
||||
|
||||
access_token = JWTUtils.create_access_token(token_data)
|
||||
|
||||
return {"Authorization": f"Bearer {access_token}"}
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def admin_headers(admin_user: User) -> dict[str, str]:
|
||||
"""Create admin authentication headers with JWT token."""
|
||||
token_data = {
|
||||
"sub": str(admin_user.id),
|
||||
"email": admin_user.email,
|
||||
"role": admin_user.role,
|
||||
}
|
||||
|
||||
access_token = JWTUtils.create_access_token(token_data)
|
||||
|
||||
return {"Authorization": f"Bearer {access_token}"}
|
||||
Reference in New Issue
Block a user