"""Tests for tasks router (/api/recipes/{id}/tasks, /api/tasks, /api/subtasks).""" import pytest from httpx import AsyncClient from sqlalchemy.ext.asyncio import AsyncSession from models.user import User from tests.conftest import auth_headers, create_test_recipe class TestListTasks: """GET /api/recipes/{recipe_id}/tasks tests.""" async def test_list_tasks( self, client: AsyncClient, maker_user: User, db_session: AsyncSession, ): """Authenticated user can list tasks for a recipe.""" recipe = await create_test_recipe(db_session, maker_user.id) resp = await client.get( f"/api/recipes/{recipe.id}/tasks", headers=auth_headers(maker_user), ) assert resp.status_code == 200 data = resp.json() assert isinstance(data, list) assert len(data) >= 1 assert data[0]["title"] == "Test Task" assert len(data[0]["subtasks"]) >= 1 class TestCreateTask: """POST /api/recipes/{recipe_id}/tasks tests.""" async def test_create_task( self, client: AsyncClient, maker_user: User, db_session: AsyncSession, ): """Maker can add a task (triggers copy-on-write new version).""" recipe = await create_test_recipe(db_session, maker_user.id) resp = await client.post( f"/api/recipes/{recipe.id}/tasks", headers=auth_headers(maker_user), json={ "title": "New Task", "directive": "Inspect surface", "subtasks": [ { "marker_number": 1, "description": "Surface roughness", "nominal": 5.0, "utl": 5.5, "ltl": 4.5, "unit": "um", } ], }, ) assert resp.status_code == 201 data = resp.json() assert data["title"] == "New Task" assert len(data["subtasks"]) == 1 assert data["subtasks"][0]["description"] == "Surface roughness" class TestUpdateTask: """PUT /api/tasks/{task_id} tests.""" async def test_update_task( self, client: AsyncClient, maker_user: User, db_session: AsyncSession, ): """Maker can update a task on the current version.""" recipe = await create_test_recipe(db_session, maker_user.id) # Get task list to find the task ID tasks_resp = await client.get( f"/api/recipes/{recipe.id}/tasks", headers=auth_headers(maker_user), ) task_id = tasks_resp.json()[0]["id"] resp = await client.put( f"/api/tasks/{task_id}", headers=auth_headers(maker_user), json={"title": "Updated Task Title"}, ) assert resp.status_code == 200 assert resp.json()["title"] == "Updated Task Title" class TestDeleteTask: """DELETE /api/tasks/{task_id} tests.""" async def test_delete_task( self, client: AsyncClient, maker_user: User, db_session: AsyncSession, ): """Maker can delete a task from the current version.""" recipe = await create_test_recipe(db_session, maker_user.id) tasks_resp = await client.get( f"/api/recipes/{recipe.id}/tasks", headers=auth_headers(maker_user), ) task_id = tasks_resp.json()[0]["id"] resp = await client.delete( f"/api/tasks/{task_id}", headers=auth_headers(maker_user) ) assert resp.status_code == 204 class TestCreateSubtask: """POST /api/tasks/{task_id}/subtasks tests.""" async def test_create_subtask( self, client: AsyncClient, maker_user: User, db_session: AsyncSession, ): """Maker can add a subtask to a task.""" recipe = await create_test_recipe(db_session, maker_user.id) tasks_resp = await client.get( f"/api/recipes/{recipe.id}/tasks", headers=auth_headers(maker_user), ) task_id = tasks_resp.json()[0]["id"] resp = await client.post( f"/api/tasks/{task_id}/subtasks", headers=auth_headers(maker_user), json={ "marker_number": 2, "description": "Width measurement", "nominal": 20.0, "utl": 20.5, "ltl": 19.5, "unit": "mm", }, ) assert resp.status_code == 201 data = resp.json() assert data["marker_number"] == 2 assert data["description"] == "Width measurement" class TestUpdateSubtask: """PUT /api/subtasks/{subtask_id} tests.""" async def test_update_subtask( self, client: AsyncClient, maker_user: User, db_session: AsyncSession, ): """Maker can update a subtask.""" recipe = await create_test_recipe(db_session, maker_user.id) tasks_resp = await client.get( f"/api/recipes/{recipe.id}/tasks", headers=auth_headers(maker_user), ) subtask_id = tasks_resp.json()[0]["subtasks"][0]["id"] resp = await client.put( f"/api/subtasks/{subtask_id}", headers=auth_headers(maker_user), json={"description": "Updated description", "nominal": 12.0}, ) assert resp.status_code == 200 assert resp.json()["description"] == "Updated description" class TestReorderTasks: """PUT /api/tasks/reorder tests.""" async def test_reorder_tasks( self, client: AsyncClient, maker_user: User, db_session: AsyncSession, ): """Maker can reorder tasks by providing task IDs in desired order.""" recipe = await create_test_recipe(db_session, maker_user.id) # Add a second task via copy-on-write add_resp = await client.post( f"/api/recipes/{recipe.id}/tasks", headers=auth_headers(maker_user), json={"title": "Second Task", "subtasks": []}, ) assert add_resp.status_code == 201 # Get tasks from new version tasks_resp = await client.get( f"/api/recipes/{recipe.id}/tasks", headers=auth_headers(maker_user), ) tasks = tasks_resp.json() assert len(tasks) >= 2 task_ids = [t["id"] for t in tasks] # Reverse the order reversed_ids = list(reversed(task_ids)) resp = await client.put( "/api/tasks/reorder", headers=auth_headers(maker_user), json={"task_ids": reversed_ids}, ) assert resp.status_code == 200 reordered = resp.json() assert [t["id"] for t in reordered] == reversed_ids