"""
Unit tests for Task API endpoints.
Tests CRUD operations for tasks.
"""
import pytest
import os
import json
from fastapi.testclient import TestClient
# Use a test database
TEST_DB_PATH = "test_task_annotation_platform.db"
@pytest.fixture(scope="function", autouse=True)
def setup_test_db():
"""Setup test database before each test and cleanup after."""
# Set test database path
original_db_path = os.environ.get("DATABASE_PATH")
os.environ["DATABASE_PATH"] = TEST_DB_PATH
# Remove existing test database
if os.path.exists(TEST_DB_PATH):
os.remove(TEST_DB_PATH)
# Import after setting env var
from database import init_database
init_database()
yield
# Cleanup
if os.path.exists(TEST_DB_PATH):
os.remove(TEST_DB_PATH)
# Restore original path
if original_db_path:
os.environ["DATABASE_PATH"] = original_db_path
elif "DATABASE_PATH" in os.environ:
del os.environ["DATABASE_PATH"]
@pytest.fixture(scope="function")
def test_client():
"""Create a test client."""
from main import app
return TestClient(app)
@pytest.fixture(scope="function")
def sample_project(test_client):
"""Create a sample project for testing."""
project_data = {
"name": "Test Project",
"description": "Test Description",
"config": ""
}
response = test_client.post("/api/projects", json=project_data)
return response.json()
def test_list_tasks_empty(test_client):
"""Test listing tasks when database is empty."""
response = test_client.get("/api/tasks")
assert response.status_code == 200
assert response.json() == []
def test_create_task(test_client, sample_project):
"""Test creating a new task."""
task_data = {
"project_id": sample_project["id"],
"name": "Test Task",
"data": {"image_url": "https://example.com/image.jpg"},
"assigned_to": "user_001"
}
response = test_client.post("/api/tasks", json=task_data)
assert response.status_code == 201
data = response.json()
assert data["name"] == task_data["name"]
assert data["project_id"] == task_data["project_id"]
assert data["data"] == task_data["data"]
assert data["assigned_to"] == task_data["assigned_to"]
assert data["status"] == "pending"
assert "id" in data
assert data["id"].startswith("task_")
assert data["progress"] == 0.0
assert "created_at" in data
def test_create_task_invalid_project(test_client):
"""Test creating a task with invalid project_id fails."""
task_data = {
"project_id": "nonexistent_project",
"name": "Test Task",
"data": {"image_url": "https://example.com/image.jpg"}
}
response = test_client.post("/api/tasks", json=task_data)
assert response.status_code == 404
assert "not found" in response.json()["detail"].lower()
def test_get_task(test_client, sample_project):
"""Test getting a task by ID."""
# Create a task first
task_data = {
"project_id": sample_project["id"],
"name": "Test Task",
"data": {"image_url": "https://example.com/image.jpg"}
}
create_response = test_client.post("/api/tasks", json=task_data)
task_id = create_response.json()["id"]
# Get the task
response = test_client.get(f"/api/tasks/{task_id}")
assert response.status_code == 200
data = response.json()
assert data["id"] == task_id
assert data["name"] == task_data["name"]
def test_get_task_not_found(test_client):
"""Test getting a non-existent task returns 404."""
response = test_client.get("/api/tasks/nonexistent_id")
assert response.status_code == 404
assert "not found" in response.json()["detail"].lower()
def test_update_task(test_client, sample_project):
"""Test updating a task."""
# Create a task first
task_data = {
"project_id": sample_project["id"],
"name": "Original Name",
"data": {"image_url": "https://example.com/image.jpg"}
}
create_response = test_client.post("/api/tasks", json=task_data)
task_id = create_response.json()["id"]
# Update the task
update_data = {
"name": "Updated Name",
"status": "in_progress"
}
response = test_client.put(f"/api/tasks/{task_id}", json=update_data)
assert response.status_code == 200
data = response.json()
assert data["name"] == update_data["name"]
assert data["status"] == update_data["status"]
assert data["data"] == task_data["data"] # Data unchanged
def test_update_task_not_found(test_client):
"""Test updating a non-existent task returns 404."""
update_data = {"name": "Updated Name"}
response = test_client.put("/api/tasks/nonexistent_id", json=update_data)
assert response.status_code == 404
def test_delete_task(test_client, sample_project):
"""Test deleting a task."""
# Create a task first
task_data = {
"project_id": sample_project["id"],
"name": "Test Task",
"data": {"image_url": "https://example.com/image.jpg"}
}
create_response = test_client.post("/api/tasks", json=task_data)
task_id = create_response.json()["id"]
# Delete the task
response = test_client.delete(f"/api/tasks/{task_id}")
assert response.status_code == 204
# Verify task is deleted
get_response = test_client.get(f"/api/tasks/{task_id}")
assert get_response.status_code == 404
def test_delete_task_not_found(test_client):
"""Test deleting a non-existent task returns 404."""
response = test_client.delete("/api/tasks/nonexistent_id")
assert response.status_code == 404
def test_list_tasks_after_creation(test_client, sample_project):
"""Test listing tasks after creating some."""
# Create multiple tasks
for i in range(3):
task_data = {
"project_id": sample_project["id"],
"name": f"Task {i}",
"data": {"image_url": f"https://example.com/image{i}.jpg"}
}
test_client.post("/api/tasks", json=task_data)
# List tasks
response = test_client.get("/api/tasks")
assert response.status_code == 200
data = response.json()
assert len(data) == 3
assert all("id" in task for task in data)
assert all("progress" in task for task in data)
def test_list_tasks_filter_by_project(test_client, sample_project):
"""Test filtering tasks by project_id."""
# Create another project
project2_data = {
"name": "Project 2",
"description": "Description 2",
"config": ""
}
project2_response = test_client.post("/api/projects", json=project2_data)
project2 = project2_response.json()
# Create tasks for both projects
task1_data = {
"project_id": sample_project["id"],
"name": "Task 1",
"data": {"image_url": "https://example.com/image1.jpg"}
}
test_client.post("/api/tasks", json=task1_data)
task2_data = {
"project_id": project2["id"],
"name": "Task 2",
"data": {"image_url": "https://example.com/image2.jpg"}
}
test_client.post("/api/tasks", json=task2_data)
# Filter by first project
response = test_client.get(f"/api/tasks?project_id={sample_project['id']}")
assert response.status_code == 200
data = response.json()
assert len(data) == 1
assert data[0]["project_id"] == sample_project["id"]
def test_list_tasks_filter_by_status(test_client, sample_project):
"""Test filtering tasks by status."""
# Create tasks with different statuses
task1_data = {
"project_id": sample_project["id"],
"name": "Task 1",
"data": {"image_url": "https://example.com/image1.jpg"}
}
response1 = test_client.post("/api/tasks", json=task1_data)
task1_id = response1.json()["id"]
task2_data = {
"project_id": sample_project["id"],
"name": "Task 2",
"data": {"image_url": "https://example.com/image2.jpg"}
}
test_client.post("/api/tasks", json=task2_data)
# Update first task status
test_client.put(f"/api/tasks/{task1_id}", json={"status": "in_progress"})
# Filter by status
response = test_client.get("/api/tasks?status=in_progress")
assert response.status_code == 200
data = response.json()
assert len(data) == 1
assert data[0]["status"] == "in_progress"
def test_get_project_tasks(test_client, sample_project):
"""Test getting all tasks for a specific project."""
# Create tasks for the project
for i in range(2):
task_data = {
"project_id": sample_project["id"],
"name": f"Task {i}",
"data": {"image_url": f"https://example.com/image{i}.jpg"}
}
test_client.post("/api/tasks", json=task_data)
# Get project tasks using the alternative endpoint
response = test_client.get(f"/api/tasks/projects/{sample_project['id']}/tasks")
assert response.status_code == 200
data = response.json()
assert len(data) == 2
assert all(task["project_id"] == sample_project["id"] for task in data)
def test_get_project_tasks_not_found(test_client):
"""Test getting tasks for non-existent project returns 404."""
response = test_client.get("/api/tasks/projects/nonexistent_id/tasks")
assert response.status_code == 404