initial commit (backend)
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
Общие фикстуры для тестов 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)
|
||||
Reference in New Issue
Block a user