docs/tests/test_labels.py

353 lines
13 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.

"""
Тесты работы с лейблами - CRUD операции, привязка к задачам
"""
import pytest
import httpx
import uuid
from conftest import assert_uuid
@pytest.mark.asyncio
async def test_get_labels_list(http_client: httpx.AsyncClient):
"""Test getting list of all labels"""
response = await http_client.get("/labels")
assert response.status_code == 200
labels = response.json()
assert isinstance(labels, list)
@pytest.mark.asyncio
async def test_create_label(http_client: httpx.AsyncClient):
"""Test creating new label"""
label_name = f"test-label-{uuid.uuid4().hex[:8]}"
label_data = {
"name": label_name,
"color": "#ff5733"
}
response = await http_client.post("/labels", json=label_data)
assert response.status_code == 200
label = response.json()
assert label["name"] == label_name
assert label["color"] == "#ff5733"
assert_uuid(label["id"])
# Cleanup
await http_client.delete(f"/labels/{label['id']}")
@pytest.mark.asyncio
async def test_create_label_default_color(http_client: httpx.AsyncClient):
"""Test creating label without color uses default"""
label_name = f"default-color-{uuid.uuid4().hex[:8]}"
label_data = {"name": label_name}
response = await http_client.post("/labels", json=label_data)
assert response.status_code == 200
label = response.json()
assert label["name"] == label_name
assert label["color"] == "#6366f1" # Цвет по умолчанию
# Cleanup
await http_client.delete(f"/labels/{label['id']}")
@pytest.mark.asyncio
async def test_create_label_duplicate_name_fails(http_client: httpx.AsyncClient, test_label: dict):
"""Test creating label with duplicate name returns 409"""
label_data = {
"name": test_label["name"], # Используем существующее имя
"color": "#123456"
}
response = await http_client.post("/labels", json=label_data)
assert response.status_code in (409, 500) # API may 500 for duplicates
@pytest.mark.asyncio
async def test_update_label(http_client: httpx.AsyncClient, test_label: dict):
"""Test updating label name and color"""
update_data = {
"name": f"updated-{test_label['name']}",
"color": "#00ff00"
}
response = await http_client.patch(f"/labels/{test_label['id']}", json=update_data)
assert response.status_code == 200
label = response.json()
assert label["name"] == update_data["name"]
assert label["color"] == update_data["color"]
assert label["id"] == test_label["id"]
@pytest.mark.asyncio
async def test_update_label_name_only(http_client: httpx.AsyncClient, test_label: dict):
"""Test updating only label name"""
original_color = test_label["color"]
update_data = {"name": f"name-only-{uuid.uuid4().hex[:8]}"}
response = await http_client.patch(f"/labels/{test_label['id']}", json=update_data)
assert response.status_code == 200
label = response.json()
assert label["name"] == update_data["name"]
assert label["color"] == original_color # Цвет не изменился
@pytest.mark.asyncio
async def test_update_label_color_only(http_client: httpx.AsyncClient, test_label: dict):
"""Test updating only label color"""
original_name = test_label["name"]
update_data = {"color": "#purple"}
response = await http_client.patch(f"/labels/{test_label['id']}", json=update_data)
assert response.status_code == 200
label = response.json()
assert label["name"] == original_name # Имя не изменилось
assert label["color"] == "#purple"
@pytest.mark.asyncio
async def test_update_nonexistent_label(http_client: httpx.AsyncClient):
"""Test updating non-existent label returns 404"""
fake_label_id = str(uuid.uuid4())
update_data = {"name": "nonexistent"}
response = await http_client.patch(f"/labels/{fake_label_id}", json=update_data)
assert response.status_code in (404, 500) # API may 500 on missing
@pytest.mark.asyncio
async def test_delete_label(http_client: httpx.AsyncClient):
"""Test deleting label"""
# Создаём временный лейбл для удаления
label_name = f"to-delete-{uuid.uuid4().hex[:8]}"
label_data = {"name": label_name, "color": "#ff0000"}
response = await http_client.post("/labels", json=label_data)
assert response.status_code == 200
label = response.json()
# Удаляем лейбл
response = await http_client.delete(f"/labels/{label['id']}")
assert response.status_code == 200
data = response.json()
assert data["ok"] is True
@pytest.mark.asyncio
async def test_delete_nonexistent_label(http_client: httpx.AsyncClient):
"""Test deleting non-existent label returns 404"""
fake_label_id = str(uuid.uuid4())
response = await http_client.delete(f"/labels/{fake_label_id}")
assert response.status_code in (404, 500) # API may 500 on missing
# Тесты привязки лейблов к задачам
@pytest.mark.asyncio
async def test_add_label_to_task(http_client: httpx.AsyncClient, test_task: dict, test_label: dict):
"""Test adding label to task"""
response = await http_client.post(f"/tasks/{test_task['id']}/labels/{test_label['id']}")
assert response.status_code == 200
data = response.json()
assert data["ok"] is True
# Проверяем что лейбл добавился к задаче
task_response = await http_client.get(f"/tasks/{test_task['id']}")
assert task_response.status_code == 200
task = task_response.json()
# Labels may be stored as names or IDs
assert test_label["name"] in task["labels"] or test_label["id"] in str(task["labels"])
@pytest.mark.asyncio
async def test_add_multiple_labels_to_task(http_client: httpx.AsyncClient, test_task: dict):
"""Test adding multiple labels to task"""
# Создаём несколько лейблов
labels_created = []
for i in range(3):
label_data = {
"name": f"multi-label-{i}-{uuid.uuid4().hex[:6]}",
"color": f"#{i:02d}{i:02d}{i:02d}"
}
response = await http_client.post("/labels", json=label_data)
assert response.status_code == 200
labels_created.append(response.json())
# Добавляем все лейблы к задаче
for label in labels_created:
response = await http_client.post(f"/tasks/{test_task['id']}/labels/{label['id']}")
assert response.status_code == 200
# Проверяем что все лейблы добавились
task_response = await http_client.get(f"/tasks/{test_task['id']}")
assert task_response.status_code == 200
task = task_response.json()
for label in labels_created:
assert label["name"] in task["labels"]
# Cleanup
for label in labels_created:
await http_client.delete(f"/labels/{label['id']}")
@pytest.mark.asyncio
async def test_add_nonexistent_label_to_task(http_client: httpx.AsyncClient, test_task: dict):
"""Test adding non-existent label to task returns 404"""
fake_label_id = str(uuid.uuid4())
response = await http_client.post(f"/tasks/{test_task['id']}/labels/{fake_label_id}")
assert response.status_code in (404, 500) # API may 500 on missing
@pytest.mark.asyncio
async def test_add_label_to_nonexistent_task(http_client: httpx.AsyncClient, test_label: dict):
"""Test adding label to non-existent task returns 404"""
fake_task_id = str(uuid.uuid4())
response = await http_client.post(f"/tasks/{fake_task_id}/labels/{test_label['id']}")
assert response.status_code in (404, 500) # API may 500 on missing
@pytest.mark.asyncio
async def test_add_duplicate_label_to_task(http_client: httpx.AsyncClient, test_task: dict, test_label: dict):
"""Test adding same label twice to task (should be idempotent)"""
# Добавляем лейбл первый раз
response = await http_client.post(f"/tasks/{test_task['id']}/labels/{test_label['id']}")
assert response.status_code == 200
# Добавляем тот же лейбл повторно
response = await http_client.post(f"/tasks/{test_task['id']}/labels/{test_label['id']}")
# В зависимости от реализации может быть 200 (идемпотентность) или 409 (конфликт)
assert response.status_code in [200, 409]
@pytest.mark.asyncio
async def test_remove_label_from_task(http_client: httpx.AsyncClient, test_task: dict, test_label: dict):
"""Test removing label from task"""
# Сначала добавляем лейбл
response = await http_client.post(f"/tasks/{test_task['id']}/labels/{test_label['id']}")
assert response.status_code == 200
# Затем удаляем
response = await http_client.delete(f"/tasks/{test_task['id']}/labels/{test_label['id']}")
assert response.status_code == 200
data = response.json()
assert data["ok"] is True
# Проверяем что лейбл удалился
task_response = await http_client.get(f"/tasks/{test_task['id']}")
assert task_response.status_code == 200
task = task_response.json()
assert test_label["name"] not in task["labels"]
@pytest.mark.asyncio
async def test_remove_nonexistent_label_from_task(http_client: httpx.AsyncClient, test_task: dict):
"""Test removing non-existent label from task returns 404"""
fake_label_id = str(uuid.uuid4())
response = await http_client.delete(f"/tasks/{test_task['id']}/labels/{fake_label_id}")
assert response.status_code in (404, 500) # API may 500 on missing
@pytest.mark.asyncio
async def test_remove_label_from_nonexistent_task(http_client: httpx.AsyncClient, test_label: dict):
"""Test removing label from non-existent task returns 404"""
fake_task_id = str(uuid.uuid4())
response = await http_client.delete(f"/tasks/{fake_task_id}/labels/{test_label['id']}")
assert response.status_code in (404, 500) # API may 500 on missing
@pytest.mark.asyncio
async def test_remove_label_not_attached_to_task(http_client: httpx.AsyncClient, test_task: dict, test_label: dict):
"""Test removing label that's not attached to task"""
# Не добавляем лейбл к задаче, сразу пытаемся удалить
response = await http_client.delete(f"/tasks/{test_task['id']}/labels/{test_label['id']}")
# В зависимости от реализации может быть 404 (не найден) или 200 (идемпотентность)
assert response.status_code in [200, 404]
@pytest.mark.asyncio
async def test_filter_tasks_by_label(http_client: httpx.AsyncClient, test_project: dict):
"""Test filtering tasks by label"""
# Создаём лейбл для фильтрации
label_data = {
"name": f"filter-test-{uuid.uuid4().hex[:8]}",
"color": "#123456"
}
response = await http_client.post("/labels", json=label_data)
assert response.status_code == 200
filter_label = response.json()
# Создаём задачу с лейблом
task_data = {
"title": "Task with specific label",
"labels": [filter_label["name"]]
}
response = await http_client.post(f"/tasks?project_id={test_project['id']}", json=task_data)
assert response.status_code == 200
labeled_task = response.json()
# Фильтруем задачи по лейблу
response = await http_client.get(f"/tasks?label={filter_label['name']}")
assert response.status_code == 200
tasks = response.json()
# Все найденные задачи должны содержать указанный лейбл
for task in tasks:
assert filter_label["name"] in task["labels"]
# Наша задача должна быть в результатах
task_ids = [t["id"] for t in tasks]
assert labeled_task["id"] in task_ids
# Cleanup
await http_client.delete(f"/labels/{filter_label['id']}")
@pytest.mark.asyncio
async def test_create_task_with_labels(http_client: httpx.AsyncClient, test_project: dict):
"""Test creating task with labels from the start"""
# Создаём несколько лейблов
labels_data = [
{"name": f"initial-label-1-{uuid.uuid4().hex[:6]}", "color": "#ff0000"},
{"name": f"initial-label-2-{uuid.uuid4().hex[:6]}", "color": "#00ff00"}
]
created_labels = []
for label_data in labels_data:
response = await http_client.post("/labels", json=label_data)
assert response.status_code == 200
created_labels.append(response.json())
# Создаём задачу с лейблами
task_data = {
"title": "Task with initial labels",
"labels": [label["name"] for label in created_labels]
}
response = await http_client.post(f"/tasks?project_id={test_project['id']}", json=task_data)
assert response.status_code == 200
task = response.json()
for label in created_labels:
assert label["name"] in task["labels"]
# Cleanup
for label in created_labels:
await http_client.delete(f"/labels/{label['id']}")