kin/tests/test_kin_ui_015_regression.py
2026-03-18 15:52:27 +02:00

139 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Regression tests for KIN-UI-015 — GET /api/tasks endpoint.
New endpoint: GET /api/tasks?status=...&limit=...&sort=...
Acceptance criteria:
- Эндпоинт существует и возвращает корректные данные
- Фильтрация по status работает корректно
- Параметр limit ограничивает количество результатов
- Параметр sort задаёт порядок сортировки
- Невалидный sort-field фоллбэчит на updated_at (защита от SQL-инъекций)
Coverage:
(1) GET /api/tasks возвращает все задачи проекта
(2) GET /api/tasks?status=completed возвращает только completed
(3) GET /api/tasks?status=completed не возвращает задачи с другим статусом
(4) GET /api/tasks?limit=1 возвращает не более 1 результата
(5) GET /api/tasks?sort=priority — сортировка по priority работает
(6) GET /api/tasks?sort=invalid_field — фоллбэк на updated_at (нет 500-ошибки)
(7) GET /api/tasks?status=completed&limit=20&sort=updated_at — все параметры вместе
(8) GET /api/tasks возвращает 200 при пустой базе
"""
import pytest
import web.api as api_module
from fastapi.testclient import TestClient
from core.db import init_db
from core import models
@pytest.fixture
def client(tmp_path):
db_path = tmp_path / "test.db"
api_module.DB_PATH = db_path
from web.api import app
c = TestClient(app)
c.post("/api/projects", json={"id": "p1", "name": "P1", "path": "/tmp/p1"})
return c
@pytest.fixture
def client_with_tasks(client, tmp_path):
"""Client pre-seeded with tasks of various statuses."""
conn = init_db(api_module.DB_PATH)
models.create_task(conn, "P1-001", "p1", "Pending task", status="pending")
models.create_task(conn, "P1-002", "p1", "Completed task 1", status="completed")
models.create_task(conn, "P1-003", "p1", "Completed task 2", status="completed")
models.create_task(conn, "P1-004", "p1", "In-progress task", status="in_progress")
conn.close()
return client
# ---------------------------------------------------------------------------
# (1) GET /api/tasks возвращает все задачи
# ---------------------------------------------------------------------------
def test_list_tasks_returns_all(client_with_tasks):
r = client_with_tasks.get("/api/tasks")
assert r.status_code == 200
tasks = r.json()
assert len(tasks) == 4
# ---------------------------------------------------------------------------
# (2) GET /api/tasks?status=completed возвращает только completed
# ---------------------------------------------------------------------------
def test_list_tasks_filter_by_status_completed(client_with_tasks):
r = client_with_tasks.get("/api/tasks?status=completed")
assert r.status_code == 200
tasks = r.json()
assert len(tasks) == 2
assert all(t["status"] == "completed" for t in tasks)
# ---------------------------------------------------------------------------
# (3) Другие статусы не попадают в фильтр status=completed
# ---------------------------------------------------------------------------
def test_list_tasks_filter_excludes_other_statuses(client_with_tasks):
r = client_with_tasks.get("/api/tasks?status=completed")
assert r.status_code == 200
tasks = r.json()
titles = [t["title"] for t in tasks]
assert "Pending task" not in titles
assert "In-progress task" not in titles
# ---------------------------------------------------------------------------
# (4) GET /api/tasks?limit=1 возвращает не более 1 результата
# ---------------------------------------------------------------------------
def test_list_tasks_limit_respected(client_with_tasks):
r = client_with_tasks.get("/api/tasks?limit=1")
assert r.status_code == 200
tasks = r.json()
assert len(tasks) == 1
# ---------------------------------------------------------------------------
# (5) GET /api/tasks?sort=priority — сортировка работает (нет 500-ошибки)
# ---------------------------------------------------------------------------
def test_list_tasks_sort_by_priority(client_with_tasks):
r = client_with_tasks.get("/api/tasks?sort=priority")
assert r.status_code == 200
tasks = r.json()
assert len(tasks) == 4
# ---------------------------------------------------------------------------
# (6) Невалидный sort-field фоллбэчит на updated_at (нет 500-ошибки)
# ---------------------------------------------------------------------------
def test_list_tasks_invalid_sort_falls_back(client_with_tasks):
r = client_with_tasks.get("/api/tasks?sort='; DROP TABLE tasks; --")
assert r.status_code == 200
tasks = r.json()
assert isinstance(tasks, list)
# ---------------------------------------------------------------------------
# (7) Все параметры вместе: status=completed&limit=20&sort=updated_at
# ---------------------------------------------------------------------------
def test_list_tasks_combined_params(client_with_tasks):
r = client_with_tasks.get("/api/tasks?status=completed&limit=20&sort=updated_at")
assert r.status_code == 200
tasks = r.json()
assert len(tasks) == 2
assert all(t["status"] == "completed" for t in tasks)
# ---------------------------------------------------------------------------
# (8) Пустая база — возвращает 200 и пустой список
# ---------------------------------------------------------------------------
def test_list_tasks_empty_db(client):
r = client.get("/api/tasks")
assert r.status_code == 200
assert r.json() == []