feature: create multitasking

This commit is contained in:
Daniil
2026-02-04 02:19:50 +03:00
parent 67e0f22b4f
commit a25bf623ea
24 changed files with 5227 additions and 21 deletions
+217 -17
View File
@@ -4,12 +4,22 @@ Shared test fixtures and configuration.
from __future__ import annotations
import pytest # type: ignore[import-not-found]
from fastapi.testclient import TestClient
import uuid
from datetime import timedelta
from typing import AsyncGenerator
from unittest.mock import AsyncMock, MagicMock
import pytest
from httpx import ASGITransport, AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from cpv3.db.base import Base
from cpv3.db.session import get_db
from cpv3.infrastructure.auth import get_current_user
from cpv3.infrastructure.deps import get_storage
from cpv3.infrastructure.security import create_token, hash_password
from cpv3.main import app
from cpv3.modules.users.models import User
# Use in-memory SQLite for tests (or configure a test database)
@@ -17,28 +27,218 @@ TEST_DATABASE_URL = "sqlite+aiosqlite:///:memory:"
@pytest.fixture
def test_client():
"""Create a test client for the FastAPI app."""
with TestClient(app) as client:
yield client
async def test_engine():
"""Create a test database engine with tables."""
engine = create_async_engine(TEST_DATABASE_URL, echo=False)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield engine
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await engine.dispose()
@pytest.fixture
async def test_db_session():
"""Create a test database session."""
engine = create_async_engine(TEST_DATABASE_URL, echo=False)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async def test_db_session(test_engine) -> AsyncGenerator[AsyncSession, None]:
"""Create a test database session with per-test transaction isolation."""
async_session = async_sessionmaker(
bind=engine, class_=AsyncSession, expire_on_commit=False
bind=test_engine, class_=AsyncSession, expire_on_commit=False
)
async with async_session() as session:
yield session
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await engine.dispose()
@pytest.fixture
async def test_user(test_db_session: AsyncSession) -> User:
"""Create a regular test user."""
user = User(
id=uuid.uuid4(),
username="testuser",
email="test@example.com",
password_hash=hash_password("testpassword"),
first_name="Test",
last_name="User",
is_active=True,
is_staff=False,
is_superuser=False,
)
test_db_session.add(user)
await test_db_session.commit()
await test_db_session.refresh(user)
return user
@pytest.fixture
async def staff_user(test_db_session: AsyncSession) -> User:
"""Create a staff test user."""
user = User(
id=uuid.uuid4(),
username="staffuser",
email="staff@example.com",
password_hash=hash_password("staffpassword"),
first_name="Staff",
last_name="User",
is_active=True,
is_staff=True,
is_superuser=False,
)
test_db_session.add(user)
await test_db_session.commit()
await test_db_session.refresh(user)
return user
@pytest.fixture
async def other_user(test_db_session: AsyncSession) -> User:
"""Create another regular user for permission testing."""
user = User(
id=uuid.uuid4(),
username="otheruser",
email="other@example.com",
password_hash=hash_password("otherpassword"),
first_name="Other",
last_name="User",
is_active=True,
is_staff=False,
is_superuser=False,
)
test_db_session.add(user)
await test_db_session.commit()
await test_db_session.refresh(user)
return user
@pytest.fixture
def auth_headers(test_user: User) -> dict[str, str]:
"""Generate auth headers with valid JWT for the test user."""
token = create_token(
subject=str(test_user.id),
token_type="access",
expires_in=timedelta(hours=1),
)
return {"Authorization": f"Bearer {token}"}
@pytest.fixture
def staff_auth_headers(staff_user: User) -> dict[str, str]:
"""Generate auth headers with valid JWT for the staff user."""
token = create_token(
subject=str(staff_user.id),
token_type="access",
expires_in=timedelta(hours=1),
)
return {"Authorization": f"Bearer {token}"}
@pytest.fixture
def other_auth_headers(other_user: User) -> dict[str, str]:
"""Generate auth headers with valid JWT for the other user."""
token = create_token(
subject=str(other_user.id),
token_type="access",
expires_in=timedelta(hours=1),
)
return {"Authorization": f"Bearer {token}"}
@pytest.fixture
def mock_storage() -> MagicMock:
"""Create a mock storage service."""
storage = MagicMock()
storage.upload_fileobj = AsyncMock(return_value="uploads/test-file.txt")
storage.exists = AsyncMock(return_value=True)
file_info = MagicMock()
file_info.file_path = "uploads/test-file.txt"
file_info.file_url = "http://example.com/uploads/test-file.txt"
file_info.file_size = 1024
file_info.filename = "test-file.txt"
storage.get_file_info = AsyncMock(return_value=file_info)
return storage
@pytest.fixture
async def async_client(
test_db_session: AsyncSession,
mock_storage: MagicMock,
) -> AsyncGenerator[AsyncClient, None]:
"""Create async test client with dependency overrides (no auth override)."""
async def override_get_db():
yield test_db_session
async def override_get_storage():
return mock_storage
app.dependency_overrides[get_db] = override_get_db
app.dependency_overrides[get_storage] = override_get_storage
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test",
) as client:
yield client
app.dependency_overrides.clear()
@pytest.fixture
async def auth_client(
test_db_session: AsyncSession,
test_user: User,
mock_storage: MagicMock,
) -> AsyncGenerator[AsyncClient, None]:
"""Create async test client with auth dependency overridden to return test_user."""
async def override_get_db():
yield test_db_session
def override_get_current_user():
return test_user
async def override_get_storage():
return mock_storage
app.dependency_overrides[get_db] = override_get_db
app.dependency_overrides[get_current_user] = override_get_current_user
app.dependency_overrides[get_storage] = override_get_storage
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test",
) as client:
yield client
app.dependency_overrides.clear()
@pytest.fixture
async def staff_client(
test_db_session: AsyncSession,
staff_user: User,
mock_storage: MagicMock,
) -> AsyncGenerator[AsyncClient, None]:
"""Create async test client with auth dependency overridden to return staff_user."""
async def override_get_db():
yield test_db_session
def override_get_current_user():
return staff_user
async def override_get_storage():
return mock_storage
app.dependency_overrides[get_db] = override_get_db
app.dependency_overrides[get_current_user] = override_get_current_user
app.dependency_overrides[get_storage] = override_get_storage
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test",
) as client:
yield client
app.dependency_overrides.clear()