Newer
Older
gnexus-tasks / backend / tests / test_tasks_api.py
"""Тесты 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