"""Геймификация (ТЗ 3.13): XP за закрытие задач, уровни, идемпотентность."""
from typing import Any
from fastapi.testclient import TestClient
def _create_task(client: TestClient, **patch: Any) -> int:
resp = client.post("/api/tasks", json={"title": "Задача для XP"})
task_id = resp.json()["id"]
if patch:
resp2 = client.patch(f"/api/tasks/{task_id}", json=patch)
assert resp2.status_code == 200
return task_id
def test_xp_granted_on_done(client: TestClient) -> None:
task_id = _create_task(client)
resp = client.patch(f"/api/tasks/{task_id}", json={"status": "done"})
assert resp.status_code == 200
# база 10, без приоритета и оценки — только база
assert resp.headers.get("X-Earned-XP") == "10"
summary = client.get("/api/xp").json()
assert summary["total_xp"] == 10
assert summary["level"] == 1
assert summary["completed_count"] == 1
def test_xp_weighted_by_priority_and_estimate(client: TestClient) -> None:
task_id = _create_task(client)
client.patch(
f"/api/tasks/{task_id}", json={"priority": 9, "estimated_minutes": 480}
)
resp = client.patch(f"/api/tasks/{task_id}", json={"status": "done"})
# 10 + urgent 15 + >4ч 15 = 40
assert resp.headers.get("X-Earned-XP") == "40"
def test_xp_granted_once(client: TestClient) -> None:
task_id = _create_task(client)
client.patch(f"/api/tasks/{task_id}", json={"status": "done"})
# возврат в работу и повторное закрытие — XP не растёт
client.patch(f"/api/tasks/{task_id}", json={"status": "in_progress"})
resp = client.patch(f"/api/tasks/{task_id}", json={"status": "done"})
assert "X-Earned-XP" not in resp.headers
assert client.get("/api/xp").json()["total_xp"] == 10
def test_deleting_task_keeps_xp(client: TestClient) -> None:
task_id = _create_task(client)
client.patch(f"/api/tasks/{task_id}", json={"status": "done"})
client.delete(f"/api/tasks/{task_id}")
assert client.get("/api/xp").json()["total_xp"] == 10
def test_level_progression(client: TestClient) -> None:
# 9 закрытий по 10 XP = 90 — уровень 1; 10 закрытий = 100 — уровень 2
for _ in range(9):
task_id = _create_task(client)
client.patch(f"/api/tasks/{task_id}", json={"status": "done"})
assert client.get("/api/xp").json()["level"] == 1
task_id = _create_task(client)
client.patch(f"/api/tasks/{task_id}", json={"status": "done"})
summary = client.get("/api/xp").json()
assert summary["level"] == 2
assert summary["level_xp"] == 0
assert summary["level_span"] == 200
assert summary["level_percent"] == 0