We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/amruthabesto28/JIRA-MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Tests for the Jira users module."""
from unittest.mock import MagicMock, patch
import pytest
import requests
from mcp_atlassian.jira.config import JiraConfig
from mcp_atlassian.jira.users import UsersMixin
class TestUsersMixin:
"""Tests for the UsersMixin class."""
@pytest.fixture
def users_mixin(self, jira_client):
"""Create a UsersMixin instance with mocked dependencies."""
mixin = UsersMixin(config=jira_client.config)
mixin.jira = jira_client.jira
return mixin
def test_get_current_user_account_id_cached(self, users_mixin):
"""Test that get_current_user_account_id returns cached value if available."""
# Set cached value
users_mixin._current_user_account_id = "cached-account-id"
# Call the method
account_id = users_mixin.get_current_user_account_id()
# Verify result
assert account_id == "cached-account-id"
# Verify the API wasn't called
users_mixin.jira.myself.assert_not_called()
def test_get_current_user_account_id_from_api(self, users_mixin):
"""Test that get_current_user_account_id calls the API if no cached value."""
# Ensure no cached value
users_mixin._current_user_account_id = None
# Mock the self.jira.myself() method
users_mixin.jira.myself = MagicMock(
return_value={"accountId": "test-account-id"}
)
# Call the method
account_id = users_mixin.get_current_user_account_id()
# Verify result
assert account_id == "test-account-id"
# Verify self.jira.myself was called
users_mixin.jira.myself.assert_called_once()
def test_get_current_user_account_id_data_center_timestamp_issue(self, users_mixin):
"""Test that get_current_user_account_id handles Jira Data Center with problematic timestamps."""
# Ensure no cached value
users_mixin._current_user_account_id = None
# Mock the self.jira.myself() method
users_mixin.jira.myself = MagicMock(
return_value={
"key": "jira-dc-user",
"name": "DC User",
"created": "9999-12-31T23:59:59.999+0000",
"lastLogin": "0000-01-01T00:00:00.000+0000",
}
)
# Call the method
account_id = users_mixin.get_current_user_account_id()
# Verify result - should extract key without timestamp parsing issues
assert account_id == "jira-dc-user"
# Verify self.jira.myself was called
users_mixin.jira.myself.assert_called_once()
def test_get_current_user_account_id_error(self, users_mixin):
"""Test that get_current_user_account_id handles errors."""
# Ensure no cached value
users_mixin._current_user_account_id = None
# Mock the self.jira.myself() method to raise an exception
users_mixin.jira.myself = MagicMock(
side_effect=requests.RequestException("API error")
)
# Call the method and verify it raises the expected exception
with pytest.raises(
Exception, match="Unable to get current user account ID: API error"
):
users_mixin.get_current_user_account_id()
# Verify self.jira.myself was called
users_mixin.jira.myself.assert_called_once()
def test_get_current_user_account_id_jira_data_center_key(self, users_mixin):
"""Test that get_current_user_account_id falls back to 'key' for Jira Data Center."""
# Ensure no cached value
users_mixin._current_user_account_id = None
# Mock the self.jira.myself() response with a Jira Data Center response
users_mixin.jira.myself = MagicMock(
return_value={"key": "jira-data-center-key", "name": "Test User"}
)
# Call the method
account_id = users_mixin.get_current_user_account_id()
# Verify result
assert account_id == "jira-data-center-key"
# Verify self.jira.myself was called
users_mixin.jira.myself.assert_called_once()
def test_get_current_user_account_id_jira_data_center_name(self, users_mixin):
"""Test that get_current_user_account_id falls back to 'name' when no 'key' or 'accountId'."""
# Ensure no cached value
users_mixin._current_user_account_id = None
# Mock the self.jira.myself() response with a Jira Data Center response
users_mixin.jira.myself = MagicMock(
return_value={"name": "jira-data-center-name"}
)
# Call the method
account_id = users_mixin.get_current_user_account_id()
# Verify result
assert account_id == "jira-data-center-name"
# Verify self.jira.myself was called
users_mixin.jira.myself.assert_called_once()
def test_get_current_user_account_id_no_identifiers(self, users_mixin):
"""Test that get_current_user_account_id raises error when no identifiers are found."""
# Ensure no cached value
users_mixin._current_user_account_id = None
# Mock the self.jira.myself() response with no identifiers
users_mixin.jira.myself = MagicMock(return_value={"someField": "someValue"})
# Call the method and verify it raises the expected exception
with pytest.raises(
Exception,
match="Unable to get current user account ID: Could not find accountId, key, or name in user data",
):
users_mixin.get_current_user_account_id()
# Verify self.jira.myself was called
users_mixin.jira.myself.assert_called_once()
def test_get_account_id_already_account_id(self, users_mixin):
"""Test that _get_account_id returns the input if it looks like an account ID."""
# Call the method with a string that looks like an account ID
account_id = users_mixin._get_account_id("5abcdef1234567890")
# Verify result
assert account_id == "5abcdef1234567890"
# Verify no lookups were performed
users_mixin.jira.user_find_by_user_string.assert_not_called()
def test_get_account_id_direct_lookup(self, users_mixin):
"""Test that _get_account_id uses direct lookup."""
# Mock both methods to avoid AttributeError
with (
patch.object(
users_mixin, "_lookup_user_directly", return_value="direct-account-id"
) as mock_direct,
patch.object(
users_mixin, "_lookup_user_by_permissions"
) as mock_permissions,
):
# Call the method
account_id = users_mixin._get_account_id("username")
# Verify result
assert account_id == "direct-account-id"
# Verify direct lookup was called
mock_direct.assert_called_once_with("username")
# Verify permissions lookup wasn't called
mock_permissions.assert_not_called()
def test_get_account_id_permissions_lookup(self, users_mixin):
"""Test that _get_account_id falls back to permissions lookup."""
# Mock direct lookup to return None
with (
patch.object(
users_mixin, "_lookup_user_directly", return_value=None
) as mock_direct,
patch.object(
users_mixin,
"_lookup_user_by_permissions",
return_value="permissions-account-id",
) as mock_permissions,
):
# Call the method
account_id = users_mixin._get_account_id("username")
# Verify result
assert account_id == "permissions-account-id"
# Verify both lookups were called
mock_direct.assert_called_once_with("username")
mock_permissions.assert_called_once_with("username")
def test_get_account_id_not_found(self, users_mixin):
"""Test that _get_account_id raises ValueError if user not found."""
# Mock both lookups to return None
with (
patch.object(users_mixin, "_lookup_user_directly", return_value=None),
patch.object(users_mixin, "_lookup_user_by_permissions", return_value=None),
):
# Call the method and verify it raises the expected exception
with pytest.raises(
ValueError, match="Could not find account ID for user: testuser"
):
users_mixin._get_account_id("testuser")
def test_lookup_user_directly(self, users_mixin):
"""Test _lookup_user_directly when user is found."""
# Mock the API response
users_mixin.jira.user_find_by_user_string.return_value = [
{
"accountId": "direct-account-id",
"displayName": "Test User",
"emailAddress": "test@example.com",
}
]
# Mock config.is_cloud to return True
users_mixin.config = MagicMock()
users_mixin.config.is_cloud = True
# Call the method
account_id = users_mixin._lookup_user_directly("Test User")
# Verify result
assert account_id == "direct-account-id"
# Verify API call with query parameter for Cloud
users_mixin.jira.user_find_by_user_string.assert_called_once_with(
query="Test User", start=0, limit=1
)
def test_lookup_user_directly_server_dc(self, users_mixin):
"""Test _lookup_user_directly for Server/DC when user is found."""
# Mock the API response
users_mixin.jira.user_find_by_user_string.return_value = [
{
"key": "server-user-key",
"name": "server-user-name",
"displayName": "Test User",
"emailAddress": "test@example.com",
}
]
# Mock config.is_cloud to return False for Server/DC
users_mixin.config = MagicMock()
users_mixin.config.is_cloud = False
# Call the method
account_id = users_mixin._lookup_user_directly("Test User")
# Verify result - should now return name instead of key for Server/DC
assert account_id == "server-user-name"
# Verify API call with username parameter for Server/DC
users_mixin.jira.user_find_by_user_string.assert_called_once_with(
username="Test User", start=0, limit=1
)
def test_lookup_user_directly_server_dc_key_fallback(self, users_mixin):
"""Test _lookup_user_directly for Server/DC falls back to key when name is not available."""
# Mock the API response
users_mixin.jira.user_find_by_user_string.return_value = [
{
"key": "server-user-key", # Only key, no name
"displayName": "Test User",
"emailAddress": "test@example.com",
}
]
# Mock config.is_cloud to return False for Server/DC
users_mixin.config = MagicMock()
users_mixin.config.is_cloud = False
# Call the method
account_id = users_mixin._lookup_user_directly("Test User")
# Verify result - should fallback to key when name is missing
assert account_id == "server-user-key"
# Verify API call with username parameter for Server/DC
users_mixin.jira.user_find_by_user_string.assert_called_once_with(
username="Test User", start=0, limit=1
)
def test_lookup_user_directly_not_found(self, users_mixin):
"""Test _lookup_user_directly when user is not found."""
# Mock empty API response
users_mixin.jira.user_find_by_user_string.return_value = []
# Mock config.is_cloud to return True (default case)
users_mixin.config = MagicMock()
users_mixin.config.is_cloud = True
# Call the method
account_id = users_mixin._lookup_user_directly("nonexistent")
# Verify result
assert account_id is None
def test_lookup_user_directly_jira_data_center_key(self, users_mixin):
"""Test _lookup_user_directly when only 'key' is available (Data Center)."""
# Mock the API response for Jira Data Center (has key but no accountId)
users_mixin.jira.user_find_by_user_string.return_value = [
{
"key": "data-center-key",
"displayName": "Test User",
"emailAddress": "test@example.com",
}
]
# Mock config.is_cloud to return False for Server/DC
users_mixin.config = MagicMock()
users_mixin.config.is_cloud = False
# Call the method
account_id = users_mixin._lookup_user_directly("Test User")
# Verify result
assert account_id == "data-center-key"
# Verify API call
users_mixin.jira.user_find_by_user_string.assert_called_once_with(
username="Test User", start=0, limit=1
)
def test_lookup_user_directly_jira_data_center_name(self, users_mixin):
"""Test _lookup_user_directly when only 'name' is available (Data Center)."""
# Mock the API response for Jira Data Center (has name but no accountId or key)
users_mixin.jira.user_find_by_user_string.return_value = [
{
"name": "data-center-name",
"displayName": "Test User",
"emailAddress": "test@example.com",
}
]
# Mock config.is_cloud to return False for Server/DC
users_mixin.config = MagicMock()
users_mixin.config.is_cloud = False
# Call the method
account_id = users_mixin._lookup_user_directly("Test User")
# Verify result
assert account_id == "data-center-name"
# Verify API call
users_mixin.jira.user_find_by_user_string.assert_called_once_with(
username="Test User", start=0, limit=1
)
def test_lookup_user_directly_error(self, users_mixin):
"""Test _lookup_user_directly when API call fails."""
# Mock API call to raise exception
users_mixin.jira.user_find_by_user_string.side_effect = Exception("API error")
# Call the method
account_id = users_mixin._lookup_user_directly("error")
# Verify result
assert account_id is None
def test_lookup_user_by_permissions(self, users_mixin):
"""Test _lookup_user_by_permissions when user is found."""
# Mock requests.get
with patch("requests.get") as mock_get:
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"users": [{"accountId": "permissions-account-id"}]
}
mock_get.return_value = mock_response
# Call the method
account_id = users_mixin._lookup_user_by_permissions("username")
# Verify result
assert account_id == "permissions-account-id"
# Verify API call
mock_get.assert_called_once()
assert mock_get.call_args[0][0].endswith("/user/permission/search")
assert mock_get.call_args[1]["params"] == {
"query": "username",
"permissions": "BROWSE",
}
def test_lookup_user_by_permissions_not_found(self, users_mixin):
"""Test _lookup_user_by_permissions when user is not found."""
# Mock requests.get
with patch("requests.get") as mock_get:
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {"users": []}
mock_get.return_value = mock_response
# Call the method
account_id = users_mixin._lookup_user_by_permissions("nonexistent")
# Verify result
assert account_id is None
def test_lookup_user_by_permissions_jira_data_center(self, users_mixin):
"""Test _lookup_user_by_permissions when both 'key' and 'name' are available (Data Center)."""
# Mock requests.get
with patch("requests.get") as mock_get:
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"users": [
{
"key": "data-center-permissions-key",
"name": "data-center-permissions-name",
}
]
}
mock_get.return_value = mock_response
# Mock config.is_cloud to return False for Server/DC
users_mixin.config = MagicMock()
users_mixin.config.is_cloud = False
# Call the method
account_id = users_mixin._lookup_user_by_permissions("username")
# Verify result - should prioritize name for Server/DC
assert account_id == "data-center-permissions-name"
# Verify API call
mock_get.assert_called_once()
assert mock_get.call_args[0][0].endswith("/user/permission/search")
assert mock_get.call_args[1]["params"] == {
"query": "username",
"permissions": "BROWSE",
}
def test_lookup_user_by_permissions_jira_data_center_key_fallback(
self, users_mixin
):
"""Test _lookup_user_by_permissions when only 'key' is available (Data Center)."""
# Mock requests.get
with patch("requests.get") as mock_get:
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"users": [{"key": "data-center-permissions-key"}]
}
mock_get.return_value = mock_response
# Mock config.is_cloud to return False for Server/DC
users_mixin.config = MagicMock()
users_mixin.config.is_cloud = False
# Call the method
account_id = users_mixin._lookup_user_by_permissions("username")
# Verify result - should fallback to key when name is missing
assert account_id == "data-center-permissions-key"
# Verify API call
mock_get.assert_called_once()
assert mock_get.call_args[0][0].endswith("/user/permission/search")
assert mock_get.call_args[1]["params"] == {
"query": "username",
"permissions": "BROWSE",
}
def test_lookup_user_by_permissions_error(self, users_mixin):
"""Test _lookup_user_by_permissions when API call fails."""
# Mock requests.get to raise exception
with patch("requests.get", side_effect=Exception("API error")):
# Call the method
account_id = users_mixin._lookup_user_by_permissions("error")
# Verify result
assert account_id is None
def test_lookup_user_by_permissions_jira_data_center_name_only(self, users_mixin):
"""Test _lookup_user_by_permissions when only 'name' is available (Data Center)."""
# Mock requests.get
with patch("requests.get") as mock_get:
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"users": [{"name": "data-center-permissions-name"}]
}
mock_get.return_value = mock_response
# Mock config.is_cloud to return False for Server/DC
users_mixin.config = MagicMock()
users_mixin.config.is_cloud = False
# Call the method
account_id = users_mixin._lookup_user_by_permissions("username")
# Verify result - should use name when that's all that's available
assert account_id == "data-center-permissions-name"
# Verify API call
mock_get.assert_called_once()
assert mock_get.call_args[0][0].endswith("/user/permission/search")
assert mock_get.call_args[1]["params"] == {
"query": "username",
"permissions": "BROWSE",
}
def test_get_user_profile_by_identifier_cloud_account_id(self, users_mixin):
"""Test get_user_profile_by_identifier with Cloud and accountId."""
users_mixin.config = MagicMock(spec=JiraConfig)
users_mixin.config.is_cloud = True
with patch(
"src.mcp_atlassian.jira.users.JiraUser.from_api_response"
) as mock_from_api_response:
mock_user_instance = MagicMock()
mock_from_api_response.return_value = mock_user_instance
mock_response_data = {
"accountId": "5b10ac8d82e05b22cc7d4ef5",
"displayName": "Cloud User",
"emailAddress": "cloud@example.com",
"active": True,
}
users_mixin.jira.user = MagicMock(return_value=mock_response_data)
test_account_id = "5b10ac8d82e05b22cc7d4ef5"
user = users_mixin.get_user_profile_by_identifier(test_account_id)
assert user == mock_user_instance
users_mixin.jira.user.assert_called_once_with(account_id=test_account_id)
mock_from_api_response.assert_called_once_with(mock_response_data)
def test_get_user_profile_by_identifier_server_username(self, users_mixin):
"""Test get_user_profile_by_identifier with Server/DC and username."""
users_mixin.config = MagicMock(spec=JiraConfig)
users_mixin.config.is_cloud = False
with patch(
"src.mcp_atlassian.jira.users.JiraUser.from_api_response"
) as mock_from_api_response:
mock_user_instance = MagicMock()
mock_from_api_response.return_value = mock_user_instance
mock_response_data = {
"name": "server_user",
"displayName": "Server User",
"emailAddress": "server@example.com",
"active": True,
}
users_mixin.jira.user = MagicMock(return_value=mock_response_data)
user = users_mixin.get_user_profile_by_identifier("server_user")
assert user == mock_user_instance
users_mixin.jira.user.assert_called_once_with(username="server_user")
mock_from_api_response.assert_called_once_with(mock_response_data)
def test_get_user_profile_by_identifier_cloud_email(self, users_mixin):
"""Test get_user_profile_by_identifier with Cloud and email."""
users_mixin.config = MagicMock(spec=JiraConfig)
users_mixin.config.is_cloud = True
users_mixin._lookup_user_directly = MagicMock(
return_value="5b10ac8d82e05b22cc7d4ef5"
)
with patch(
"src.mcp_atlassian.jira.users.JiraUser.from_api_response"
) as mock_from_api_response:
mock_user_instance = MagicMock()
mock_from_api_response.return_value = mock_user_instance
mock_response_data = {
"accountId": "5b10ac8d82e05b22cc7d4ef5",
"displayName": "Email User",
"emailAddress": "email@example.com",
"active": True,
}
users_mixin.jira.user = MagicMock(return_value=mock_response_data)
user = users_mixin.get_user_profile_by_identifier("email@example.com")
assert user == mock_user_instance
users_mixin.jira.user.assert_called_once_with(
account_id="5b10ac8d82e05b22cc7d4ef5"
)
users_mixin._lookup_user_directly.assert_called_once_with(
"email@example.com"
)
mock_from_api_response.assert_called_once_with(mock_response_data)
def test_get_user_profile_by_identifier_not_found(self, users_mixin):
"""Test get_user_profile_by_identifier when user is not found (404 or cannot resolve)."""
users_mixin.config = MagicMock(spec=JiraConfig)
users_mixin.config.is_cloud = True
users_mixin._lookup_user_directly = MagicMock(return_value=None)
users_mixin._lookup_user_by_permissions = MagicMock(return_value=None)
# Simulate the identifier cannot be resolved to an account ID
with pytest.raises(
ValueError, match="Could not determine how to look up user 'nonexistent'."
):
users_mixin.get_user_profile_by_identifier("nonexistent")
def test_get_user_profile_by_identifier_permission_error(self, users_mixin):
"""Test get_user_profile_by_identifier with a permission error (403)."""
users_mixin.config = MagicMock(spec=JiraConfig)
users_mixin.config.is_cloud = True
users_mixin._get_account_id = MagicMock(
return_value="account-id-for-restricted"
)
mock_response = MagicMock(spec=requests.Response)
mock_response.status_code = 403
http_error = requests.exceptions.HTTPError(response=mock_response)
users_mixin.jira.user = MagicMock(side_effect=http_error)
from mcp_atlassian.exceptions import MCPAtlassianAuthenticationError
with pytest.raises(
MCPAtlassianAuthenticationError,
match="Permission denied accessing user 'restricted_user'.",
):
users_mixin.get_user_profile_by_identifier("restricted_user")
def test_get_user_profile_by_identifier_api_error(self, users_mixin):
"""Test get_user_profile_by_identifier with a generic API error."""
# Mock config
users_mixin.config = MagicMock(spec=JiraConfig)
users_mixin.config.is_cloud = True
# Mock resolution methods to succeed
users_mixin._get_account_id = MagicMock(return_value="account-id-for-error")
# Mock API to raise a generic exception
users_mixin.jira.user = MagicMock(side_effect=Exception("Network Timeout"))
# Call method and assert generic Exception
with pytest.raises(
Exception, match="Error processing user profile for 'error_user'"
):
users_mixin.get_user_profile_by_identifier("error_user")