initial commit (backend)

This commit is contained in:
2026-09-20 11:45:58 +00:00
commit 033adf023e
65 changed files with 28292 additions and 0 deletions
+120
View File
@@ -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)