test_jira_v3_api.py•15.9 kB
# pylint: disable=import-error, protected-access
"""
Tests for the Jira v3 API client functionality.
"""
from unittest.mock import Mock, patch
import pytest  # pylint: disable=import-error
from src.mcp_server_jira.jira_v3_api import JiraV3APIClient
class TestJiraV3APIClient:
    """Test suite for JiraV3APIClient"""
    def test_init_with_username_password(self):
        """Test initialization with username and password"""
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            password="testpass",
        )
        assert client.server_url == "https://test.atlassian.net"
        assert client.username == "testuser"
        assert client.password == "testpass"
        assert client.token is None
    def test_init_with_token(self):
        """Test initialization with token only"""
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net", token="test-token"
        )
        assert client.server_url == "https://test.atlassian.net"
        assert client.username is None
        assert client.password is None
        assert client.token == "test-token"
    @patch("src.mcp_server_jira.jira_v3_api.requests.request")
    def test_make_v3_api_request_success(self, mock_request):
        """Test successful API request"""
        # Setup mock response
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"key": "TEST", "name": "Test Project"}
        mock_request.return_value = mock_response
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        result = client._make_v3_api_request("POST", "/project", {"test": "data"})
        assert result == {"key": "TEST", "name": "Test Project"}
        mock_request.assert_called_once()
    @patch("src.mcp_server_jira.jira_v3_api.requests.request")
    def test_make_v3_api_request_error(self, mock_request):
        """Test API request with error response"""
        # Setup mock error response
        mock_response = Mock()
        mock_response.status_code = 400
        mock_response.json.return_value = {"errorMessages": ["Bad request"]}
        mock_request.return_value = mock_response
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        with pytest.raises(ValueError, match="HTTP 400"):
            client._make_v3_api_request("POST", "/project", {"test": "data"})
    @patch("src.mcp_server_jira.jira_v3_api.requests.request")
    def test_create_project_success(self, mock_request):
        """Test successful project creation"""
        # Setup mock response
        mock_response = Mock()
        mock_response.status_code = 201
        mock_response.json.return_value = {
            "self": "https://test.atlassian.net/rest/api/3/project/10000",
            "id": "10000",
            "key": "TEST",
            "name": "Test Project",
        }
        mock_request.return_value = mock_response
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        result = client.create_project(
            key="TEST", name="Test Project", ptype="software"
        )
        assert result["key"] == "TEST"
        assert result["name"] == "Test Project"
        mock_request.assert_called_once()
        # Verify the request was made with correct data
        call_args = mock_request.call_args
        assert call_args[1]["method"] == "POST"
        assert "/rest/api/3/project" in call_args[1]["url"]
        # Check the request body
        request_data = call_args[1]["json"]
        assert request_data["key"] == "TEST"
        assert request_data["name"] == "Test Project"
        assert request_data["projectTypeKey"] == "software"
        assert request_data["assigneeType"] == "PROJECT_LEAD"
    @patch("src.mcp_server_jira.jira_v3_api.requests.request")
    def test_create_project_with_template(self, mock_request):
        """Test project creation with template"""
        # Setup mock response
        mock_response = Mock()
        mock_response.status_code = 201
        mock_response.json.return_value = {
            "self": "https://test.atlassian.net/rest/api/3/project/10000",
            "id": "10000",
            "key": "TEMP",
            "name": "Template Project",
        }
        mock_request.return_value = mock_response
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        result = client.create_project(
            key="TEMP",
            name="Template Project",
            ptype="business",
            template_name="com.atlassian.jira-core-project-templates:jira-core-project-management",
            assignee="user123",
        )
        assert result["key"] == "TEMP"
        mock_request.assert_called_once()
        # Verify the request data includes template information
        call_args = mock_request.call_args
        request_data = call_args[1]["json"]
        assert (
            request_data["projectTemplateKey"]
            == "com.atlassian.jira-core-project-templates:jira-core-project-management"
        )
        assert request_data["leadAccountId"] == "user123"
        assert request_data["projectTypeKey"] == "business"
    def test_create_project_missing_key(self):
        """Test project creation with missing key"""
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        with pytest.raises(ValueError, match="Project key is required"):
            client.create_project(key="")
    def test_create_project_missing_assignee(self):
        """Test project creation with missing assignee"""
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        with pytest.raises(ValueError, match="Parameter 'assignee'"):
            client.create_project(key="TEST")
    @patch("src.mcp_server_jira.jira_v3_api.requests.request")
    def test_authentication_username_token(self, mock_request):
        """Test authentication with username and token"""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"test": "data"}
        mock_request.return_value = mock_response
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        client._make_v3_api_request("GET", "/test")
        call_args = mock_request.call_args
        auth = call_args[1]["auth"]
        assert auth == ("testuser", "testtoken")
    @patch("src.mcp_server_jira.jira_v3_api.requests.request")
    def test_authentication_token_only(self, mock_request):
        """Test authentication with token only"""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"test": "data"}
        mock_request.return_value = mock_response
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net", token="testtoken"
        )
        client._make_v3_api_request("GET", "/test")
        call_args = mock_request.call_args
        headers = call_args[1]["headers"]
        assert "Authorization" in headers
        assert headers["Authorization"] == "Bearer testtoken"
    @patch("src.mcp_server_jira.jira_v3_api.requests.request")
    def test_get_projects_success(self, mock_request):
        """Test successful get projects request"""
        # Setup mock response
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "startAt": 0,
            "maxResults": 50,
            "total": 2,
            "isLast": True,
            "values": [
                {
                    "id": "10000",
                    "key": "TEST",
                    "name": "Test Project",
                    "lead": {"displayName": "John Doe"}
                },
                {
                    "id": "10001",
                    "key": "DEMO", 
                    "name": "Demo Project",
                    "lead": {"displayName": "Jane Smith"}
                }
            ]
        }
        mock_request.return_value = mock_response
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        result = client.get_projects()
        assert result["total"] == 2
        assert len(result["values"]) == 2
        assert result["values"][0]["key"] == "TEST"
        assert result["values"][1]["key"] == "DEMO"
        mock_request.assert_called_once()
        # Verify the request was made to the correct endpoint
        call_args = mock_request.call_args
        assert call_args[1]["method"] == "GET"
        assert "/rest/api/3/project/search" in call_args[1]["url"]
    @patch("src.mcp_server_jira.jira_v3_api.requests.request")
    def test_get_projects_with_parameters(self, mock_request):
        """Test get projects with query parameters"""
        # Setup mock response
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "startAt": 10,
            "maxResults": 20,
            "total": 50,
            "isLast": False,
            "values": []
        }
        mock_request.return_value = mock_response
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        result = client.get_projects(
            start_at=10,
            max_results=20,
            order_by="name",
            query="test",
            keys=["PROJ1", "PROJ2"]
        )
        assert result["startAt"] == 10
        assert result["maxResults"] == 20
        mock_request.assert_called_once()
        # Verify the request URL includes query parameters
        call_args = mock_request.call_args
        url = call_args[1]["url"]
        assert "startAt=10" in url
        assert "maxResults=20" in url
        assert "orderBy=name" in url
        assert "query=test" in url
        assert "keys=PROJ1,PROJ2" in url
    @patch("src.mcp_server_jira.jira_v3_api.requests.request")
    def test_get_projects_error(self, mock_request):
        """Test get projects with error response"""
        # Setup mock error response
        mock_response = Mock()
        mock_response.status_code = 401
        mock_response.json.return_value = {"errorMessages": ["Unauthorized"]}
        mock_request.return_value = mock_response
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        with pytest.raises(ValueError, match="HTTP 401"):
            client.get_projects()
    @patch("src.mcp_server_jira.jira_v3_api.httpx.AsyncClient")
    async def test_get_transitions_success(self, mock_async_client):
        """Test successful get transitions request"""
        # Setup mock response
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "transitions": [
                {
                    "id": "2",
                    "name": "Close Issue",
                    "to": {
                        "id": "10000",
                        "name": "Done",
                        "description": "Issue is done"
                    },
                    "hasScreen": False,
                    "isAvailable": True,
                    "isConditional": False,
                    "isGlobal": False,
                    "isInitial": False
                },
                {
                    "id": "711",
                    "name": "QA Review",
                    "to": {
                        "id": "5",
                        "name": "In Review",
                        "description": "Issue is under review"
                    },
                    "hasScreen": True,
                    "isAvailable": True,
                    "isConditional": False,
                    "isGlobal": False,
                    "isInitial": False
                }
            ]
        }
        mock_response.raise_for_status.return_value = None
        
        # Setup mock client
        mock_client_instance = Mock()
        mock_client_instance.request.return_value = mock_response
        mock_async_client.return_value = mock_client_instance
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        result = await client.get_transitions("PROJ-123")
        assert "transitions" in result
        assert len(result["transitions"]) == 2
        assert result["transitions"][0]["id"] == "2"
        assert result["transitions"][0]["name"] == "Close Issue"
        assert result["transitions"][1]["id"] == "711"
        assert result["transitions"][1]["name"] == "QA Review"
        
        # Verify the request was made with correct parameters
        mock_client_instance.request.assert_called_once()
        call_args = mock_client_instance.request.call_args
        assert call_args[1]["method"] == "GET"
        assert "/rest/api/3/issue/PROJ-123/transitions" in call_args[1]["url"]
    @patch("src.mcp_server_jira.jira_v3_api.httpx.AsyncClient")
    async def test_get_transitions_with_parameters(self, mock_async_client):
        """Test get transitions with query parameters"""
        # Setup mock response
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"transitions": []}
        mock_response.raise_for_status.return_value = None
        
        # Setup mock client
        mock_client_instance = Mock()
        mock_client_instance.request.return_value = mock_response
        mock_async_client.return_value = mock_client_instance
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        await client.get_transitions(
            issue_id_or_key="PROJ-123",
            expand="transitions.fields",
            transition_id="2",
            skip_remote_only_condition=True,
            include_unavailable_transitions=False,
            sort_by_ops_bar_and_status=True
        )
        # Verify the request was made with correct parameters
        mock_client_instance.request.assert_called_once()
        call_args = mock_client_instance.request.call_args
        assert call_args[1]["method"] == "GET"
        
        params = call_args[1]["params"]
        assert params["expand"] == "transitions.fields"
        assert params["transitionId"] == "2"
        assert params["skipRemoteOnlyCondition"] is True
        assert params["includeUnavailableTransitions"] is False
        assert params["sortByOpsBarAndStatus"] is True
    async def test_get_transitions_missing_issue_key(self):
        """Test get transitions with missing issue key"""
        client = JiraV3APIClient(
            server_url="https://test.atlassian.net",
            username="testuser",
            token="testtoken",
        )
        with pytest.raises(ValueError, match="issue_id_or_key is required"):
            await client.get_transitions("")