"""P14.1 — Tasks CRUD API tests. Covers: - List tasks (project-scoped, milestone-scoped) - Get task by ID - Create task - Update task - Delete task - Task filtering by status, assignee, etc. """ import pytest from datetime import datetime, timedelta class TestTasksCRUD: """Task CRUD endpoints.""" def test_list_tasks(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member): """List tasks for a project.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions user = make_user() project = make_project() make_member(project.id, user.id, dev_role.id) # Create milestone and tasks from app.models.milestone import Milestone, MilestoneStatus from app.models.task import Task, TaskStatus, TaskPriority milestone = Milestone(title="M1", project_id=project.id, status=MilestoneStatus.OPEN) db.add(milestone) db.commit() task1 = Task( title="Task 1", project_id=project.id, milestone_id=milestone.id, reporter_id=user.id, status=TaskStatus.OPEN, priority=TaskPriority.MEDIUM ) task2 = Task( title="Task 2", project_id=project.id, milestone_id=milestone.id, reporter_id=user.id, status=TaskStatus.OPEN, priority=TaskPriority.MEDIUM ) db.add_all([task1, task2]) db.commit() resp = client.get(f"/projects/{project.id}/tasks", headers=auth_header(user)) assert resp.status_code == 200 data = resp.json() assert len(data) >= 2 def test_get_task_by_id(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member): """Get specific task.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions user = make_user() project = make_project() make_member(project.id, user.id, dev_role.id) from app.models.milestone import Milestone, MilestoneStatus from app.models.task import Task, TaskStatus, TaskPriority milestone = Milestone(title="M1", project_id=project.id, status=MilestoneStatus.OPEN) db.add(milestone) db.commit() task = Task( title="Test Task", project_id=project.id, milestone_id=milestone.id, reporter_id=user.id, status=TaskStatus.OPEN, priority=TaskPriority.HIGH, task_type="issue" ) db.add(task) db.commit() resp = client.get(f"/projects/{project.id}/tasks/{task.id}", headers=auth_header(user)) assert resp.status_code == 200 data = resp.json() assert data["id"] == task.id assert data["title"] == "Test Task" assert data["task_type"] == "issue" def test_create_task(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member): """Create new task.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions user = make_user() project = make_project(project_code="PROJ") make_member(project.id, user.id, dev_role.id) from app.models.milestone import Milestone, MilestoneStatus milestone = Milestone(title="M1", project_id=project.id, status=MilestoneStatus.OPEN) db.add(milestone) db.commit() resp = client.post( f"/projects/{project.id}/tasks", json={ "title": "New Task", "description": "Task description", "milestone_id": milestone.id, "task_type": "issue", "priority": "high" }, headers=auth_header(user) ) assert resp.status_code == 201 data = resp.json() assert data["title"] == "New Task" assert data["status"] == "open" assert data["task_code"].startswith("PROJ:") def test_update_task(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member): """Update task.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions user = make_user() project = make_project() make_member(project.id, user.id, dev_role.id) from app.models.milestone import Milestone, MilestoneStatus from app.models.task import Task, TaskStatus, TaskPriority milestone = Milestone(title="M1", project_id=project.id, status=MilestoneStatus.OPEN) db.add(milestone) db.commit() task = Task( title="Old Title", project_id=project.id, milestone_id=milestone.id, reporter_id=user.id, status=TaskStatus.OPEN, priority=TaskPriority.MEDIUM ) db.add(task) db.commit() resp = client.patch( f"/projects/{project.id}/tasks/{task.id}", json={"title": "Updated Title"}, headers=auth_header(user) ) assert resp.status_code == 200 data = resp.json() assert data["title"] == "Updated Title" def test_delete_task(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member): """Delete task.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions user = make_user() project = make_project() make_member(project.id, user.id, dev_role.id) from app.models.milestone import Milestone, MilestoneStatus from app.models.task import Task, TaskStatus, TaskPriority milestone = Milestone(title="M1", project_id=project.id, status=MilestoneStatus.OPEN) db.add(milestone) db.commit() task = Task( title="To Delete", project_id=project.id, milestone_id=milestone.id, reporter_id=user.id, status=TaskStatus.OPEN, priority=TaskPriority.MEDIUM ) db.add(task) db.commit() resp = client.delete(f"/projects/{project.id}/tasks/{task.id}", headers=auth_header(user)) assert resp.status_code == 204 def test_task_filter_by_status(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member): """Filter tasks by status.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions user = make_user() project = make_project() make_member(project.id, user.id, dev_role.id) from app.models.milestone import Milestone, MilestoneStatus from app.models.task import Task, TaskStatus, TaskPriority milestone = Milestone(title="M1", project_id=project.id, status=MilestoneStatus.OPEN) db.add(milestone) db.commit() open_task = Task( title="Open", project_id=project.id, milestone_id=milestone.id, reporter_id=user.id, status=TaskStatus.OPEN, priority=TaskPriority.MEDIUM ) closed_task = Task( title="Closed", project_id=project.id, milestone_id=milestone.id, reporter_id=user.id, status=TaskStatus.CLOSED, priority=TaskPriority.MEDIUM ) db.add_all([open_task, closed_task]) db.commit() resp = client.get(f"/projects/{project.id}/tasks?status=open", headers=auth_header(user)) assert resp.status_code == 200 data = resp.json() assert all(t["status"] == "open" for t in data) def test_task_filter_by_assignee(self, client, db, make_user, make_project, auth_header, seed_roles_and_permissions, make_member): """Filter tasks by assignee.""" admin_role, mgr_role, dev_role = seed_roles_and_permissions user = make_user() assignee = make_user(username="assignee") project = make_project() make_member(project.id, user.id, dev_role.id) from app.models.milestone import Milestone, MilestoneStatus from app.models.task import Task, TaskStatus, TaskPriority milestone = Milestone(title="M1", project_id=project.id, status=MilestoneStatus.OPEN) db.add(milestone) db.commit() assigned_task = Task( title="Assigned", project_id=project.id, milestone_id=milestone.id, reporter_id=user.id, assignee_id=assignee.id, status=TaskStatus.OPEN, priority=TaskPriority.MEDIUM ) db.add(assigned_task) db.commit() resp = client.get(f"/projects/{project.id}/tasks?assignee_id={assignee.id}", headers=auth_header(user)) assert resp.status_code == 200 data = resp.json() assert all(t["assignee_id"] == assignee.id for t in data)