initial commit (backend)
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
"""
|
||||
Тесты для get_next_lesson.
|
||||
Проверяют приоритеты: overdue → weak → test → next.
|
||||
"""
|
||||
import pytest
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# ХЕЛПЕРЫ
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
async def set_user_level(conn, user_id: int, level: str):
|
||||
await conn.execute("UPDATE users SET current_level = $1 WHERE telegram_id = $2", level, user_id)
|
||||
|
||||
|
||||
async def insert_mastery(
|
||||
conn,
|
||||
user_id: int,
|
||||
skill_tag: str,
|
||||
mastery: float,
|
||||
retention: str = "fresh",
|
||||
attempts: int = 3,
|
||||
days_since: int = 0,
|
||||
):
|
||||
"""Вставляет запись в user_skill_mastery."""
|
||||
last_attempt = datetime.now() - timedelta(days=days_since)
|
||||
await conn.execute("""
|
||||
INSERT INTO user_skill_mastery (
|
||||
user_id, skill_tag, mastery_score, retention_status,
|
||||
attempts_count, last_attempt_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6)
|
||||
ON CONFLICT (user_id, skill_tag) DO UPDATE SET
|
||||
mastery_score = $3,
|
||||
retention_status = $4,
|
||||
attempts_count = $5,
|
||||
last_attempt_at = $6
|
||||
""", user_id, skill_tag, mastery, retention, attempts, last_attempt)
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# ТЕСТЫ
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
async def test_orchestrator_prioritizes_overdue(conn, test_user, create_test_lesson, clean_mastery):
|
||||
"""Если есть overdue навык — он выбирается первым."""
|
||||
from routers.orchestrator import get_next_lesson
|
||||
|
||||
await set_user_level(conn, test_user, "A2")
|
||||
await create_test_lesson("test_skill")
|
||||
await insert_mastery(conn, test_user, "test_skill", mastery=50.0, retention="overdue", days_since=20)
|
||||
|
||||
result = await get_next_lesson(conn, test_user)
|
||||
|
||||
assert result["type"] == "review"
|
||||
assert result["reason"] == "overdue"
|
||||
|
||||
|
||||
async def test_orchestrator_prioritizes_weak(conn, test_user, create_test_lesson, clean_mastery):
|
||||
"""Если нет overdue, но есть weak (mastery < 75) — выбирается он."""
|
||||
from routers.orchestrator import get_next_lesson
|
||||
|
||||
await set_user_level(conn, test_user, "A2")
|
||||
await create_test_lesson("test_skill")
|
||||
await insert_mastery(conn, test_user, "test_skill", mastery=50.0, retention="fresh", days_since=1)
|
||||
|
||||
result = await get_next_lesson(conn, test_user)
|
||||
|
||||
assert result["type"] == "review"
|
||||
assert result["reason"] == "weak_mastery"
|
||||
|
||||
|
||||
async def test_orchestrator_prefers_overdue_over_weak(conn, test_user, create_test_lesson, clean_mastery):
|
||||
"""Если есть и overdue, и weak — overdue важнее."""
|
||||
from routers.orchestrator import get_next_lesson
|
||||
|
||||
await set_user_level(conn, test_user, "A2")
|
||||
await create_test_lesson("weak_skill", order=1)
|
||||
await create_test_lesson("overdue_skill", order=2)
|
||||
|
||||
await insert_mastery(conn, test_user, "weak_skill", mastery=40.0, retention="fresh", days_since=1)
|
||||
await insert_mastery(conn, test_user, "overdue_skill", mastery=60.0, retention="overdue", days_since=20)
|
||||
|
||||
result = await get_next_lesson(conn, test_user)
|
||||
|
||||
assert result["reason"] == "overdue"
|
||||
|
||||
|
||||
async def test_orchestrator_weak_requires_min_attempts(conn, test_user, create_test_lesson, clean_mastery):
|
||||
"""Weak skill требует минимум 2 попытки — если меньше, он не выбирается."""
|
||||
from routers.orchestrator import get_next_lesson
|
||||
|
||||
await set_user_level(conn, test_user, "A2")
|
||||
await create_test_lesson("test_skill")
|
||||
await insert_mastery(conn, test_user, "test_skill", mastery=50.0, retention="fresh", attempts=1)
|
||||
|
||||
result = await get_next_lesson(conn, test_user)
|
||||
|
||||
assert result["reason"] != "weak_mastery"
|
||||
Reference in New Issue
Block a user