Skip to main content
Glama

MCP Atlassian

by ArconixForge
test_users.py21.2 kB
"""Unit tests for the Confluence users module.""" from unittest.mock import MagicMock, patch import pytest from requests.exceptions import HTTPError from mcp_atlassian.confluence.users import UsersMixin from mcp_atlassian.exceptions import MCPAtlassianAuthenticationError class TestUsersMixin: """Tests for the UsersMixin class.""" @pytest.fixture def users_mixin(self, confluence_client): """Create a UsersMixin instance for testing.""" # UsersMixin inherits from ConfluenceClient, so we need to create it properly with patch( "mcp_atlassian.confluence.users.ConfluenceClient.__init__" ) as mock_init: mock_init.return_value = None mixin = UsersMixin() # Copy the necessary attributes from our mocked client mixin.confluence = confluence_client.confluence mixin.config = confluence_client.config return mixin # Mock user data for different scenarios @pytest.fixture def mock_user_data_cloud(self): """Mock user data for Confluence Cloud.""" return { "accountId": "5b10ac8d82e05b22cc7d4ef5", "accountType": "atlassian", "email": "user@example.com", "publicName": "Test User", "displayName": "Test User", "profilePicture": { "path": "/wiki/aa-avatar/5b10ac8d82e05b22cc7d4ef5", "width": 48, "height": 48, "isDefault": False, }, "isExternalCollaborator": False, "accountStatus": "active", } @pytest.fixture def mock_user_data_server(self): """Mock user data for Confluence Server/DC.""" return { "username": "testuser", "userKey": "testuser-key-12345", "displayName": "Test User", "fullName": "Test User Full Name", "email": "testuser@example.com", "status": "active", } @pytest.fixture def mock_user_data_with_status(self): """Mock user data with status expansion.""" return { "accountId": "5b10ac8d82e05b22cc7d4ef5", "accountType": "atlassian", "email": "user@example.com", "publicName": "Test User", "displayName": "Test User", "accountStatus": "active", "status": "Active", # Expanded status field } @pytest.fixture def mock_current_user_data(self): """Mock current user data for get_current_user_info.""" return { "accountId": "5b10ac8d82e05b22cc7d4ef5", "type": "known", "accountType": "atlassian", "email": "current@example.com", "publicName": "Current User", "displayName": "Current User", "profilePicture": { "path": "/wiki/aa-avatar/5b10ac8d82e05b22cc7d4ef5", "width": 48, "height": 48, "isDefault": False, }, "isExternalCollaborator": False, "isGuest": False, "locale": "en_US", "accountStatus": "active", } def test_get_user_details_by_accountid_success( self, users_mixin, mock_user_data_cloud ): """Test successfully getting user details by account ID.""" # Arrange account_id = "5b10ac8d82e05b22cc7d4ef5" users_mixin.confluence.get_user_details_by_accountid.return_value = ( mock_user_data_cloud ) # Act result = users_mixin.get_user_details_by_accountid(account_id) # Assert users_mixin.confluence.get_user_details_by_accountid.assert_called_once_with( account_id, None ) assert result == mock_user_data_cloud assert result["accountId"] == account_id assert result["displayName"] == "Test User" def test_get_user_details_by_accountid_with_expand( self, users_mixin, mock_user_data_with_status ): """Test getting user details by account ID with status expansion.""" # Arrange account_id = "5b10ac8d82e05b22cc7d4ef5" expand = "status" users_mixin.confluence.get_user_details_by_accountid.return_value = ( mock_user_data_with_status ) # Act result = users_mixin.get_user_details_by_accountid(account_id, expand=expand) # Assert users_mixin.confluence.get_user_details_by_accountid.assert_called_once_with( account_id, expand ) assert result == mock_user_data_with_status assert result["status"] == "Active" assert result["accountStatus"] == "active" def test_get_user_details_by_accountid_invalid_account_id(self, users_mixin): """Test getting user details with invalid account ID.""" # Arrange invalid_account_id = "invalid-account-id" users_mixin.confluence.get_user_details_by_accountid.side_effect = Exception( "User not found" ) # Act/Assert with pytest.raises(Exception, match="User not found"): users_mixin.get_user_details_by_accountid(invalid_account_id) def test_get_user_details_by_username_success( self, users_mixin, mock_user_data_server ): """Test successfully getting user details by username.""" # Arrange username = "testuser" users_mixin.confluence.get_user_details_by_username.return_value = ( mock_user_data_server ) # Act result = users_mixin.get_user_details_by_username(username) # Assert users_mixin.confluence.get_user_details_by_username.assert_called_once_with( username, None ) assert result == mock_user_data_server assert result["username"] == username assert result["displayName"] == "Test User" def test_get_user_details_by_username_with_expand( self, users_mixin, mock_user_data_server ): """Test getting user details by username with status expansion.""" # Arrange username = "testuser" expand = "status" mock_data_with_status = mock_user_data_server.copy() mock_data_with_status["status"] = "Active" users_mixin.confluence.get_user_details_by_username.return_value = ( mock_data_with_status ) # Act result = users_mixin.get_user_details_by_username(username, expand=expand) # Assert users_mixin.confluence.get_user_details_by_username.assert_called_once_with( username, expand ) assert result == mock_data_with_status assert result["status"] == "Active" def test_get_user_details_by_username_invalid_username(self, users_mixin): """Test getting user details with invalid username.""" # Arrange invalid_username = "nonexistent-user" users_mixin.confluence.get_user_details_by_username.side_effect = Exception( "User not found" ) # Act/Assert with pytest.raises(Exception, match="User not found"): users_mixin.get_user_details_by_username(invalid_username) def test_get_user_details_by_username_server_dc_pattern( self, users_mixin, mock_user_data_server ): """Test that username lookup follows Server/DC patterns.""" # Arrange username = "dc.user@example.com" # Email-like username common in DC users_mixin.confluence.get_user_details_by_username.return_value = ( mock_user_data_server ) # Act result = users_mixin.get_user_details_by_username(username) # Assert users_mixin.confluence.get_user_details_by_username.assert_called_once_with( username, None ) assert result == mock_user_data_server def test_get_current_user_info_success(self, users_mixin, mock_current_user_data): """Test successfully getting current user info.""" # Arrange users_mixin.confluence.get.return_value = mock_current_user_data # Act result = users_mixin.get_current_user_info() # Assert users_mixin.confluence.get.assert_called_once_with("rest/api/user/current") assert result == mock_current_user_data assert result["accountId"] == "5b10ac8d82e05b22cc7d4ef5" assert result["displayName"] == "Current User" def test_get_current_user_info_returns_non_dict(self, users_mixin): """Test get_current_user_info when API returns non-dict data.""" # Arrange users_mixin.confluence.get.return_value = "Invalid response" # Act/Assert with pytest.raises( MCPAtlassianAuthenticationError, match="Confluence token validation failed: Did not receive valid JSON user data", ): users_mixin.get_current_user_info() users_mixin.confluence.get.assert_called_once_with("rest/api/user/current") def test_get_current_user_info_returns_none(self, users_mixin): """Test get_current_user_info when API returns None.""" # Arrange users_mixin.confluence.get.return_value = None # Act/Assert with pytest.raises( MCPAtlassianAuthenticationError, match="Confluence token validation failed: Did not receive valid JSON user data", ): users_mixin.get_current_user_info() def test_get_current_user_info_http_error_401(self, users_mixin): """Test get_current_user_info with 401 authentication error.""" # Arrange mock_response = MagicMock() mock_response.status_code = 401 http_error = HTTPError(response=mock_response) users_mixin.confluence.get.side_effect = http_error # Act/Assert with pytest.raises( MCPAtlassianAuthenticationError, match="Confluence token validation failed: 401 from /rest/api/user/current", ): users_mixin.get_current_user_info() def test_get_current_user_info_http_error_403(self, users_mixin): """Test get_current_user_info with 403 forbidden error.""" # Arrange mock_response = MagicMock() mock_response.status_code = 403 http_error = HTTPError(response=mock_response) users_mixin.confluence.get.side_effect = http_error # Act/Assert with pytest.raises( MCPAtlassianAuthenticationError, match="Confluence token validation failed: 403 from /rest/api/user/current", ): users_mixin.get_current_user_info() def test_get_current_user_info_http_error_other(self, users_mixin): """Test get_current_user_info with other HTTP error codes.""" # Arrange mock_response = MagicMock() mock_response.status_code = 500 http_error = HTTPError(response=mock_response) users_mixin.confluence.get.side_effect = http_error # Act/Assert with pytest.raises( MCPAtlassianAuthenticationError, match="Confluence token validation failed with HTTPError", ): users_mixin.get_current_user_info() def test_get_current_user_info_http_error_no_response(self, users_mixin): """Test get_current_user_info with HTTPError but no response object.""" # Arrange http_error = HTTPError() http_error.response = None users_mixin.confluence.get.side_effect = http_error # Act/Assert with pytest.raises( MCPAtlassianAuthenticationError, match="Confluence token validation failed with HTTPError", ): users_mixin.get_current_user_info() def test_get_current_user_info_generic_exception(self, users_mixin): """Test get_current_user_info with generic exception.""" # Arrange users_mixin.confluence.get.side_effect = ConnectionError("Network error") # Act/Assert with pytest.raises( MCPAtlassianAuthenticationError, match="Confluence token validation failed: Network error", ): users_mixin.get_current_user_info() @pytest.mark.parametrize( "expand_param", [ None, "status", "", # Empty string ], ) def test_get_user_details_by_accountid_expand_parameter_handling( self, users_mixin, mock_user_data_cloud, expand_param ): """Test that expand parameter is properly handled for account ID lookup.""" # Arrange account_id = "5b10ac8d82e05b22cc7d4ef5" expected_data = mock_user_data_cloud.copy() if expand_param == "status": expected_data["status"] = "Active" users_mixin.confluence.get_user_details_by_accountid.return_value = ( expected_data ) # Act result = users_mixin.get_user_details_by_accountid(account_id, expand_param) # Assert users_mixin.confluence.get_user_details_by_accountid.assert_called_once_with( account_id, expand_param ) assert result == expected_data @pytest.mark.parametrize( "expand_param", [ None, "status", "", # Empty string ], ) def test_get_user_details_by_username_expand_parameter_handling( self, users_mixin, mock_user_data_server, expand_param ): """Test that expand parameter is properly handled for username lookup.""" # Arrange username = "testuser" expected_data = mock_user_data_server.copy() if expand_param == "status": expected_data["status"] = "Active" users_mixin.confluence.get_user_details_by_username.return_value = expected_data # Act result = users_mixin.get_user_details_by_username(username, expand_param) # Assert users_mixin.confluence.get_user_details_by_username.assert_called_once_with( username, expand_param ) assert result == expected_data def test_users_mixin_inheritance(self, users_mixin): """Test that UsersMixin properly inherits from ConfluenceClient.""" # Verify that UsersMixin is indeed a ConfluenceClient from mcp_atlassian.confluence.client import ConfluenceClient assert isinstance(users_mixin, ConfluenceClient) # Verify it has the expected attributes from ConfluenceClient assert hasattr(users_mixin, "confluence") assert hasattr(users_mixin, "config") def test_users_mixin_has_required_methods(self): """Test that UsersMixin has all required methods.""" # Verify the mixin has the expected methods assert hasattr(UsersMixin, "get_user_details_by_accountid") assert hasattr(UsersMixin, "get_user_details_by_username") assert hasattr(UsersMixin, "get_current_user_info") # Verify method signatures import inspect # Check get_user_details_by_accountid signature sig = inspect.signature(UsersMixin.get_user_details_by_accountid) params = list(sig.parameters.keys()) assert "self" in params assert "account_id" in params assert "expand" in params assert sig.parameters["expand"].default is None # Check get_user_details_by_username signature sig = inspect.signature(UsersMixin.get_user_details_by_username) params = list(sig.parameters.keys()) assert "self" in params assert "username" in params assert "expand" in params assert sig.parameters["expand"].default is None # Check get_current_user_info signature sig = inspect.signature(UsersMixin.get_current_user_info) params = list(sig.parameters.keys()) assert "self" in params assert len(params) == 1 # Only self parameter def test_user_permission_scenarios(self, users_mixin): """Test various permission error scenarios.""" # Test 401 Unauthorized mock_response_401 = MagicMock() mock_response_401.status_code = 401 http_error_401 = HTTPError(response=mock_response_401) users_mixin.confluence.get_user_details_by_accountid.side_effect = ( http_error_401 ) with pytest.raises(Exception): # Should propagate the original exception users_mixin.get_user_details_by_accountid("test-account-id") # Test 403 Forbidden mock_response_403 = MagicMock() mock_response_403.status_code = 403 http_error_403 = HTTPError(response=mock_response_403) users_mixin.confluence.get_user_details_by_username.side_effect = http_error_403 with pytest.raises(Exception): # Should propagate the original exception users_mixin.get_user_details_by_username("testuser") def test_cloud_vs_server_authentication_patterns(self, users_mixin): """Test that different authentication patterns work for Cloud vs Server/DC.""" # Mock Cloud response (account ID based) cloud_user_data = { "accountId": "5b10ac8d82e05b22cc7d4ef5", "accountType": "atlassian", "displayName": "Cloud User", "accountStatus": "active", } # Mock Server/DC response (username based) server_user_data = { "username": "serveruser", "userKey": "serveruser-key-12345", "displayName": "Server User", "status": "active", } # Test Cloud pattern users_mixin.confluence.get_user_details_by_accountid.return_value = ( cloud_user_data ) cloud_result = users_mixin.get_user_details_by_accountid( "5b10ac8d82e05b22cc7d4ef5" ) assert cloud_result["accountId"] == "5b10ac8d82e05b22cc7d4ef5" assert "accountType" in cloud_result # Test Server/DC pattern users_mixin.confluence.get_user_details_by_username.return_value = ( server_user_data ) server_result = users_mixin.get_user_details_by_username("serveruser") assert server_result["username"] == "serveruser" assert "userKey" in server_result def test_response_data_validation_and_transformation( self, users_mixin, mock_user_data_cloud ): """Test that response data is properly validated and returned as-is.""" # Arrange account_id = "5b10ac8d82e05b22cc7d4ef5" users_mixin.confluence.get_user_details_by_accountid.return_value = ( mock_user_data_cloud ) # Act result = users_mixin.get_user_details_by_accountid(account_id) # Assert - should return the data exactly as received from the API assert result is mock_user_data_cloud # Same object reference assert isinstance(result, dict) assert all( key in result for key in ["accountId", "displayName", "email", "accountStatus"] ) def test_deactivated_user_status_handling(self, users_mixin): """Test handling of deactivated users with status expansion.""" # Arrange deactivated_user_data = { "accountId": "5b10ac8d82e05b22cc7d4ef5", "displayName": "Deactivated User", "accountStatus": "inactive", "status": "Deactivated", # Expanded status } users_mixin.confluence.get_user_details_by_accountid.return_value = ( deactivated_user_data ) # Act result = users_mixin.get_user_details_by_accountid( "5b10ac8d82e05b22cc7d4ef5", expand="status" ) # Assert assert result["accountStatus"] == "inactive" assert result["status"] == "Deactivated" users_mixin.confluence.get_user_details_by_accountid.assert_called_once_with( "5b10ac8d82e05b22cc7d4ef5", "status" ) def test_method_delegation_to_confluence_client( self, users_mixin, mock_current_user_data ): """Test that methods properly delegate to the underlying confluence client.""" # Test that the methods are thin wrappers around confluence client methods account_id = "test-account-id" username = "testuser" expand = "status" # Test account ID method delegation users_mixin.get_user_details_by_accountid(account_id, expand) users_mixin.confluence.get_user_details_by_accountid.assert_called_with( account_id, expand ) # Test username method delegation users_mixin.get_user_details_by_username(username, expand) users_mixin.confluence.get_user_details_by_username.assert_called_with( username, expand ) # Test current user method delegation - need to mock the return value users_mixin.confluence.get.return_value = mock_current_user_data users_mixin.get_current_user_info() users_mixin.confluence.get.assert_called_with("rest/api/user/current")

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/ArconixForge/mcp-atlassian'

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