# tests/api/routes/syndarix/test_issues.py """ Comprehensive tests for the Issues API endpoints. Tests cover: - CRUD operations (create, read, update, delete) - Issue filtering and search - Issue assignment - Issue statistics - Authorization checks """ import uuid import pytest import pytest_asyncio from fastapi import status @pytest_asyncio.fixture async def test_project(client, user_token): """Create a test project for issue tests.""" response = await client.post( "/api/v1/projects", json={"name": "Issue Test Project", "slug": "issue-test-project"}, headers={"Authorization": f"Bearer {user_token}"}, ) return response.json() @pytest_asyncio.fixture async def superuser_project(client, superuser_token): """Create a project owned by superuser.""" response = await client.post( "/api/v1/projects", json={"name": "Superuser Project", "slug": "superuser-project"}, headers={"Authorization": f"Bearer {superuser_token}"}, ) return response.json() @pytest.mark.asyncio class TestCreateIssue: """Tests for POST /api/v1/projects/{project_id}/issues endpoint.""" async def test_create_issue_success(self, client, user_token, test_project): """Test successful issue creation.""" project_id = test_project["id"] response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Test Issue", "body": "This is a test issue description", "priority": "medium", }, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_201_CREATED data = response.json() assert data["title"] == "Test Issue" assert data["body"] == "This is a test issue description" assert data["priority"] == "medium" assert data["status"] == "open" assert data["project_id"] == project_id assert "id" in data assert "created_at" in data async def test_create_issue_minimal_fields(self, client, user_token, test_project): """Test creating issue with only required fields.""" project_id = test_project["id"] response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Minimal Issue", }, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_201_CREATED data = response.json() assert data["title"] == "Minimal Issue" assert data["body"] == "" # Body defaults to empty string assert data["status"] == "open" async def test_create_issue_with_labels(self, client, user_token, test_project): """Test creating issue with labels.""" project_id = test_project["id"] response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Labeled Issue", "labels": ["bug", "urgent", "frontend"], }, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_201_CREATED data = response.json() assert "bug" in data["labels"] assert "urgent" in data["labels"] assert "frontend" in data["labels"] async def test_create_issue_with_story_points(self, client, user_token, test_project): """Test creating issue with story points.""" project_id = test_project["id"] response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Story Points Issue", "story_points": 5, }, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_201_CREATED data = response.json() assert data["story_points"] == 5 async def test_create_issue_unauthorized_project( self, client, user_token, superuser_project ): """Test that users cannot create issues in others' projects.""" project_id = superuser_project["id"] response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Unauthorized Issue", }, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_403_FORBIDDEN async def test_create_issue_nonexistent_project(self, client, user_token): """Test creating issue in nonexistent project.""" fake_project_id = str(uuid.uuid4()) response = await client.post( f"/api/v1/projects/{fake_project_id}/issues", json={ "project_id": fake_project_id, "title": "Orphan Issue", }, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_404_NOT_FOUND @pytest.mark.asyncio class TestListIssues: """Tests for GET /api/v1/projects/{project_id}/issues endpoint.""" async def test_list_issues_empty(self, client, user_token, test_project): """Test listing issues when none exist.""" project_id = test_project["id"] response = await client.get( f"/api/v1/projects/{project_id}/issues", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert data["data"] == [] assert data["pagination"]["total"] == 0 async def test_list_issues_with_data(self, client, user_token, test_project): """Test listing issues returns created issues.""" project_id = test_project["id"] # Create multiple issues for i in range(3): await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": f"Issue {i + 1}", }, headers={"Authorization": f"Bearer {user_token}"}, ) response = await client.get( f"/api/v1/projects/{project_id}/issues", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert len(data["data"]) == 3 assert data["pagination"]["total"] == 3 async def test_list_issues_filter_by_status(self, client, user_token, test_project): """Test filtering issues by status.""" project_id = test_project["id"] # Create issues with different statuses await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Open Issue", "status": "open", }, headers={"Authorization": f"Bearer {user_token}"}, ) await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Closed Issue", "status": "closed", }, headers={"Authorization": f"Bearer {user_token}"}, ) # Filter by open response = await client.get( f"/api/v1/projects/{project_id}/issues?status=open", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert len(data["data"]) == 1 assert data["data"][0]["status"] == "open" async def test_list_issues_filter_by_priority(self, client, user_token, test_project): """Test filtering issues by priority.""" project_id = test_project["id"] # Create issues with different priorities await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "High Priority Issue", "priority": "high", }, headers={"Authorization": f"Bearer {user_token}"}, ) await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Low Priority Issue", "priority": "low", }, headers={"Authorization": f"Bearer {user_token}"}, ) # Filter by high priority response = await client.get( f"/api/v1/projects/{project_id}/issues?priority=high", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert len(data["data"]) == 1 assert data["data"][0]["priority"] == "high" async def test_list_issues_search(self, client, user_token, test_project): """Test searching issues by title/body.""" project_id = test_project["id"] await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Authentication Bug", "body": "Users cannot login", }, headers={"Authorization": f"Bearer {user_token}"}, ) await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "UI Enhancement", "body": "Improve dashboard layout", }, headers={"Authorization": f"Bearer {user_token}"}, ) # Search for authentication response = await client.get( f"/api/v1/projects/{project_id}/issues?search=authentication", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert len(data["data"]) == 1 assert "Authentication" in data["data"][0]["title"] async def test_list_issues_pagination(self, client, user_token, test_project): """Test pagination works correctly.""" project_id = test_project["id"] # Create 5 issues for i in range(5): await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": f"Issue {i + 1}", }, headers={"Authorization": f"Bearer {user_token}"}, ) # Get first page (2 items) response = await client.get( f"/api/v1/projects/{project_id}/issues?page=1&limit=2", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert len(data["data"]) == 2 assert data["pagination"]["total"] == 5 assert data["pagination"]["page"] == 1 @pytest.mark.asyncio class TestGetIssue: """Tests for GET /api/v1/projects/{project_id}/issues/{issue_id} endpoint.""" async def test_get_issue_success(self, client, user_token, test_project): """Test getting an issue by ID.""" project_id = test_project["id"] # Create issue create_response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Get Test Issue", "body": "Test description", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = create_response.json()["id"] # Get issue response = await client.get( f"/api/v1/projects/{project_id}/issues/{issue_id}", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert data["id"] == issue_id assert data["title"] == "Get Test Issue" async def test_get_issue_not_found(self, client, user_token, test_project): """Test getting a nonexistent issue.""" project_id = test_project["id"] fake_issue_id = str(uuid.uuid4()) response = await client.get( f"/api/v1/projects/{project_id}/issues/{fake_issue_id}", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_404_NOT_FOUND @pytest.mark.asyncio class TestUpdateIssue: """Tests for PATCH /api/v1/projects/{project_id}/issues/{issue_id} endpoint.""" async def test_update_issue_success(self, client, user_token, test_project): """Test updating an issue.""" project_id = test_project["id"] # Create issue create_response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Original Title", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = create_response.json()["id"] # Update issue response = await client.patch( f"/api/v1/projects/{project_id}/issues/{issue_id}", json={"title": "Updated Title", "body": "New description"}, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert data["title"] == "Updated Title" assert data["body"] == "New description" async def test_update_issue_status(self, client, user_token, test_project): """Test updating issue status.""" project_id = test_project["id"] # Create issue create_response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Status Test Issue", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = create_response.json()["id"] # Update status to in_progress response = await client.patch( f"/api/v1/projects/{project_id}/issues/{issue_id}", json={"status": "in_progress"}, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK assert response.json()["status"] == "in_progress" async def test_update_issue_priority(self, client, user_token, test_project): """Test updating issue priority.""" project_id = test_project["id"] # Create issue with low priority create_response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Priority Test Issue", "priority": "low", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = create_response.json()["id"] # Update to critical response = await client.patch( f"/api/v1/projects/{project_id}/issues/{issue_id}", json={"priority": "critical"}, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK assert response.json()["priority"] == "critical" async def test_update_issue_not_found(self, client, user_token, test_project): """Test updating a nonexistent issue.""" project_id = test_project["id"] fake_issue_id = str(uuid.uuid4()) response = await client.patch( f"/api/v1/projects/{project_id}/issues/{fake_issue_id}", json={"title": "Updated Title"}, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_404_NOT_FOUND @pytest.mark.asyncio class TestDeleteIssue: """Tests for DELETE /api/v1/projects/{project_id}/issues/{issue_id} endpoint.""" async def test_delete_issue_success(self, client, user_token, test_project): """Test deleting an issue.""" project_id = test_project["id"] # Create issue create_response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Delete Test Issue", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = create_response.json()["id"] # Delete issue response = await client.delete( f"/api/v1/projects/{project_id}/issues/{issue_id}", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK assert response.json()["success"] is True async def test_delete_issue_not_found(self, client, user_token, test_project): """Test deleting a nonexistent issue.""" project_id = test_project["id"] fake_issue_id = str(uuid.uuid4()) response = await client.delete( f"/api/v1/projects/{project_id}/issues/{fake_issue_id}", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_404_NOT_FOUND @pytest.mark.asyncio class TestIssueStats: """Tests for GET /api/v1/projects/{project_id}/issues/stats endpoint.""" async def test_get_issue_stats_empty(self, client, user_token, test_project): """Test getting stats when no issues exist.""" project_id = test_project["id"] response = await client.get( f"/api/v1/projects/{project_id}/issues/stats", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert data["total"] == 0 assert data["open"] == 0 assert data["in_progress"] == 0 assert data["in_review"] == 0 assert data["blocked"] == 0 assert data["closed"] == 0 async def test_get_issue_stats_with_data(self, client, user_token, test_project): """Test getting stats with issues.""" project_id = test_project["id"] # Create issues with different statuses and priorities await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Open High Issue", "status": "open", "priority": "high", "story_points": 5, }, headers={"Authorization": f"Bearer {user_token}"}, ) await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Closed Low Issue", "status": "closed", "priority": "low", "story_points": 3, }, headers={"Authorization": f"Bearer {user_token}"}, ) response = await client.get( f"/api/v1/projects/{project_id}/issues/stats", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert data["total"] == 2 assert data["open"] == 1 assert data["closed"] == 1 assert data["by_priority"]["high"] == 1 assert data["by_priority"]["low"] == 1 @pytest.mark.asyncio class TestIssueAuthorization: """Tests for issue authorization.""" async def test_superuser_can_manage_any_project_issues( self, client, user_token, superuser_token, test_project ): """Test that superuser can manage issues in any project.""" project_id = test_project["id"] # Create issue as superuser in user's project response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Superuser Issue", }, headers={"Authorization": f"Bearer {superuser_token}"}, ) assert response.status_code == status.HTTP_201_CREATED async def test_user_cannot_access_other_project_issues( self, client, user_token, superuser_project ): """Test that users cannot access issues in others' projects.""" project_id = superuser_project["id"] response = await client.get( f"/api/v1/projects/{project_id}/issues", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_403_FORBIDDEN @pytest.mark.asyncio class TestIssueAssignment: """Tests for issue assignment endpoints.""" async def test_assign_issue_to_human(self, client, user_token, test_project): """Test assigning an issue to a human.""" project_id = test_project["id"] # Create issue create_response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Issue to Assign", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = create_response.json()["id"] # Assign to human response = await client.post( f"/api/v1/projects/{project_id}/issues/{issue_id}/assign", json={"human_assignee": "john.doe@example.com"}, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK data = response.json() assert data["human_assignee"] == "john.doe@example.com" async def test_unassign_issue(self, client, user_token, test_project): """Test unassigning an issue.""" project_id = test_project["id"] # Create issue and assign create_response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Issue to Unassign", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = create_response.json()["id"] await client.post( f"/api/v1/projects/{project_id}/issues/{issue_id}/assign", json={"human_assignee": "john.doe@example.com"}, headers={"Authorization": f"Bearer {user_token}"}, ) # Unassign response = await client.delete( f"/api/v1/projects/{project_id}/issues/{issue_id}/assignment", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK data = response.json() # After unassign, assigned_agent_id should be None # Note: human_assignee may or may not be cleared depending on implementation assert data["assigned_agent_id"] is None async def test_assign_issue_not_found(self, client, user_token, test_project): """Test assigning a nonexistent issue.""" project_id = test_project["id"] fake_issue_id = str(uuid.uuid4()) response = await client.post( f"/api/v1/projects/{project_id}/issues/{fake_issue_id}/assign", json={"human_assignee": "john.doe@example.com"}, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_404_NOT_FOUND async def test_unassign_issue_not_found(self, client, user_token, test_project): """Test unassigning a nonexistent issue.""" project_id = test_project["id"] fake_issue_id = str(uuid.uuid4()) response = await client.delete( f"/api/v1/projects/{project_id}/issues/{fake_issue_id}/assignment", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_404_NOT_FOUND async def test_assign_issue_clears_assignment(self, client, user_token, test_project): """Test that assigning to null clears both assignments.""" project_id = test_project["id"] # Create issue and assign create_response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Issue to Clear", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = create_response.json()["id"] await client.post( f"/api/v1/projects/{project_id}/issues/{issue_id}/assign", json={"human_assignee": "john.doe@example.com"}, headers={"Authorization": f"Bearer {user_token}"}, ) # Clear assignment by sending empty object response = await client.post( f"/api/v1/projects/{project_id}/issues/{issue_id}/assign", json={}, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_200_OK @pytest.mark.asyncio class TestIssueSync: """Tests for issue sync endpoint.""" async def test_sync_issue_no_tracker(self, client, user_token, test_project): """Test syncing an issue without external tracker.""" project_id = test_project["id"] # Create issue without external tracker create_response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Issue without Tracker", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = create_response.json()["id"] # Try to sync response = await client.post( f"/api/v1/projects/{project_id}/issues/{issue_id}/sync", headers={"Authorization": f"Bearer {user_token}"}, ) # Should fail because no external tracker configured assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY async def test_sync_issue_not_found(self, client, user_token, test_project): """Test syncing a nonexistent issue.""" project_id = test_project["id"] fake_issue_id = str(uuid.uuid4()) response = await client.post( f"/api/v1/projects/{project_id}/issues/{fake_issue_id}/sync", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_404_NOT_FOUND @pytest.mark.asyncio class TestIssueCrossProjectValidation: """Tests for cross-project validation (IDOR prevention).""" async def test_issue_not_in_project(self, client, user_token): """Test accessing issue that exists but not in the specified project.""" # Create two projects project1 = await client.post( "/api/v1/projects", json={"name": "Project 1", "slug": "project-1-idor"}, headers={"Authorization": f"Bearer {user_token}"}, ) project2 = await client.post( "/api/v1/projects", json={"name": "Project 2", "slug": "project-2-idor"}, headers={"Authorization": f"Bearer {user_token}"}, ) project1_id = project1.json()["id"] project2_id = project2.json()["id"] # Create issue in project1 issue_response = await client.post( f"/api/v1/projects/{project1_id}/issues", json={ "project_id": project1_id, "title": "Project 1 Issue", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = issue_response.json()["id"] # Try to access issue via project2 (IDOR attempt) response = await client.get( f"/api/v1/projects/{project2_id}/issues/{issue_id}", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_404_NOT_FOUND async def test_update_issue_wrong_project(self, client, user_token): """Test updating issue through wrong project.""" # Create two projects project1 = await client.post( "/api/v1/projects", json={"name": "Project A", "slug": "project-a-idor"}, headers={"Authorization": f"Bearer {user_token}"}, ) project2 = await client.post( "/api/v1/projects", json={"name": "Project B", "slug": "project-b-idor"}, headers={"Authorization": f"Bearer {user_token}"}, ) project1_id = project1.json()["id"] project2_id = project2.json()["id"] # Create issue in project1 issue_response = await client.post( f"/api/v1/projects/{project1_id}/issues", json={ "project_id": project1_id, "title": "Project A Issue", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = issue_response.json()["id"] # Try to update issue via project2 (IDOR attempt) response = await client.patch( f"/api/v1/projects/{project2_id}/issues/{issue_id}", json={"title": "Hacked Title"}, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_404_NOT_FOUND async def test_delete_issue_wrong_project(self, client, user_token): """Test deleting issue through wrong project.""" # Create two projects project1 = await client.post( "/api/v1/projects", json={"name": "Project X", "slug": "project-x-idor"}, headers={"Authorization": f"Bearer {user_token}"}, ) project2 = await client.post( "/api/v1/projects", json={"name": "Project Y", "slug": "project-y-idor"}, headers={"Authorization": f"Bearer {user_token}"}, ) project1_id = project1.json()["id"] project2_id = project2.json()["id"] # Create issue in project1 issue_response = await client.post( f"/api/v1/projects/{project1_id}/issues", json={ "project_id": project1_id, "title": "Project X Issue", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = issue_response.json()["id"] # Try to delete issue via project2 (IDOR attempt) response = await client.delete( f"/api/v1/projects/{project2_id}/issues/{issue_id}", headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_404_NOT_FOUND @pytest.mark.asyncio class TestIssueValidation: """Tests for issue validation during create/update.""" async def test_create_issue_invalid_priority(self, client, user_token, test_project): """Test creating issue with invalid priority.""" project_id = test_project["id"] response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Issue with Invalid Priority", "priority": "invalid_priority", }, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY async def test_create_issue_invalid_status(self, client, user_token, test_project): """Test creating issue with invalid status.""" project_id = test_project["id"] response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Issue with Invalid Status", "status": "invalid_status", }, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY async def test_update_issue_invalid_priority(self, client, user_token, test_project): """Test updating issue with invalid priority.""" project_id = test_project["id"] # Create issue create_response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Issue to Update", }, headers={"Authorization": f"Bearer {user_token}"}, ) issue_id = create_response.json()["id"] # Update with invalid priority response = await client.patch( f"/api/v1/projects/{project_id}/issues/{issue_id}", json={"priority": "invalid_priority"}, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY async def test_create_issue_with_nonexistent_sprint( self, client, user_token, test_project ): """Test creating issue with nonexistent sprint ID.""" project_id = test_project["id"] fake_sprint_id = str(uuid.uuid4()) response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Issue with Fake Sprint", "sprint_id": fake_sprint_id, }, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_404_NOT_FOUND async def test_create_issue_with_nonexistent_agent( self, client, user_token, test_project ): """Test creating issue with nonexistent agent ID.""" project_id = test_project["id"] fake_agent_id = str(uuid.uuid4()) response = await client.post( f"/api/v1/projects/{project_id}/issues", json={ "project_id": project_id, "title": "Issue with Fake Agent", "assigned_agent_id": fake_agent_id, }, headers={"Authorization": f"Bearer {user_token}"}, ) assert response.status_code == status.HTTP_404_NOT_FOUND