kin: auto-commit after pipeline

This commit is contained in:
Gros Frumos 2026-03-18 15:52:27 +02:00
parent 747625b05d
commit 4f50c4eb73

View file

@ -0,0 +1,139 @@
"""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() == []