""" Общие фикстуры для тестов 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)