Skip to main content
Glama

AWS Model Context Protocol Server

by alexei-led
test_resources.py25.6 kB
"""Unit tests for AWS MCP Server resources module.""" import configparser import os from unittest.mock import MagicMock, patch import pytest from botocore.exceptions import ClientError from aws_mcp_server.resources import ( _get_region_description, _get_region_geographic_location, get_aws_account_info, get_aws_environment, get_aws_profiles, get_aws_regions, get_region_available_services, get_region_details, register_resources, ) @pytest.fixture def mock_config_files(monkeypatch, tmp_path): """Create mock AWS config and credentials files for testing.""" config_dir = tmp_path / ".aws" config_dir.mkdir() config_file = config_dir / "config" config_file.write_text("[default]\nregion = us-west-2\n\n[profile dev]\nregion = us-east-1\n\n[profile prod]\nregion = eu-west-1\n") creds_file = config_dir / "credentials" creds_file.write_text( "[default]\n" "aws_access_key_id = AKIADEFAULT000000000\n" "aws_secret_access_key = 1234567890abcdef1234567890abcdef\n" "\n" "[dev]\n" "aws_access_key_id = AKIADEV0000000000000\n" "aws_secret_access_key = abcdef1234567890abcdef1234567890\n" "\n" "[test]\n" "aws_access_key_id = AKIATEST000000000000\n" "aws_secret_access_key = test1234567890abcdef1234567890ab\n" ) monkeypatch.setenv("HOME", str(tmp_path)) return tmp_path def test_get_aws_profiles(mock_config_files): """Test retrieving AWS profiles from config files.""" profiles = get_aws_profiles() assert set(profiles) == {"default", "dev", "prod", "test"} def test_get_aws_profiles_custom_config_file(monkeypatch, tmp_path): """Test get_aws_profiles with custom AWS_CONFIG_FILE.""" custom_config = tmp_path / "custom_config" custom_config.write_text("[default]\nregion = us-west-2\n\n[profile custom-profile]\nregion = eu-west-1\n") monkeypatch.setenv("AWS_CONFIG_FILE", str(custom_config)) monkeypatch.setenv("HOME", str(tmp_path)) profiles = get_aws_profiles() assert "default" in profiles assert "custom-profile" in profiles def test_get_aws_profiles_custom_credentials_file(monkeypatch, tmp_path): """Test get_aws_profiles with custom AWS_SHARED_CREDENTIALS_FILE.""" custom_creds = tmp_path / "custom_credentials" custom_creds.write_text( "[default]\n" "aws_access_key_id = AKIATEST\n" "aws_secret_access_key = secret\n\n" "[custom-creds-profile]\n" "aws_access_key_id = AKIACUSTOM\n" "aws_secret_access_key = customsecret\n" ) monkeypatch.setenv("AWS_SHARED_CREDENTIALS_FILE", str(custom_creds)) monkeypatch.setenv("HOME", str(tmp_path)) profiles = get_aws_profiles() assert "default" in profiles assert "custom-creds-profile" in profiles def test_get_aws_profiles_both_custom_paths(monkeypatch, tmp_path): """Test get_aws_profiles with both custom config and credentials files.""" custom_config = tmp_path / "custom_config" custom_config.write_text("[profile config-only-profile]\nregion = us-west-2\n") custom_creds = tmp_path / "custom_credentials" custom_creds.write_text("[creds-only-profile]\naws_access_key_id = AKIATEST\naws_secret_access_key = secret\n") monkeypatch.setenv("AWS_CONFIG_FILE", str(custom_config)) monkeypatch.setenv("AWS_SHARED_CREDENTIALS_FILE", str(custom_creds)) monkeypatch.setenv("HOME", str(tmp_path)) profiles = get_aws_profiles() assert "default" in profiles assert "config-only-profile" in profiles assert "creds-only-profile" in profiles @patch("boto3.session.Session") def test_get_aws_regions(mock_session): """Test retrieving AWS regions with mocked boto3.""" mock_ec2 = MagicMock() mock_session.return_value.client.return_value = mock_ec2 mock_ec2.describe_regions.return_value = { "Regions": [ {"RegionName": "us-east-1"}, {"RegionName": "us-west-2"}, {"RegionName": "eu-central-1"}, ] } regions = get_aws_regions() assert len(regions) == 3 assert regions[0]["RegionName"] == "eu-central-1" assert regions[0]["RegionDescription"] == "EU Central (Frankfurt)" assert regions[1]["RegionName"] == "us-east-1" assert regions[2]["RegionName"] == "us-west-2" mock_session.assert_called_once() mock_session.return_value.client.assert_called_once_with("ec2") @patch("boto3.session.Session") def test_get_aws_regions_fallback(mock_session): """Test fallback behavior when region retrieval fails.""" mock_session.return_value.client.side_effect = ClientError( {"Error": {"Code": "AccessDenied", "Message": "Access denied"}}, "DescribeRegions", ) regions = get_aws_regions() assert len(regions) >= 12 assert any(r["RegionName"] == "us-east-1" for r in regions) assert any(r["RegionName"] == "eu-west-1" for r in regions) @patch("boto3.session.Session") def test_get_aws_environment(mock_session, monkeypatch): """Test retrieving AWS environment information.""" monkeypatch.setenv("AWS_PROFILE", "test-profile") monkeypatch.setenv("AWS_REGION", "us-west-2") mock_credentials = MagicMock() mock_credentials.method = "shared-credentials-file" mock_session.return_value.get_credentials.return_value = mock_credentials env_info = get_aws_environment() assert env_info["aws_profile"] == "test-profile" assert env_info["aws_region"] == "us-west-2" assert env_info["has_credentials"] is True assert env_info["credentials_source"] == "profile" @patch("boto3.session.Session") def test_get_aws_environment_no_credentials(mock_session, monkeypatch): """Test environment info with no credentials.""" for var in ["AWS_PROFILE", "AWS_REGION", "AWS_DEFAULT_REGION", "AWS_ACCESS_KEY_ID"]: if var in os.environ: monkeypatch.delenv(var) mock_session.return_value.get_credentials.return_value = None env_info = get_aws_environment() assert env_info["aws_profile"] == "default" assert env_info["aws_region"] == "us-east-1" assert env_info["has_credentials"] is False assert env_info["credentials_source"] == "none" @patch("boto3.session.Session") def test_get_aws_account_info(mock_session): """Test retrieving AWS account information.""" mock_sts = MagicMock() mock_iam = MagicMock() mock_org = MagicMock() mock_session.return_value.client.side_effect = lambda service: { "sts": mock_sts, "iam": mock_iam, "organizations": mock_org, }[service] mock_sts.get_caller_identity.return_value = {"Account": "123456789012"} mock_iam.list_account_aliases.return_value = {"AccountAliases": ["my-account"]} mock_org.describe_organization.return_value = {"Organization": {"Id": "o-abcdef1234"}} account_info = get_aws_account_info() assert account_info["account_id"] == "123456789012" assert account_info["account_alias"] == "my-account" assert account_info["organization_id"] == "o-abcdef1234" @patch("boto3.session.Session") def test_get_aws_account_info_minimal(mock_session): """Test account info with minimal permissions.""" mock_sts = MagicMock() def mock_client(service): if service == "sts": return mock_sts elif service == "iam": raise ClientError({"Error": {"Code": "AccessDenied"}}, "ListAccountAliases") else: raise ClientError({"Error": {"Code": "AccessDenied"}}, "DescribeAccount") mock_session.return_value.client.side_effect = mock_client mock_sts.get_caller_identity.return_value = {"Account": "123456789012"} account_info = get_aws_account_info() assert account_info["account_id"] == "123456789012" assert account_info["account_alias"] is None assert account_info["organization_id"] is None def test_register_resources(): """Test registering MCP resources.""" mock_mcp = MagicMock() register_resources(mock_mcp) assert mock_mcp.resource.call_count == 5 expected_resources = [ { "uri": "aws://config/profiles", "name": "aws_profiles", "description": "Get available AWS profiles", }, { "uri": "aws://config/regions", "name": "aws_regions", "description": "Get available AWS regions", }, { "uri": "aws://config/regions/{region}", "name": "aws_region_details", "description": "Get detailed information about a specific AWS region", }, { "uri": "aws://config/environment", "name": "aws_environment", "description": "Get AWS environment information", }, { "uri": "aws://config/account", "name": "aws_account", "description": "Get AWS account information", }, ] for call in mock_mcp.resource.call_args_list: found = False for resource in expected_resources: if resource["uri"] == call.kwargs.get("uri"): assert call.kwargs.get("name") == resource["name"] assert call.kwargs.get("description") == resource["description"] found = True break assert found, f"URI {call.kwargs.get('uri')} not found in expected resources" def test_get_region_description(): """Test the region description utility function.""" assert _get_region_description("us-east-1") == "US East (N. Virginia)" assert _get_region_description("eu-west-2") == "EU West (London)" assert _get_region_description("ap-southeast-1") == "Asia Pacific (Singapore)" assert _get_region_description("unknown-region-1") == "AWS Region unknown-region-1" assert _get_region_description("test-region-2") == "AWS Region test-region-2" @patch("configparser.ConfigParser") @patch("os.path.exists") def test_get_aws_profiles_exception(mock_exists, mock_config_parser): """Test exception handling in get_aws_profiles.""" mock_exists.return_value = True mock_parser_instance = MagicMock() mock_config_parser.return_value = mock_parser_instance mock_parser_instance.read.side_effect = configparser.Error("Config file error") profiles = get_aws_profiles() assert profiles == ["default"] assert mock_parser_instance.read.called @patch("boto3.session.Session") def test_get_aws_regions_generic_exception(mock_session): """Test general exception handling in get_aws_regions.""" mock_session.return_value.client.side_effect = Exception("Generic error") regions = get_aws_regions() assert len(regions) == 0 assert isinstance(regions, list) @patch("boto3.session.Session") def test_get_aws_environment_credential_methods(mock_session): """Test different credential methods in get_aws_environment.""" test_cases = [ ("environment", "environment"), ("iam-role", "instance-profile"), ("assume-role", "assume-role"), ("container-role", "container-role"), ("unknown-method", "profile"), ] for method, expected_source in test_cases: mock_session.reset_mock() mock_credentials = MagicMock() mock_credentials.method = method mock_session.return_value.get_credentials.return_value = mock_credentials env_info = get_aws_environment() assert env_info["has_credentials"] is True assert env_info["credentials_source"] == expected_source @patch("boto3.session.Session") def test_get_aws_environment_exception(mock_session): """Test exception handling in get_aws_environment.""" mock_session.return_value.get_credentials.side_effect = ClientError( {"Error": {"Code": "AccessDenied", "Message": "Credential error"}}, "GetCredentials", ) env_info = get_aws_environment() assert env_info["aws_profile"] == "default" assert env_info["aws_region"] == "us-east-1" assert env_info["has_credentials"] is False assert env_info["credentials_source"] == "none" @patch("boto3.session.Session") def test_get_aws_account_info_with_org(mock_session): """Test AWS account info with organization access.""" mock_sts = MagicMock() mock_iam = MagicMock() mock_org = MagicMock() mock_session.return_value.client.side_effect = lambda service: { "sts": mock_sts, "iam": mock_iam, "organizations": mock_org, }[service] mock_sts.get_caller_identity.return_value = {"Account": "123456789012"} mock_iam.list_account_aliases.return_value = {"AccountAliases": ["my-account"]} mock_org.describe_organization.return_value = {"Organization": {}} account_info = get_aws_account_info() assert account_info["account_id"] == "123456789012" assert account_info["account_alias"] == "my-account" assert account_info["organization_id"] is None @patch("boto3.session.Session") def test_get_aws_account_info_general_exception(mock_session): """Test exception handling in get_aws_account_info.""" mock_session.return_value.client.side_effect = ClientError( {"Error": {"Code": "AccessDenied", "Message": "Access denied"}}, "GetCallerIdentity", ) account_info = get_aws_account_info() assert account_info["account_id"] is None assert account_info["account_alias"] is None assert account_info["organization_id"] is None @patch("aws_mcp_server.resources.get_aws_profiles") @patch("os.environ.get") def test_resource_aws_profiles(mock_environ_get, mock_get_aws_profiles): """Test the aws_profiles resource function implementation.""" mock_environ_get.return_value = "test-profile" mock_get_aws_profiles.return_value = ["default", "test-profile", "dev"] async def mock_resource_function(): profiles = mock_get_aws_profiles.return_value current_profile = mock_environ_get.return_value return {"profiles": [{"name": profile, "is_current": profile == current_profile} for profile in profiles]} import asyncio result = asyncio.run(mock_resource_function()) assert "profiles" in result assert len(result["profiles"]) == 3 current_profile = None for profile in result["profiles"]: if profile["is_current"]: current_profile = profile["name"] assert current_profile == "test-profile" @patch("aws_mcp_server.resources.get_aws_regions") @patch("os.environ.get") def test_resource_aws_regions(mock_environ_get, mock_get_aws_regions): """Test the aws_regions resource function implementation.""" mock_environ_get.side_effect = lambda key, default=None: ("us-west-2" if key in ("AWS_REGION", "AWS_DEFAULT_REGION") else default) mock_get_aws_regions.return_value = [ {"RegionName": "us-east-1", "RegionDescription": "US East (N. Virginia)"}, {"RegionName": "us-west-2", "RegionDescription": "US West (Oregon)"}, ] async def mock_resource_function(): regions = mock_get_aws_regions.return_value current_region = "us-west-2" return { "regions": [ { "name": region["RegionName"], "description": region["RegionDescription"], "is_current": region["RegionName"] == current_region, } for region in regions ] } import asyncio result = asyncio.run(mock_resource_function()) assert "regions" in result assert len(result["regions"]) == 2 current_region = None for region in result["regions"]: if region["is_current"]: current_region = region["name"] assert current_region == "us-west-2" @patch("aws_mcp_server.resources.get_aws_environment") def test_resource_aws_environment(mock_get_aws_environment): """Test the aws_environment resource function implementation.""" mock_env = { "aws_profile": "test-profile", "aws_region": "us-west-2", "has_credentials": True, "credentials_source": "profile", } mock_get_aws_environment.return_value = mock_env async def mock_resource_function(): return mock_get_aws_environment.return_value import asyncio result = asyncio.run(mock_resource_function()) assert result == mock_env @patch("aws_mcp_server.resources.get_aws_account_info") def test_resource_aws_account(mock_get_aws_account_info): """Test the aws_account resource function implementation.""" mock_account_info = { "account_id": "123456789012", "account_alias": "test-account", "organization_id": "o-abcdef123456", } mock_get_aws_account_info.return_value = mock_account_info async def mock_resource_function(): return mock_get_aws_account_info.return_value import asyncio result = asyncio.run(mock_resource_function()) assert result == mock_account_info def test_get_region_geographic_location(): """Test the region geographic location utility function.""" us_east_1 = _get_region_geographic_location("us-east-1") assert us_east_1["continent"] == "North America" assert us_east_1["country"] == "United States" assert us_east_1["city"] == "Ashburn, Virginia" eu_west_2 = _get_region_geographic_location("eu-west-2") assert eu_west_2["continent"] == "Europe" assert eu_west_2["country"] == "United Kingdom" assert eu_west_2["city"] == "London" unknown = _get_region_geographic_location("unknown-region") assert unknown["continent"] == "Unknown" assert unknown["country"] == "Unknown" assert unknown["city"] == "Unknown" @patch("boto3.session.Session") def test_get_region_available_services(mock_session): """Test retrieving available AWS services for a region using Service Quotas API.""" mock_quotas_client = MagicMock() def mock_client(service_name, **kwargs): if service_name == "service-quotas": return mock_quotas_client return MagicMock() mock_session.return_value.client.side_effect = mock_client mock_quotas_client.list_services.return_value = { "Services": [ {"ServiceCode": "AWS.EC2", "ServiceName": "Amazon Elastic Compute Cloud"}, {"ServiceCode": "AWS.S3", "ServiceName": "Amazon Simple Storage Service"}, {"ServiceCode": "Lambda", "ServiceName": "AWS Lambda"}, {"ServiceCode": "Organizations", "ServiceName": "AWS Organizations"}, {"ServiceCode": "AWS.CloudFormation", "ServiceName": "AWS CloudFormation"}, ], "NextToken": None, } services = get_region_available_services(mock_session.return_value, "us-east-1") assert len(services) == 5 assert {"id": "ec2", "name": "Amazon Elastic Compute Cloud"} in services assert {"id": "s3", "name": "Amazon Simple Storage Service"} in services assert {"id": "lambda", "name": "AWS Lambda"} in services assert {"id": "organizations", "name": "AWS Organizations"} in services assert {"id": "cloudformation", "name": "AWS CloudFormation"} in services mock_quotas_client.list_services.assert_called_once() @patch("boto3.session.Session") def test_get_region_available_services_pagination(mock_session): """Test pagination handling in Service Quotas API.""" mock_quotas_client = MagicMock() mock_session.return_value.client.return_value = mock_quotas_client mock_quotas_client.list_services.side_effect = [ { "Services": [ { "ServiceCode": "AWS.EC2", "ServiceName": "Amazon Elastic Compute Cloud", }, { "ServiceCode": "AWS.S3", "ServiceName": "Amazon Simple Storage Service", }, ], "NextToken": "next-token-1", }, { "Services": [ {"ServiceCode": "Lambda", "ServiceName": "AWS Lambda"}, {"ServiceCode": "AWS.DynamoDB", "ServiceName": "Amazon DynamoDB"}, ], "NextToken": None, }, ] services = get_region_available_services(mock_session.return_value, "us-east-1") assert len(services) == 4 assert mock_quotas_client.list_services.call_count == 2 mock_quotas_client.list_services.assert_any_call() mock_quotas_client.list_services.assert_any_call(NextToken="next-token-1") @patch("boto3.session.Session") def test_get_region_available_services_fallback(mock_session): """Test fallback to client creation when Service Quotas API fails.""" def mock_client(service_name, **kwargs): if service_name == "service-quotas": raise ClientError({"Error": {"Code": "AccessDenied"}}, "ListServices") return MagicMock() mock_session.return_value.client.side_effect = mock_client services = get_region_available_services(mock_session.return_value, "us-east-1") assert len(services) > 0 common_service_ids = [service["id"] for service in services] for service_id in ["ec2", "s3", "lambda"]: assert service_id in common_service_ids for service in services: assert "id" in service assert "name" in service @patch("aws_mcp_server.resources.get_region_available_services") @patch("boto3.session.Session") def test_get_region_details(mock_session, mock_get_region_available_services): """Test retrieving detailed AWS region information.""" mock_ec2 = MagicMock() def mock_client(service_name, **kwargs): if service_name == "ec2": return mock_ec2 return MagicMock() mock_session.return_value.client.side_effect = mock_client mock_ec2.describe_availability_zones.return_value = { "AvailabilityZones": [ { "ZoneName": "us-east-1a", "State": "available", "ZoneId": "use1-az1", "ZoneType": "availability-zone", }, { "ZoneName": "us-east-1b", "State": "available", "ZoneId": "use1-az2", "ZoneType": "availability-zone", }, ] } mock_services = [ {"id": "ec2", "name": "EC2"}, {"id": "s3", "name": "S3"}, {"id": "lambda", "name": "Lambda"}, ] mock_get_region_available_services.return_value = mock_services region_details = get_region_details("us-east-1") assert region_details["code"] == "us-east-1" assert region_details["name"] == "US East (N. Virginia)" geo_location = region_details["geographic_location"] assert geo_location["continent"] == "North America" assert geo_location["country"] == "United States" assert geo_location["city"] == "Ashburn, Virginia" assert len(region_details["availability_zones"]) == 2 assert region_details["availability_zones"][0]["name"] == "us-east-1a" assert region_details["availability_zones"][1]["name"] == "us-east-1b" assert region_details["services"] == mock_services mock_get_region_available_services.assert_called_once_with(mock_session.return_value, "us-east-1") @patch("aws_mcp_server.resources.get_region_available_services") @patch("boto3.session.Session") def test_get_region_details_with_error(mock_session, mock_get_region_available_services): """Test region details with API errors.""" mock_session.return_value.client.side_effect = ClientError( {"Error": {"Code": "AccessDenied", "Message": "Access denied"}}, "DescribeAvailabilityZones", ) mock_get_region_available_services.return_value = [] region_details = get_region_details("us-east-1") assert region_details["code"] == "us-east-1" assert region_details["name"] == "US East (N. Virginia)" assert "geographic_location" in region_details assert len(region_details["availability_zones"]) == 0 assert region_details["services"] == [] mock_get_region_available_services.assert_called_once_with(mock_session.return_value, "us-east-1") @patch("aws_mcp_server.resources.get_region_details") def test_resource_aws_region_details(mock_get_region_details): """Test the aws_region_details resource function implementation.""" mock_region_details = { "code": "us-east-1", "name": "US East (N. Virginia)", "geographic_location": { "continent": "North America", "country": "United States", "city": "Ashburn, Virginia", }, "availability_zones": [ { "name": "us-east-1a", "state": "available", "zone_id": "use1-az1", "zone_type": "availability-zone", }, { "name": "us-east-1b", "state": "available", "zone_id": "use1-az2", "zone_type": "availability-zone", }, ], "services": [ {"id": "ec2", "name": "EC2"}, {"id": "s3", "name": "S3"}, {"id": "lambda", "name": "Lambda"}, ], "is_current": True, } mock_get_region_details.return_value = mock_region_details async def mock_resource_function(region: str): return mock_get_region_details(region) import asyncio result = asyncio.run(mock_resource_function("us-east-1")) mock_get_region_details.assert_called_once_with("us-east-1") assert result == mock_region_details

Latest Blog Posts

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/alexei-led/aws-mcp-server'

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