"""Тесты API M1: быстрый захват, стек, детализация, проекты, теги."""
from typing import Any
from fastapi.testclient import TestClient
def create_task(client: TestClient, title: str = "тестовая задача") -> dict[str, Any]:
resp = client.post("/api/tasks", json={"title": title})
assert resp.status_code == 200, resp.text
task_id = resp.json()["id"]
resp = client.get(f"/api/tasks/{task_id}")
assert resp.status_code == 200, resp.text
return resp.json()
def test_quick_capture_lands_in_stack(client: TestClient) -> None:
task = create_task(client, "позвонить маме")
assert task["detail_state"] == "raw"
assert task["status"] == "to_do"
assert task["title"] == "позвонить маме"
stack = client.get("/api/tasks", params={"detail_state": "raw"}).json()
assert any(t["id"] == task["id"] for t in stack)
def test_approve_moves_out_of_stack(client: TestClient) -> None:
task = create_task(client)
resp = client.post(f"/api/tasks/{task['id']}/approve")
assert resp.status_code == 200
approved = resp.json()
assert approved["detail_state"] == "approved"
assert approved["approved_at"] is not None
stack = client.get("/api/tasks", params={"detail_state": "raw"}).json()
assert all(t["id"] != task["id"] for t in stack)
def test_update_with_project_and_tags(client: TestClient) -> None:
project_id = client.post("/api/projects", json={"name": "дом"}).json()["id"]
tag_id = client.post("/api/tags", json={"name": "срочное"}).json()["id"]
task = create_task(client)
resp = client.patch(
f"/api/tasks/{task['id']}",
json={"project_id": project_id, "tag_ids": [tag_id], "priority": 3},
)
assert resp.status_code == 200, resp.text
updated = resp.json()
assert updated["project"]["id"] == project_id
assert [t["id"] for t in updated["tags"]] == [tag_id]
assert updated["priority"] == 3
def test_update_unknown_project_rejected(client: TestClient) -> None:
task = create_task(client)
resp = client.patch(f"/api/tasks/{task['id']}", json={"project_id": 99999})
assert resp.status_code == 400
def test_status_validation_and_done_at(client: TestClient) -> None:
task = create_task(client)
bad = client.patch(f"/api/tasks/{task['id']}", json={"status": "nope"})
assert bad.status_code == 422
done = client.patch(f"/api/tasks/{task['id']}", json={"status": "done"})
assert done.status_code == 200
assert done.json()["done_at"] is not None
def test_delete_task(client: TestClient) -> None:
task = create_task(client)
assert client.delete(f"/api/tasks/{task['id']}").json() == {"ok": True}
assert client.get(f"/api/tasks/{task['id']}").status_code == 404
def test_project_crud_and_duplicate_name(client: TestClient) -> None:
resp = client.post("/api/projects", json={"name": "gntodo-разработка"})
assert resp.status_code == 200
pid = resp.json()["id"]
dup = client.post("/api/projects", json={"name": "gntodo-разработка"})
assert dup.status_code == 409
resp = client.patch(f"/api/projects/{pid}", json={"relevance_status": "paused"})
assert resp.status_code == 200
assert resp.json()["relevance_status"] == "paused"
bad = client.patch(f"/api/projects/{pid}", json={"relevance_status": "nope"})
assert bad.status_code == 422
def test_tag_duplicate(client: TestClient) -> None:
assert client.post("/api/tags", json={"name": "быт"}).status_code == 200
assert client.post("/api/tags", json={"name": "быт"}).status_code == 409