import re from parameterized import parameterized from unittest.case import TestCase from unittest.mock import MagicMock, patch, ANY from samcli.lib.utils.resource_trigger import ( CodeResourceTrigger, DefinitionCodeTrigger, LambdaFunctionCodeTrigger, LambdaImageCodeTrigger, LambdaLayerCodeTrigger, LambdaZipCodeTrigger, ResourceTrigger, TemplateTrigger, ) from samcli.local.lambdafn.exceptions import FunctionNotFound, ResourceNotFound from samcli.lib.providers.exceptions import MissingLocalDefinition, InvalidTemplateFile from samcli.lib.providers.provider import ResourceIdentifier class TestResourceTrigger(TestCase): @patch("samcli.lib.utils.resource_trigger.PathHandler") @patch("samcli.lib.utils.resource_trigger.RegexMatchingEventHandler") def test_single_file_path_handler(self, handler_mock, bundle_mock): path = MagicMock() file_path = MagicMock() file_path.__str__.return_value = "/parent/file" parent_path = MagicMock() parent_path.__str__.return_value = "/parent/" file_path.parent = parent_path path.resolve.return_value = file_path ResourceTrigger.get_single_file_path_handler(path) path.resolve.assert_called_once() escaped_path = re.escape("/parent/file") handler_mock.assert_called_once_with( regexes=[f"^{escaped_path}$"], ignore_regexes=[], ignore_directories=True, case_sensitive=ANY ) bundle_mock.assert_called_once_with(path=parent_path, event_handler=handler_mock.return_value, recursive=False) @patch("samcli.lib.utils.resource_trigger.PathHandler") @patch("samcli.lib.utils.resource_trigger.RegexMatchingEventHandler") def test_dir_path_handler(self, handler_mock, bundle_mock): path = MagicMock() folder_path = MagicMock() path.resolve.return_value = folder_path ResourceTrigger.get_dir_path_handler(path, ignore_regexes=["a", "a/b"]) path.resolve.assert_called_once() handler_mock.assert_called_once_with( regexes=["^.*$"], ignore_regexes=["a", "a/b"], ignore_directories=False, case_sensitive=ANY ) bundle_mock.assert_called_once_with( path=folder_path, event_handler=handler_mock.return_value, recursive=True, static_folder=True ) class TestTemplateTrigger(TestCase): @patch("samcli.lib.utils.resource_trigger.DefinitionValidator") @patch("samcli.lib.utils.resource_trigger.Path") @patch("samcli.lib.utils.resource_trigger.ResourceTrigger.get_single_file_path_handler") def test_invalid_template(self, single_file_handler_mock, path_mock, validator_mock): validator_mock.return_value.validate_file.return_value = False with self.assertRaises(InvalidTemplateFile): trigger = TemplateTrigger("template.yaml", "stack", MagicMock()) trigger.validate_template() @patch("samcli.lib.utils.resource_trigger.DefinitionValidator") @patch("samcli.lib.utils.resource_trigger.Path") @patch("samcli.lib.utils.resource_trigger.ResourceTrigger.get_single_file_path_handler") def test_get_path_handler(self, single_file_handler_mock, path_mock, validator_mock): validator_mock.return_value.raw_validate.return_value = True trigger = TemplateTrigger("template.yaml", "stack", MagicMock()) result = trigger.get_path_handlers() self.assertEqual(result, [single_file_handler_mock.return_value]) self.assertEqual(single_file_handler_mock.return_value.event_handler.on_any_event, trigger._validator_wrapper) @patch("samcli.lib.utils.resource_trigger.DefinitionValidator") @patch("samcli.lib.utils.resource_trigger.Path") def test_validator_wrapper(self, path_mock, validator_mock): validator_mock.return_value.raw_validate.return_value = True on_template_change_mock = MagicMock() event_mock = MagicMock() validator_mock.return_value.raw_validate.return_value = True trigger = TemplateTrigger("template.yaml", "stack", on_template_change_mock) trigger._validator_wrapper(event_mock) on_template_change_mock.assert_called_once_with(event_mock) class TestCodeResourceTrigger(TestCase): @patch.multiple(CodeResourceTrigger, __abstractmethods__=set()) @patch("samcli.lib.utils.resource_trigger.get_resource_by_id") def test_init(self, get_resource_by_id_mock): stacks = [MagicMock(), MagicMock()] base_dir = MagicMock() on_code_change_mock = MagicMock() trigger = CodeResourceTrigger(ResourceIdentifier("A"), stacks, base_dir, on_code_change_mock) self.assertEqual(trigger._resource, get_resource_by_id_mock.return_value) self.assertEqual(trigger._on_code_change, on_code_change_mock) @patch.multiple(CodeResourceTrigger, __abstractmethods__=set()) @patch("samcli.lib.utils.resource_trigger.get_resource_by_id") def test_init_invalid(self, get_resource_by_id_mock): stacks = [MagicMock(), MagicMock()] base_dir = MagicMock() on_code_change_mock = MagicMock() get_resource_by_id_mock.return_value = None with self.assertRaises(ResourceNotFound): CodeResourceTrigger(ResourceIdentifier("A"), stacks, base_dir, on_code_change_mock) class TestLambdaFunctionCodeTrigger(TestCase): @patch.multiple(LambdaFunctionCodeTrigger, __abstractmethods__=set()) @patch("samcli.lib.utils.resource_trigger.SamFunctionProvider") @patch("samcli.lib.utils.resource_trigger.get_resource_by_id") def test_init(self, get_resource_by_id_mock, function_provider_mock): stacks = [MagicMock(), MagicMock()] base_dir = MagicMock() on_code_change_mock = MagicMock() function_mock = function_provider_mock.return_value.get.return_value code_uri_mock = MagicMock() LambdaFunctionCodeTrigger._get_code_uri = code_uri_mock trigger = LambdaFunctionCodeTrigger(ResourceIdentifier("A"), stacks, base_dir, on_code_change_mock) self.assertEqual(trigger._function, function_mock) self.assertEqual(trigger._code_uri, code_uri_mock.return_value) @patch.multiple(LambdaFunctionCodeTrigger, __abstractmethods__=set()) @patch("samcli.lib.utils.resource_trigger.SamFunctionProvider") @patch("samcli.lib.utils.resource_trigger.get_resource_by_id") def test_init_invalid(self, get_resource_by_id_mock, function_provider_mock): stacks = [MagicMock(), MagicMock()] base_dir = MagicMock() on_code_change_mock = MagicMock() function_provider_mock.return_value.get.return_value = None code_uri_mock = MagicMock() LambdaFunctionCodeTrigger._get_code_uri = code_uri_mock with self.assertRaises(FunctionNotFound): LambdaFunctionCodeTrigger(ResourceIdentifier("A"), stacks, base_dir, on_code_change_mock) @patch.multiple(LambdaFunctionCodeTrigger, __abstractmethods__=set()) @patch("samcli.lib.utils.resource_trigger.ResourceTrigger.get_dir_path_handler") @patch("samcli.lib.utils.resource_trigger.SamFunctionProvider") @patch("samcli.lib.utils.resource_trigger.get_resource_by_id") def test_get_path_handlers(self, get_resource_by_id_mock, function_provider_mock, get_dir_path_handler_mock): stacks = [MagicMock(), MagicMock()] base_dir = MagicMock() on_code_change_mock = MagicMock() function_mock = function_provider_mock.return_value.get.return_value code_uri_mock = MagicMock() LambdaFunctionCodeTrigger._get_code_uri = code_uri_mock bundle = MagicMock() get_dir_path_handler_mock.return_value = bundle trigger = LambdaFunctionCodeTrigger(ResourceIdentifier("A"), stacks, base_dir, on_code_change_mock) result = trigger.get_path_handlers() self.assertEqual(result, [bundle]) self.assertEqual(bundle.self_create, on_code_change_mock) self.assertEqual(bundle.self_delete, on_code_change_mock) self.assertEqual(bundle.event_handler.on_any_event, on_code_change_mock) class TestLambdaZipCodeTrigger(TestCase): @patch("samcli.lib.utils.resource_trigger.SamFunctionProvider") @patch("samcli.lib.utils.resource_trigger.get_resource_by_id") def test_get_code_uri(self, get_resource_by_id_mock, function_provider_mock): stacks = [MagicMock(), MagicMock()] base_dir = MagicMock() on_code_change_mock = MagicMock() function_mock = function_provider_mock.return_value.get.return_value trigger = LambdaZipCodeTrigger(ResourceIdentifier("A"), stacks, base_dir, on_code_change_mock) result = trigger._get_code_uri() self.assertEqual(result, function_mock.codeuri) class TestLambdaImageCodeTrigger(TestCase): @patch("samcli.lib.utils.resource_trigger.SamFunctionProvider") @patch("samcli.lib.utils.resource_trigger.get_resource_by_id") def test_get_code_uri(self, get_resource_by_id_mock, function_provider_mock): stacks = [MagicMock(), MagicMock()] base_dir = MagicMock() on_code_change_mock = MagicMock() function_mock = function_provider_mock.return_value.get.return_value trigger = LambdaImageCodeTrigger(ResourceIdentifier("A"), stacks, base_dir, on_code_change_mock) result = trigger._get_code_uri() self.assertEqual(result, function_mock.metadata.get.return_value) class TestLambdaLayerCodeTrigger(TestCase): @patch("samcli.lib.utils.resource_trigger.SamLayerProvider") @patch("samcli.lib.utils.resource_trigger.get_resource_by_id") def test_init(self, get_resource_by_id_mock, layer_provider_mock): stacks = [MagicMock(), MagicMock()] base_dir = MagicMock() on_code_change_mock = MagicMock() layer_mock = layer_provider_mock.return_value.get.return_value trigger = LambdaLayerCodeTrigger(ResourceIdentifier("A"), stacks, base_dir, on_code_change_mock) self.assertEqual(trigger._layer, layer_mock) self.assertEqual(trigger._code_uri, layer_mock.codeuri) @patch("samcli.lib.utils.resource_trigger.ResourceTrigger.get_dir_path_handler") @patch("samcli.lib.utils.resource_trigger.SamLayerProvider") @patch("samcli.lib.utils.resource_trigger.get_resource_by_id") def test_get_path_handlers(self, get_resource_by_id_mock, layer_provider_mock, get_dir_path_handler_mock): stacks = [MagicMock(), MagicMock()] base_dir = MagicMock() on_code_change_mock = MagicMock() layer_mock = layer_provider_mock.return_value.get.return_value bundle = MagicMock() get_dir_path_handler_mock.return_value = bundle trigger = LambdaLayerCodeTrigger(ResourceIdentifier("A"), stacks, base_dir, on_code_change_mock) result = trigger.get_path_handlers() self.assertEqual(result, [bundle]) self.assertEqual(bundle.self_create, on_code_change_mock) self.assertEqual(bundle.self_delete, on_code_change_mock) self.assertEqual(bundle.event_handler.on_any_event, on_code_change_mock) class TestDefinitionCodeTrigger(TestCase): @patch("samcli.lib.utils.resource_trigger.DefinitionValidator") @patch("samcli.lib.utils.resource_trigger.Path") @patch("samcli.lib.utils.resource_trigger.ResourceTrigger.get_single_file_path_handler") @patch("samcli.lib.utils.resource_trigger.get_resource_by_id") def test_get_path_handler(self, get_resource_by_id_mock, single_file_handler_mock, path_mock, validator_mock): stacks = [MagicMock(), MagicMock()] base_dir = MagicMock() resource = {"Properties": {"DefinitionUri": "abc"}} get_resource_by_id_mock.return_value = resource trigger = DefinitionCodeTrigger("TestApi", "AWS::Serverless::Api", stacks, base_dir, MagicMock()) result = trigger.get_path_handlers() self.assertEqual(result, [single_file_handler_mock.return_value]) self.assertEqual(single_file_handler_mock.return_value.event_handler.on_any_event, trigger._validator_wrapper) @patch("samcli.lib.utils.resource_trigger.DefinitionValidator") @patch("samcli.lib.utils.resource_trigger.Path") @patch("samcli.lib.utils.resource_trigger.ResourceTrigger.get_single_file_path_handler") @patch("samcli.lib.utils.resource_trigger.get_resource_by_id") def test_get_path_handler_missing_definition( self, get_resource_by_id_mock, single_file_handler_mock, path_mock, validator_mock ): stacks = [MagicMock(), MagicMock()] base_dir = MagicMock() resource = {"Properties": {"Field": "abc"}} get_resource_by_id_mock.return_value = resource with self.assertRaises(MissingLocalDefinition): trigger = DefinitionCodeTrigger("TestApi", "AWS::Serverless::Api", stacks, base_dir, MagicMock()) @patch("samcli.lib.utils.resource_trigger.DefinitionValidator") @patch("samcli.lib.utils.resource_trigger.Path") @patch("samcli.lib.utils.resource_trigger.get_resource_by_id") def test_validator_wrapper(self, get_resource_by_id_mock, path_mock, validator_mock): stacks = [MagicMock(), MagicMock()] base_dir = MagicMock() on_definition_change_mock = MagicMock() event_mock = MagicMock() validator_mock.return_value.validate.return_value = True resource = {"Properties": {"DefinitionUri": "abc"}} get_resource_by_id_mock.return_value = resource trigger = DefinitionCodeTrigger("TestApi", "AWS::Serverless::Api", stacks, base_dir, on_definition_change_mock) trigger._validator_wrapper(event_mock) on_definition_change_mock.assert_called_once_with(event_mock)