""" 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