diff --git a/tests/plugins/application/test_serverless_app_plugin.py b/tests/plugins/application/test_serverless_app_plugin.py index 6c7271e35..de7222071 100644 --- a/tests/plugins/application/test_serverless_app_plugin.py +++ b/tests/plugins/application/test_serverless_app_plugin.py @@ -7,15 +7,12 @@ from samtranslator.plugins.application.serverless_app_plugin import ServerlessAppPlugin from samtranslator.plugins.exceptions import InvalidPluginException -# TODO: run tests when AWS CLI is not configured (so they can run in brazil) - MOCK_TEMPLATE_URL = "https://awsserverlessrepo-changesets-xxx.s3.amazonaws.com/pre-signed-url" MOCK_TEMPLATE_ID = "id-xx-xx" STATUS_ACTIVE = "ACTIVE" STATUS_PREPARING = "PREPARING" STATUS_EXPIRED = "EXPIRED" - def mock_create_cloud_formation_template(ApplicationId=None, SemanticVersion=None): return { "ApplicationId": ApplicationId, @@ -25,7 +22,6 @@ def mock_create_cloud_formation_template(ApplicationId=None, SemanticVersion=Non "TemplateUrl": MOCK_TEMPLATE_URL, } - def mock_get_application(ApplicationId=None, SemanticVersion=None): return { "ApplicationId": ApplicationId, @@ -36,7 +32,6 @@ def mock_get_application(ApplicationId=None, SemanticVersion=None): "SemanticVersion": SemanticVersion, } - def mock_get_cloud_formation_template(ApplicationId=None, TemplateId=None): return { "ApplicationId": ApplicationId, @@ -46,18 +41,15 @@ def mock_get_cloud_formation_template(ApplicationId=None, TemplateId=None): "TemplateUrl": MOCK_TEMPLATE_URL, } - def mock_get_region(self, service_name, region_name): return "us-east-1" - class TestServerlessAppPlugin_init(TestCase): def setUp(self): client = boto3.client("serverlessrepo", region_name="us-east-1") self.plugin = ServerlessAppPlugin(sar_client=client) def test_plugin_must_setup_correct_name(self): - # Name is the class name expected_name = "ServerlessAppPlugin" self.assertEqual(self.plugin.name, expected_name) @@ -66,7 +58,6 @@ def test_plugin_default_values(self): self.assertEqual(self.plugin._wait_for_template_active_status, False) self.assertEqual(self.plugin._validate_only, False) self.assertTrue(self.plugin._sar_client is not None) - # For some reason, `isinstance` or comparing classes did not work here self.assertEqual(str(self.plugin._sar_client.__class__), str(boto3.client("serverlessrepo").__class__)) @patch("botocore.client.ClientEndpointBridge._check_default_region", mock_get_region) @@ -92,7 +83,6 @@ def test_plugin_accepts_parameters(self): self.plugin = ServerlessAppPlugin(parameters=parameters) self.assertEqual(self.plugin._parameters, parameters) - class TestServerlessAppPlugin_sar_client_creator(TestCase): def setUp(self): self.client_mock = Mock() @@ -105,7 +95,6 @@ def sar_client_creator(): def test_lazy_load(self): plugin = ServerlessAppPlugin(sar_client_creator=self.sar_client_creator) self.client_mock.assert_not_called() - self.assertEqual(plugin._sar_client, self.client_mock("serverlessrepo")) def test_not_used_when_sar_client_provided(self): @@ -114,7 +103,6 @@ def test_not_used_when_sar_client_provided(self): self.assertEqual(plugin._sar_client, sar_client) self.client_mock.assert_not_called() - class TestServerlessAppPlugin_on_before_transform_template_translate(TestCase): def setUp(self): client = boto3.client("serverlessrepo", region_name="us-east-1") @@ -131,17 +119,12 @@ def test_must_process_applications(self, SamTemplateMock): ("id2", ApplicationResource(app_id="id2")), ("id3", ApplicationResource()), ] - sam_template = Mock() SamTemplateMock.return_value = sam_template sam_template.iterate = Mock() sam_template.iterate.return_value = app_resources - self.plugin.on_before_transform_template(template_dict) - SamTemplateMock.assert_called_with(template_dict) - - # Make sure this is called only for Apis sam_template.iterate.assert_called_with({"AWS::Serverless::Application"}) @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") @@ -155,17 +138,12 @@ def test_must_process_applications_validate(self, SamTemplateMock): ("id2", ApplicationResource(app_id="id2")), ("id3", ApplicationResource()), ] - sam_template = Mock() SamTemplateMock.return_value = sam_template sam_template.iterate = Mock() sam_template.iterate.return_value = app_resources - self.plugin.on_before_transform_template(template_dict) - SamTemplateMock.assert_called_with(template_dict) - - # Make sure this is called only for Apis sam_template.iterate.assert_called_with({"AWS::Serverless::Application"}) @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") @@ -179,17 +157,12 @@ def test_process_invalid_applications(self, SamTemplateMock): ("id2", ApplicationResource(app_id=None)), ("id3", ApplicationResource(app_id="id3", semver=None)), ] - sam_template = Mock() SamTemplateMock.return_value = sam_template sam_template.iterate = Mock() sam_template.iterate.return_value = app_resources - self.plugin.on_before_transform_template(template_dict) - SamTemplateMock.assert_called_with(template_dict) - - # Make sure this is called only for Apis sam_template.iterate.assert_called_with({"AWS::Serverless::Application"}) @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") @@ -203,17 +176,12 @@ def test_process_invalid_applications_validate(self, SamTemplateMock): ("id2", ApplicationResource(app_id=None)), ("id3", ApplicationResource(app_id="id3", semver=None)), ] - sam_template = Mock() SamTemplateMock.return_value = sam_template sam_template.iterate = Mock() sam_template.iterate.return_value = app_resources - self.plugin.on_before_transform_template(template_dict) - SamTemplateMock.assert_called_with(template_dict) - - # Make sure this is called only for Apis sam_template.iterate.assert_called_with({"AWS::Serverless::Application"}) @patch("botocore.client.ClientEndpointBridge._check_default_region", mock_get_region) @@ -231,54 +199,56 @@ def test_resolve_intrinsics(self): input = {"Fn::FindInMap": ["MapA", {"Ref": "AWS::Region"}, "SecondLevelKey1"]} intrinsic_resolvers = self.plugin._get_intrinsic_resolvers(mappings) output = self.plugin._resolve_location_value(input, intrinsic_resolvers) - self.assertEqual("value1", output) @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") - def test_sar_throttling_doesnt_stop_processing(self, SamTemplateMock): + @patch("boto3.Session") + def test_sar_throttling_doesnt_stop_processing(self, BotoSessionMock, SamTemplateMock): + session_mock = Mock() + session_mock.region_name = "us-east-1" + session_mock.get_available_regions.return_value = ["us-east-1"] + BotoSessionMock.return_value = session_mock client = Mock() client.create_cloud_formation_template = Mock() client.create_cloud_formation_template.side_effect = ClientError( {"Error": {"Code": "TooManyRequestsException"}}, "CreateCloudFormationTemplate" ) - app_resources = [ ("id1", ApplicationResource(app_id="id1", semver="1.0.0", location=True)), ] - sam_template = Mock() SamTemplateMock.return_value = sam_template sam_template.iterate = Mock() sam_template.iterate.return_value = app_resources - self.plugin = ServerlessAppPlugin(sar_client=client) self.plugin._can_process_application = Mock() self.plugin._can_process_application.return_value = True self.plugin._get_sleep_time_sec = Mock() self.plugin._get_sleep_time_sec.return_value = 0.02 self.plugin.TEMPLATE_WAIT_TIMEOUT_SECONDS = 1.0 - self.plugin.on_before_transform_template({}) self.assertEqual( self.plugin._applications.get(ServerlessAppPlugin._make_app_key("id1", "1.0.0")).message, "Resource with id [id1] is invalid. Failed to call SAR, timeout limit exceeded.", ) - # confirm we had at least two attempts to call SAR and that we executed a sleep self.assertGreater(client.create_cloud_formation_template.call_count, 1) self.assertGreaterEqual(self.plugin._get_sleep_time_sec.call_count, 1) @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") - def test_unexpected_sar_error_stops_processing(self, SamTemplateMock): + @patch("boto3.Session") + def test_unexpected_sar_error_stops_processing(self, BotoSessionMock, SamTemplateMock): + session_mock = Mock() + session_mock.region_name = "us-east-1" + session_mock.get_available_regions.return_value = ["us-east-1"] + BotoSessionMock.return_value = session_mock template_dict = {"a": "b"} app_resources = [ ("id1", ApplicationResource(app_id="id1", semver="1.0.0", location=True)), ] - sam_template = Mock() SamTemplateMock.return_value = sam_template sam_template.iterate = Mock() sam_template.iterate.return_value = app_resources - client = Mock() client.create_cloud_formation_template.side_effect = ClientError( {"Error": {"Code": "BadBadError"}}, "CreateCloudFormationTemplate" @@ -286,22 +256,24 @@ def test_unexpected_sar_error_stops_processing(self, SamTemplateMock): self.plugin = ServerlessAppPlugin(sar_client=client) self.plugin._can_process_application = Mock() self.plugin._can_process_application.return_value = True - with self.assertRaises(ClientError): self.plugin.on_before_transform_template(template_dict) @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") - def test_sar_success_one_app(self, SamTemplateMock): + @patch("boto3.Session") + def test_sar_success_one_app(self, BotoSessionMock, SamTemplateMock): + session_mock = Mock() + session_mock.region_name = "us-east-1" + session_mock.get_available_regions.return_value = ["us-east-1"] + BotoSessionMock.return_value = session_mock template_dict = {"a": "b"} app_resources = [ ("id1", ApplicationResource(app_id="id1", semver="1.0.0", location=True)), ] - sam_template = Mock() SamTemplateMock.return_value = sam_template sam_template.iterate = Mock() sam_template.iterate.return_value = app_resources - client = Mock() client.create_cloud_formation_template = Mock() client.create_cloud_formation_template.return_value = {"TemplateUrl": "/URL", "Status": STATUS_ACTIVE} @@ -309,18 +281,20 @@ def test_sar_success_one_app(self, SamTemplateMock): self.plugin._can_process_application = Mock() self.plugin._can_process_application.return_value = True self.plugin.on_before_transform_template(template_dict) - self.assertEqual(client.create_cloud_formation_template.call_count, 1) @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") - def test_sleep_between_sar_checks(self, SamTemplateMock): + @patch("boto3.Session") + def test_sleep_between_sar_checks(self, BotoSessionMock, SamTemplateMock): + session_mock = Mock() + session_mock.region_name = "us-east-1" + session_mock.get_available_regions.return_value = ["us-east-1"] + BotoSessionMock.return_value = session_mock template_dict = {"a": "b"} client = Mock() - app_resources = [ ("id1", ApplicationResource(app_id="id1", semver="1.0.0", location=True)), ] - sam_template = Mock() SamTemplateMock.return_value = sam_template sam_template.iterate = Mock() @@ -336,10 +310,8 @@ def test_sleep_between_sar_checks(self, SamTemplateMock): self.plugin._get_sleep_time_sec = Mock() self.plugin._get_sleep_time_sec.return_value = 0.001 self.plugin.on_before_transform_template(template_dict) - # should have exactly two calls to SAR self.assertEqual(client.create_cloud_formation_template.call_count, 2) - self.assertEqual(self.plugin._get_sleep_time_sec.call_count, 1) # make sure we slept once - + self.assertEqual(self.plugin._get_sleep_time_sec.call_count, 1) class ApplicationResource: def __init__(self, app_id="app_id", semver="1.3.5", location=None): @@ -349,37 +321,6 @@ def __init__(self, app_id="app_id", semver="1.3.5", location=None): else {"Location": {"ApplicationId": app_id, "SemanticVersion": semver}} ) - -# class TestServerlessAppPlugin_on_before_transform_resource(TestCase): - -# def setUp(self): -# self.plugin = ServerlessAppPlugin() - -# TODO: test this lifecycle event - -# @parameterized.expand( -# itertools.product([ -# ServerlessAppPlugin(), -# ServerlessAppPlugin(wait_for_template_active_status=True), -# ]) -# ) -# @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") -# @patch('botocore.client.BaseClient._make_api_call', mock_create_cloud_formation_template) -# def test_process_invalid_applications(self, plugin, SamTemplateMock): -# self.plugin = plugin -# template_dict = {"a": "b"} -# app_resources = [("id1", ApplicationResource(app_id = '')), ("id2", ApplicationResource(app_id=None))] - -# sam_template = Mock() -# SamTemplateMock.return_value = sam_template -# sam_template.iterate = Mock() -# sam_template.iterate.return_value = app_resources - -# self.plugin.on_before_transform_template(template_dict) - -# self.plugin.on_before_transform_resource(app_resources[0][0], 'AWS::Serverless::Application', app_resources[0][1].properties) - - class TestServerlessAppPlugin_on_after_transform_template(TestCase): def test_sar_throttling_doesnt_stop_processing(self): client = Mock() @@ -394,7 +335,6 @@ def test_sar_throttling_doesnt_stop_processing(self): plugin.TEMPLATE_WAIT_TIMEOUT_SECONDS = 0.2 with self.assertRaises(InvalidResourceException): plugin.on_after_transform_template("template") - # confirm we had at least two attempts to call SAR and that we executed a sleep self.assertGreater(client.get_cloud_formation_template.call_count, 1) self.assertGreaterEqual(plugin._get_sleep_time_sec.call_count, 1) @@ -416,7 +356,6 @@ def test_sar_success_one_app(self): plugin = ServerlessAppPlugin(sar_client=client, wait_for_template_active_status=True, validate_only=False) plugin._in_progress_templates = [("appid", "template")] plugin.on_after_transform_template("template") - # should have exactly one call to SAR self.assertEqual(client.get_cloud_formation_template.call_count, 1) def test_sar_success_two_apps(self): @@ -426,7 +365,6 @@ def test_sar_success_two_apps(self): plugin = ServerlessAppPlugin(sar_client=client, wait_for_template_active_status=True, validate_only=False) plugin._in_progress_templates = [("appid1", "template1"), ("appid2", "template2")] plugin.on_after_transform_template("template") - # should have exactly one call to SAR per app self.assertEqual(client.get_cloud_formation_template.call_count, 2) def test_expired_sar_app_throws(self): @@ -437,7 +375,6 @@ def test_expired_sar_app_throws(self): plugin._in_progress_templates = [("appid1", "template1"), ("appid2", "template2")] with self.assertRaises(InvalidResourceException): plugin.on_after_transform_template("template") - # should have exactly one call to SAR since the first app will be expired self.assertEqual(client.get_cloud_formation_template.call_count, 1) def test_sleep_between_sar_checks(self): @@ -449,24 +386,25 @@ def test_sleep_between_sar_checks(self): plugin._get_sleep_time_sec = Mock() plugin._get_sleep_time_sec.return_value = 0.001 plugin.on_after_transform_template("template") - # should have exactly two calls to SAR self.assertEqual(client.get_cloud_formation_template.call_count, 2) - self.assertEqual(plugin._get_sleep_time_sec.call_count, 1) # make sure we slept once - + self.assertEqual(plugin._get_sleep_time_sec.call_count, 1) class TestServerlessAppPlugin_on_before_and_on_after_transform_template(TestCase): @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") - def test_time_limit_exceeds_between_combined_sar_calls(self, SamTemplateMock): + @patch("boto3.Session") + def test_time_limit_exceeds_between_combined_sar_calls(self, BotoSessionMock, SamTemplateMock): + session_mock = Mock() + session_mock.region_name = "us-east-1" + session_mock.get_available_regions.return_value = ["us-east-1"] + BotoSessionMock.return_value = session_mock template_dict = {"a": "b"} app_resources = [ ("id1", ApplicationResource(app_id="id1", semver="1.0.0", location=True)), ] - sam_template = Mock() SamTemplateMock.return_value = sam_template sam_template.iterate = Mock() sam_template.iterate.return_value = app_resources - client = Mock() client.get_cloud_formation_template = Mock() client.get_cloud_formation_template.side_effect = [ @@ -483,11 +421,9 @@ def test_time_limit_exceeds_between_combined_sar_calls(self, SamTemplateMock): plugin._get_sleep_time_sec.return_value = 0.04 plugin._in_progress_templates = [("appid", "template"), ("appid2", "template2")] plugin.TEMPLATE_WAIT_TIMEOUT_SECONDS = 0.08 - plugin.on_before_transform_template(template_dict) with self.assertRaises(InvalidResourceException): plugin.on_after_transform_template(template_dict) - # confirm we had at least two attempts to call SAR and that we executed a sleep self.assertEqual(client.get_cloud_formation_template.call_count, 1) self.assertEqual(client.create_cloud_formation_template.call_count, 2) - self.assertGreaterEqual(plugin._get_sleep_time_sec.call_count, 2) + self.assertGreaterEqual(plugin._get_sleep_time_sec.call_count, 2) \ No newline at end of file diff --git a/tests/unit/test_region_configuration.py b/tests/unit/test_region_configuration.py index 6f5a661eb..91fc2caaf 100644 --- a/tests/unit/test_region_configuration.py +++ b/tests/unit/test_region_configuration.py @@ -1,50 +1,47 @@ from unittest import TestCase -from unittest.mock import patch - -import boto3 +from unittest.mock import patch, Mock from parameterized import parameterized +import boto3 from samtranslator.region_configuration import RegionConfiguration - class TestRegionConfiguration(TestCase): - @parameterized.expand( - [ - ["aws"], - ] - ) + @parameterized.expand([ + ["aws"], + ]) def test_when_apigw_edge_configuration_supported(self, partition): with patch( "samtranslator.translator.arn_generator.ArnGenerator.get_partition_name" ) as get_partition_name_patch: get_partition_name_patch.return_value = partition - self.assertTrue(RegionConfiguration.is_apigw_edge_configuration_supported()) - @parameterized.expand( - [["aws-cn"], ["aws-us-gov"], ["aws-iso"], ["aws-iso-b"], ["aws-iso-e"], ["aws-iso-f"], ["aws-eusc"]] - ) + @parameterized.expand([ + ["aws-cn"], ["aws-us-gov"], ["aws-iso"], ["aws-iso-b"], ["aws-iso-e"], ["aws-iso-f"], ["aws-eusc"] + ]) def test_when_apigw_edge_configuration_is_not_supported(self, partition): with patch( "samtranslator.translator.arn_generator.ArnGenerator.get_partition_name" ) as get_partition_name_patch: get_partition_name_patch.return_value = partition - self.assertFalse(RegionConfiguration.is_apigw_edge_configuration_supported()) - @parameterized.expand( - [ - # use ec2 as it's just about everywhere - ["ec2", "cn-north-1"], - ["ec2", "us-west-2"], - ["ec2", "us-gov-east-1"], - ["ec2", "us-isob-east-1"], - ["ec2", None], - # test SAR since SAM uses that - ["serverlessrepo", "us-east-1"], - ["serverlessrepo", "ap-southeast-2"], + @parameterized.expand([ + ["ec2", "cn-north-1"], + ["ec2", "us-west-2"], + ["ec2", "us-gov-east-1"], + ["ec2", "us-isob-east-1"], + ["ec2", None], + ["serverlessrepo", "us-east-1"], + ["serverlessrepo", "ap-southeast-2"], + ]) + @patch("boto3.Session") + def test_is_service_supported_positive(self, service, region, BotoSessionMock): + session_mock = Mock() + session_mock.region_name = "us-east-1" + session_mock.get_available_regions.return_value = [ + "us-east-1", "cn-north-1", "us-west-2", "us-gov-east-1", "us-isob-east-1", "ap-southeast-2" ] - ) - def test_is_service_supported_positive(self, service, region): + BotoSessionMock.return_value = session_mock self.assertTrue(RegionConfiguration.is_service_supported(service, region)) def test_is_service_supported_positive_boto3_default_session(self): @@ -53,9 +50,5 @@ def test_is_service_supported_positive_boto3_default_session(self): self.assertTrue(RegionConfiguration.is_service_supported("ec2", new_region)) def test_is_service_supported_negative(self): - # use an unknown service name self.assertFalse(RegionConfiguration.is_service_supported("ec1", "us-east-1")) - # use a region that does not exist - self.assertFalse(RegionConfiguration.is_service_supported("ec2", "us-east-0")) - # hard to test with a real service, since the test may start failing once that - # service is rolled out to more regions... + self.assertFalse(RegionConfiguration.is_service_supported("ec2", "us-east-0")) \ No newline at end of file