120 lines
5.0 KiB
Python
120 lines
5.0 KiB
Python
"""
|
|
Общие фикстуры для тестов Filwords PL.
|
|
Использует реальную БД (dev), но с изолированными тестовыми данными.
|
|
После каждого теста данные удаляются.
|
|
"""
|
|
import pytest
|
|
from config import DB_CONFIG
|
|
from database import get_pool, init_db
|
|
|
|
|
|
# Тестовые ID — чтобы не пересекаться с реальными данными
|
|
TEST_USER_ID = 999999999
|
|
TEST_TOPIC_ID = "test_topic_zzz"
|
|
|
|
|
|
@pytest.fixture(scope="session", autouse=True)
|
|
async def setup_database():
|
|
"""Один раз на сессию: инициализирует БД (миграции)."""
|
|
await init_db()
|
|
yield
|
|
|
|
|
|
@pytest.fixture
|
|
async def conn():
|
|
"""Соединение с БД для одного теста."""
|
|
pool = await get_pool()
|
|
async with pool.acquire() as connection:
|
|
yield connection
|
|
|
|
|
|
@pytest.fixture
|
|
async def test_user(conn):
|
|
"""Создаёт тестового юзера, удаляет после теста."""
|
|
await conn.execute("""
|
|
INSERT INTO users (telegram_id, username, app_language, current_level)
|
|
VALUES ($1, 'test_user', 'ru', 'A2')
|
|
ON CONFLICT (telegram_id) DO NOTHING
|
|
""", TEST_USER_ID)
|
|
|
|
yield TEST_USER_ID
|
|
|
|
# Cleanup — удаляем всё, что связано с юзером.
|
|
# ВАЖНО: порядок — сначала дочерние таблицы (FK), потом родительские.
|
|
await conn.execute("DELETE FROM user_skill_mastery WHERE user_id = $1", TEST_USER_ID)
|
|
await conn.execute("DELETE FROM user_learning_answers WHERE user_id = $1", TEST_USER_ID)
|
|
await conn.execute("DELETE FROM user_lesson_progress WHERE user_id = $1", TEST_USER_ID)
|
|
await conn.execute("DELETE FROM user_topic_progress WHERE user_id = $1", TEST_USER_ID)
|
|
await conn.execute("DELETE FROM user_achievements WHERE user_id = $1", TEST_USER_ID)
|
|
await conn.execute("DELETE FROM learning_sessions WHERE user_id = $1", TEST_USER_ID)
|
|
await conn.execute("DELETE FROM users WHERE telegram_id = $1", TEST_USER_ID)
|
|
|
|
|
|
@pytest.fixture
|
|
async def test_session(conn, test_user):
|
|
"""Тестовая сессия обучения (для FK в user_learning_answers)."""
|
|
row = await conn.fetchrow("""
|
|
INSERT INTO learning_sessions (user_id, level_code, topic_id, session_type)
|
|
VALUES ($1, 'A2', 'test_topic', 'lesson')
|
|
RETURNING id
|
|
""", test_user)
|
|
|
|
session_id = row["id"]
|
|
yield session_id
|
|
|
|
# 🔧 ФИКС: сначала удаляем ответы, ссылающиеся на сессию (FK),
|
|
# только потом саму сессию
|
|
await conn.execute("DELETE FROM user_learning_answers WHERE session_id = $1", session_id)
|
|
await conn.execute("DELETE FROM learning_sessions WHERE id = $1", session_id)
|
|
|
|
|
|
@pytest.fixture
|
|
async def test_topic(conn):
|
|
"""Создаёт тестовый топик, удаляет после теста."""
|
|
await conn.execute("""
|
|
INSERT INTO learning_topics (topic_id, level_code, order_number, name_pl, name_ru, name_uk, name_en)
|
|
VALUES ($1, 'A2', 999, 'Test Topic', 'Тестовый топик', 'Тестовий топік', 'Test Topic')
|
|
ON CONFLICT (topic_id) DO NOTHING
|
|
""", TEST_TOPIC_ID)
|
|
|
|
yield TEST_TOPIC_ID
|
|
|
|
await conn.execute("DELETE FROM learning_lessons WHERE topic_id = $1", TEST_TOPIC_ID)
|
|
await conn.execute("DELETE FROM learning_topics WHERE topic_id = $1", TEST_TOPIC_ID)
|
|
|
|
|
|
@pytest.fixture
|
|
async def create_test_lesson(conn, test_topic):
|
|
"""
|
|
Фабрика: создаёт урок с произвольным skill_tag.
|
|
Использование: await create_test_lesson("test_skill")
|
|
"""
|
|
created_ids = []
|
|
|
|
async def _create(skill_tag: str, order: int = 1) -> str:
|
|
lesson_id = f"test_lesson_{skill_tag}"
|
|
await conn.execute("""
|
|
INSERT INTO learning_lessons
|
|
(lesson_id, topic_id, order_number, skill_tag,
|
|
title_pl, title_ru, title_uk, title_en,
|
|
explanation_pl, explanation_ru, explanation_uk, explanation_en)
|
|
VALUES ($1, $2, $3, $4,
|
|
'Test Lesson', 'Тестовый урок', 'Тестовий урок', 'Test Lesson',
|
|
'Test', 'Test', 'Test', 'Test')
|
|
ON CONFLICT (lesson_id) DO NOTHING
|
|
""", lesson_id, test_topic, order, skill_tag)
|
|
created_ids.append(lesson_id)
|
|
return lesson_id
|
|
|
|
yield _create
|
|
|
|
for lid in created_ids:
|
|
await conn.execute("DELETE FROM learning_lessons WHERE lesson_id = $1", lid)
|
|
|
|
|
|
@pytest.fixture
|
|
async def clean_mastery(conn, test_user):
|
|
"""Очищает user_skill_mastery для тестового юзера до и после теста."""
|
|
await conn.execute("DELETE FROM user_skill_mastery WHERE user_id = $1", test_user)
|
|
yield
|
|
await conn.execute("DELETE FROM user_skill_mastery WHERE user_id = $1", test_user) |