Skip to main content
Glama

Jira MCP Server

test_search_issues_v3_api.py19.8 kB
"""Test cases for search_issues V3 API client and server integration""" import asyncio from unittest.mock import Mock, patch, AsyncMock import pytest from src.mcp_server_jira.jira_v3_api import JiraV3APIClient from src.mcp_server_jira.server import JiraServer, JiraIssueResult class TestSearchIssuesV3API: """Test suite for search_issues V3 API client""" @pytest.mark.asyncio async def test_v3_api_search_issues_success(self): """Test successful search issues request with V3 API""" # Mock successful search response mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = { "issues": [ { "key": "PROJ-123", "fields": { "summary": "Test issue summary", "description": "Test issue description", "status": {"name": "Open"}, "assignee": {"displayName": "John Doe"}, "reporter": {"displayName": "Jane Smith"}, "created": "2023-01-01T00:00:00.000+0000", "updated": "2023-01-02T00:00:00.000+0000" } }, { "key": "PROJ-124", "fields": { "summary": "Another test issue", "description": "Another description", "status": {"name": "In Progress"}, "assignee": None, "reporter": {"displayName": "Bob Wilson"}, "created": "2023-01-03T00:00:00.000+0000", "updated": "2023-01-04T00:00:00.000+0000" } } ], "startAt": 0, "maxResults": 50, "total": 2, "isLast": True } mock_response.text = "" mock_response.raise_for_status.return_value = None # Mock httpx client mock_client = AsyncMock() mock_client.request.return_value = mock_response client = JiraV3APIClient( server_url="https://test.atlassian.net", username="testuser", token="testtoken" ) # Replace the client instance client.client = mock_client result = await client.search_issues( jql="project = PROJ", max_results=10 ) # Verify the request was made correctly mock_client.request.assert_called_once() call_args = mock_client.request.call_args assert call_args[1]["method"] == "GET" assert call_args[1]["url"] == "https://test.atlassian.net/rest/api/3/search/jql" assert call_args[1]["params"]["jql"] == "project = PROJ" assert call_args[1]["params"]["maxResults"] == 10 # Verify response assert result["total"] == 2 assert len(result["issues"]) == 2 assert result["issues"][0]["key"] == "PROJ-123" @pytest.mark.asyncio async def test_v3_api_search_issues_with_parameters(self): """Test search issues with optional parameters""" # Mock successful search response mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = { "issues": [], "startAt": 0, "maxResults": 25, "total": 0, "isLast": True } mock_response.text = "" mock_response.raise_for_status.return_value = None # Mock httpx client mock_client = AsyncMock() mock_client.request.return_value = mock_response client = JiraV3APIClient( server_url="https://test.atlassian.net", username="testuser", token="testtoken" ) # Replace the client instance client.client = mock_client result = await client.search_issues( jql="project = PROJ AND status = Open", start_at=10, max_results=25, fields="summary,status,assignee", expand="changelog" ) # Verify the request was made correctly mock_client.request.assert_called_once() call_args = mock_client.request.call_args assert call_args[1]["method"] == "GET" assert call_args[1]["url"] == "https://test.atlassian.net/rest/api/3/search/jql" params = call_args[1]["params"] assert params["jql"] == "project = PROJ AND status = Open" assert params["startAt"] == 10 assert params["maxResults"] == 25 assert params["fields"] == "summary,status,assignee" assert params["expand"] == "changelog" @pytest.mark.asyncio async def test_v3_api_search_issues_missing_jql(self): """Test search issues with missing JQL parameter""" client = JiraV3APIClient( server_url="https://test.atlassian.net", username="testuser", token="testtoken" ) with pytest.raises(ValueError, match="jql parameter is required"): await client.search_issues("") @pytest.mark.asyncio async def test_v3_api_search_issues_api_error(self): """Test search issues with API error response""" # Mock error response mock_response = Mock() mock_response.status_code = 400 mock_response.reason_phrase = "Bad Request" mock_response.json.return_value = {"errorMessages": ["Invalid JQL"]} from httpx import HTTPStatusError, Request, Response mock_request = Mock(spec=Request) mock_request.url = "https://test.atlassian.net/rest/api/3/search/jql" # Mock httpx client mock_client = AsyncMock() mock_client.request.side_effect = HTTPStatusError( "400 Bad Request", request=mock_request, response=mock_response ) client = JiraV3APIClient( server_url="https://test.atlassian.net", username="testuser", token="testtoken" ) # Replace the client instance client.client = mock_client with pytest.raises(ValueError, match="Jira API returned an error: 400"): await client.search_issues(jql="invalid jql syntax") class TestSearchIssuesJiraServer: """Test suite for search_issues in JiraServer class""" @pytest.mark.asyncio async def test_server_search_issues_success(self): """Test JiraServer search_issues method with successful V3 API response""" # Mock V3 API response mock_v3_response = { "issues": [ { "key": "TEST-1", "fields": { "summary": "Test Summary", "description": "Test Description", "status": {"name": "Open"}, "assignee": {"displayName": "Test User"}, "reporter": {"displayName": "Reporter User"}, "created": "2023-01-01T00:00:00.000+0000", "updated": "2023-01-02T00:00:00.000+0000" } } ] } # Mock V3 API client mock_v3_client = AsyncMock() mock_v3_client.search_issues.return_value = mock_v3_response # Create JiraServer instance and mock the V3 client server = JiraServer() server.server_url = "https://test.atlassian.net" server.username = "testuser" server.token = "testtoken" with patch.object(server, '_get_v3_api_client', return_value=mock_v3_client): result = await server.search_jira_issues("project = TEST", max_results=10) # Verify the result assert isinstance(result, list) assert len(result) == 1 issue = result[0] assert isinstance(issue, dict) assert issue["key"] == "TEST-1" assert issue["fields"]["summary"] == "Test Summary" assert issue["fields"]["description"] == "Test Description" assert issue["fields"]["status"]["name"] == "Open" assert issue["fields"]["assignee"]["displayName"] == "Test User" assert issue["fields"]["reporter"]["displayName"] == "Reporter User" assert issue["fields"]["created"] == "2023-01-01T00:00:00.000+0000" assert issue["fields"]["updated"] == "2023-01-02T00:00:00.000+0000" # Verify V3 client was called correctly mock_v3_client.search_issues.assert_called_once_with( jql="project = TEST", start_at=0, max_results=10 ) @pytest.mark.asyncio async def test_server_search_issues_handles_missing_fields(self): """Test JiraServer search_issues method handles missing optional fields gracefully""" # Mock V3 API response with minimal data mock_v3_response = { "issues": [ { "key": "TEST-2", "fields": { "summary": "Basic Summary", # Missing description, status, assignee, reporter, etc. } } ] } # Mock V3 API client mock_v3_client = AsyncMock() mock_v3_client.search_issues.return_value = mock_v3_response # Create JiraServer instance and mock the V3 client server = JiraServer() server.server_url = "https://test.atlassian.net" server.username = "testuser" server.token = "testtoken" with patch.object(server, '_get_v3_api_client', return_value=mock_v3_client): result = await server.search_jira_issues("project = TEST") # Verify the result handles missing fields gracefully assert isinstance(result, list) assert len(result) == 1 issue = result[0] assert isinstance(issue, dict) assert issue["key"] == "TEST-2" assert issue["fields"]["summary"] == "Basic Summary" # Missing description, status, assignee, reporter should be absent or None assert issue["fields"].get("description") is None assert issue["fields"].get("status") is None assert issue["fields"].get("assignee") is None assert issue["fields"].get("reporter") is None @pytest.mark.asyncio async def test_server_search_issues_api_error(self): """Test JiraServer search_issues method with API error""" # Mock V3 API client that raises an error mock_v3_client = AsyncMock() mock_v3_client.search_issues.side_effect = ValueError("API connection failed") # Create JiraServer instance and mock the V3 client server = JiraServer() server.server_url = "https://test.atlassian.net" server.username = "testuser" server.token = "testtoken" with patch.object(server, '_get_v3_api_client', return_value=mock_v3_client): with pytest.raises(ValueError, match="Failed to search issues"): await server.search_jira_issues("project = TEST") @pytest.mark.asyncio async def test_server_search_issues_pagination(self): """Test JiraServer search_issues method handles pagination correctly""" # Mock V3 API responses for pagination # First page response page1_response = { "issues": [ { "key": "TEST-1", "fields": { "summary": "First Issue", "description": "First Description", "status": {"name": "Open"}, "assignee": {"displayName": "User 1"}, "reporter": {"displayName": "Reporter 1"}, "created": "2023-01-01T00:00:00.000+0000", "updated": "2023-01-01T00:00:00.000+0000" } }, { "key": "TEST-2", "fields": { "summary": "Second Issue", "description": "Second Description", "status": {"name": "In Progress"}, "assignee": {"displayName": "User 2"}, "reporter": {"displayName": "Reporter 2"}, "created": "2023-01-02T00:00:00.000+0000", "updated": "2023-01-02T00:00:00.000+0000" } } ], "startAt": 0, "maxResults": 2, "total": 5, "isLast": False } # Second page response page2_response = { "issues": [ { "key": "TEST-3", "fields": { "summary": "Third Issue", "description": "Third Description", "status": {"name": "Done"}, "assignee": {"displayName": "User 3"}, "reporter": {"displayName": "Reporter 3"}, "created": "2023-01-03T00:00:00.000+0000", "updated": "2023-01-03T00:00:00.000+0000" } }, { "key": "TEST-4", "fields": { "summary": "Fourth Issue", "description": "Fourth Description", "status": {"name": "Closed"}, "assignee": None, "reporter": {"displayName": "Reporter 4"}, "created": "2023-01-04T00:00:00.000+0000", "updated": "2023-01-04T00:00:00.000+0000" } } ], "startAt": 2, "maxResults": 2, "total": 5, "isLast": False } # Third page response page3_response = { "issues": [ { "key": "TEST-5", "fields": { "summary": "Fifth Issue", "description": "Fifth Description", "status": {"name": "Open"}, "assignee": {"displayName": "User 5"}, "reporter": {"displayName": "Reporter 5"}, "created": "2023-01-05T00:00:00.000+0000", "updated": "2023-01-05T00:00:00.000+0000" } } ], "startAt": 4, "maxResults": 2, "total": 5, "isLast": True } # Mock V3 API client with side_effect to return different pages mock_v3_client = AsyncMock() mock_v3_client.search_issues.side_effect = [page1_response, page2_response, page3_response] # Create JiraServer instance and mock the V3 client server = JiraServer() server.server_url = "https://test.atlassian.net" server.username = "testuser" server.token = "testtoken" with patch.object(server, '_get_v3_api_client', return_value=mock_v3_client): result = await server.search_jira_issues("project = TEST", max_results=10) # Verify all issues from all pages were retrieved assert isinstance(result, list) assert len(result) == 5 # Check each issue dict assert isinstance(result[0], dict) assert result[0]["key"] == "TEST-1" assert result[0]["fields"]["summary"] == "First Issue" assert result[0]["fields"]["status"]["name"] == "Open" assert result[1]["key"] == "TEST-2" assert result[1]["fields"]["summary"] == "Second Issue" assert result[1]["fields"]["status"]["name"] == "In Progress" assert result[2]["key"] == "TEST-3" assert result[2]["fields"]["summary"] == "Third Issue" assert result[2]["fields"]["status"]["name"] == "Done" assert result[3]["key"] == "TEST-4" assert result[3]["fields"]["summary"] == "Fourth Issue" assert result[3]["fields"]["status"]["name"] == "Closed" # None handling assert result[3]["fields"].get("assignee") is None assert result[4]["key"] == "TEST-5" assert result[4]["fields"]["summary"] == "Fifth Issue" assert result[4]["fields"]["status"]["name"] == "Open" # Verify V3 client was called the correct number of times with correct parameters assert mock_v3_client.search_issues.call_count == 3 # Check first call first_call = mock_v3_client.search_issues.call_args_list[0] assert first_call[1]["jql"] == "project = TEST" assert first_call[1]["start_at"] == 0 assert first_call[1]["max_results"] == 10 # Check second call second_call = mock_v3_client.search_issues.call_args_list[1] assert second_call[1]["jql"] == "project = TEST" assert second_call[1]["start_at"] == 2 # After first 2 issues assert second_call[1]["max_results"] == 8 # Remaining needed: 10 - 2 = 8, min(8, 100) = 8 # Check third call third_call = mock_v3_client.search_issues.call_args_list[2] assert third_call[1]["jql"] == "project = TEST" assert third_call[1]["start_at"] == 4 # After first 4 issues assert third_call[1]["max_results"] == 6 # Remaining needed: 10 - 4 = 6, min(6, 100) = 6 @pytest.mark.asyncio async def test_server_search_issues_pagination_with_limit(self): """Test JiraServer search_issues method respects max_results when paginating""" # Mock V3 API responses for multiple pages, but we'll limit results page1_response = { "issues": [ {"key": "TEST-1", "fields": {"summary": "First Issue"}}, {"key": "TEST-2", "fields": {"summary": "Second Issue"}}, {"key": "TEST-3", "fields": {"summary": "Third Issue"}} ], "startAt": 0, "maxResults": 3, "total": 10, "isLast": False } page2_response = { "issues": [ {"key": "TEST-4", "fields": {"summary": "Fourth Issue"}}, {"key": "TEST-5", "fields": {"summary": "Fifth Issue"}} ], "startAt": 3, "maxResults": 2, # Only 2 more to reach our limit of 5 "total": 10, "isLast": False } # Mock V3 API client mock_v3_client = AsyncMock() mock_v3_client.search_issues.side_effect = [page1_response, page2_response] # Create JiraServer instance and mock the V3 client server = JiraServer() server.server_url = "https://test.atlassian.net" server.username = "testuser" server.token = "testtoken" with patch.object(server, '_get_v3_api_client', return_value=mock_v3_client): # Request only 5 results max result = await server.search_jira_issues("project = TEST", max_results=5) # Verify exactly 5 issues were returned (respecting max_results) assert isinstance(result, list) assert len(result) == 5 assert result[0]["key"] == "TEST-1" assert result[1]["key"] == "TEST-2" assert result[2]["key"] == "TEST-3" assert result[3]["key"] == "TEST-4" assert result[4]["key"] == "TEST-5" # Verify pagination stopped at the right point assert mock_v3_client.search_issues.call_count == 2

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/InfinitIQ-Tech/mcp-jira'

If you have feedback or need assistance with the MCP directory API, please join our Discord server