2025-11-05 11:31:19 +01:00
|
|
|
|
# tests/conftest.py
|
2025-11-05 12:41:56 +01:00
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
2025-11-05 11:31:19 +01:00
|
|
|
|
import asyncio
|
|
|
|
|
|
import sys
|
|
|
|
|
|
from typing import AsyncGenerator
|
2025-11-05 12:41:56 +01:00
|
|
|
|
from uuid import uuid4
|
2025-11-05 11:31:19 +01:00
|
|
|
|
|
|
|
|
|
|
import pytest
|
2025-11-05 12:41:56 +01:00
|
|
|
|
import pytest_asyncio
|
2025-11-05 11:31:19 +01:00
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
|
|
from httpx import AsyncClient, ASGITransport
|
|
|
|
|
|
from sqlalchemy import text
|
2025-11-05 12:41:56 +01:00
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker
|
|
|
|
|
|
|
2025-11-05 11:31:19 +01:00
|
|
|
|
load_dotenv()
|
|
|
|
|
|
|
|
|
|
|
|
from dataloader.api import app_main
|
|
|
|
|
|
from dataloader.config import APP_CONFIG
|
2025-11-05 12:41:56 +01:00
|
|
|
|
from dataloader.context import APP_CTX, get_session
|
|
|
|
|
|
from dataloader.storage.models import Base
|
|
|
|
|
|
from dataloader.storage.engine import create_engine, create_sessionmaker
|
2025-11-05 11:31:19 +01:00
|
|
|
|
|
|
|
|
|
|
if sys.platform == "win32":
|
|
|
|
|
|
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-11-05 12:41:56 +01:00
|
|
|
|
@pytest_asyncio.fixture(scope="function")
|
2025-11-05 11:31:19 +01:00
|
|
|
|
async def db_engine() -> AsyncGenerator[AsyncEngine, None]:
|
|
|
|
|
|
"""
|
2025-11-05 12:41:56 +01:00
|
|
|
|
Создаёт тестовый движок для теста.
|
|
|
|
|
|
Использует реальную БД из конфига.
|
2025-11-05 11:31:19 +01:00
|
|
|
|
"""
|
2025-11-05 12:41:56 +01:00
|
|
|
|
engine = create_engine(APP_CONFIG.pg.url)
|
2025-11-05 11:31:19 +01:00
|
|
|
|
|
|
|
|
|
|
yield engine
|
|
|
|
|
|
|
|
|
|
|
|
await engine.dispose()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-11-05 12:41:56 +01:00
|
|
|
|
|
|
|
|
|
|
@pytest_asyncio.fixture(scope="function")
|
|
|
|
|
|
async def db_session(db_engine: AsyncEngine) -> AsyncGenerator[AsyncSession, None]:
|
2025-11-05 11:31:19 +01:00
|
|
|
|
"""
|
2025-11-05 12:41:56 +01:00
|
|
|
|
Предоставляет сессию БД для каждого теста.
|
|
|
|
|
|
НЕ использует транзакцию, чтобы работали advisory locks.
|
2025-11-05 11:31:19 +01:00
|
|
|
|
"""
|
2025-11-05 12:41:56 +01:00
|
|
|
|
sessionmaker = async_sessionmaker(bind=db_engine, expire_on_commit=False, class_=AsyncSession)
|
|
|
|
|
|
async with sessionmaker() as session:
|
|
|
|
|
|
yield session
|
|
|
|
|
|
await session.rollback()
|
2025-11-05 11:31:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
2025-11-05 12:41:56 +01:00
|
|
|
|
@pytest_asyncio.fixture(scope="function")
|
|
|
|
|
|
async def clean_queue_tables(db_session: AsyncSession) -> None:
|
2025-11-05 11:31:19 +01:00
|
|
|
|
"""
|
2025-11-05 12:41:56 +01:00
|
|
|
|
Очищает таблицы очереди перед каждым тестом.
|
2025-11-05 11:31:19 +01:00
|
|
|
|
"""
|
2025-11-05 12:41:56 +01:00
|
|
|
|
schema = APP_CONFIG.pg.schema_queue
|
|
|
|
|
|
await db_session.execute(text(f"TRUNCATE TABLE {schema}.dl_job_events CASCADE"))
|
|
|
|
|
|
await db_session.execute(text(f"TRUNCATE TABLE {schema}.dl_jobs CASCADE"))
|
|
|
|
|
|
await db_session.commit()
|
2025-11-05 11:31:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
2025-11-05 12:41:56 +01:00
|
|
|
|
@pytest_asyncio.fixture
|
2025-11-05 11:31:19 +01:00
|
|
|
|
async def client(db_session: AsyncSession) -> AsyncGenerator[AsyncClient, None]:
|
|
|
|
|
|
"""
|
2025-11-05 12:41:56 +01:00
|
|
|
|
HTTP клиент для тестирования API.
|
2025-11-05 11:31:19 +01:00
|
|
|
|
"""
|
|
|
|
|
|
async def override_get_session() -> AsyncGenerator[AsyncSession, None]:
|
|
|
|
|
|
yield db_session
|
|
|
|
|
|
|
|
|
|
|
|
app_main.dependency_overrides[get_session] = override_get_session
|
|
|
|
|
|
|
|
|
|
|
|
transport = ASGITransport(app=app_main)
|
|
|
|
|
|
async with AsyncClient(transport=transport, base_url="http://test") as c:
|
|
|
|
|
|
yield c
|
|
|
|
|
|
|
|
|
|
|
|
app_main.dependency_overrides.clear()
|
2025-11-05 12:41:56 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
|
def job_id() -> str:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Генерирует уникальный job_id для тестов.
|
|
|
|
|
|
"""
|
|
|
|
|
|
return str(uuid4())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
|
def queue_name() -> str:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Возвращает имя тестовой очереди.
|
|
|
|
|
|
"""
|
|
|
|
|
|
return "test.queue"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
|
def task_name() -> str:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Возвращает имя тестовой задачи.
|
|
|
|
|
|
"""
|
|
|
|
|
|
return "test.task"
|