kin: auto-commit after pipeline
This commit is contained in:
parent
747625b05d
commit
4f50c4eb73
1 changed files with 139 additions and 0 deletions
139
tests/test_kin_ui_015_regression.py
Normal file
139
tests/test_kin_ui_015_regression.py
Normal 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() == []
|
||||||
Loading…
Add table
Add a link
Reference in a new issue