"""Test Terraform property builder""" import json from unittest.mock import patch, Mock, call from parameterized import parameterized from samcli.hook_packages.terraform.hooks.prepare.property_builder import ( _build_code_property, REMOTE_DUMMY_VALUE, _get_json_body, _get_property_extractor, _build_lambda_function_environment_property, _build_lambda_function_image_config_property, _check_image_config_value, ) from samcli.lib.hook.exceptions import PrepareHookException from tests.unit.hook_packages.terraform.hooks.prepare.prepare_base import PrepareHookUnitBase class TestTerraformPropBuilder(PrepareHookUnitBase): def setUp(self): super().setUp() def test_build_lambda_function_code_property_zip(self): resource_mock = Mock() expected_cfn_property = self.expected_cfn_zip_function_properties["Code"] translated_cfn_property = _build_code_property(self.tf_zip_function_properties, resource_mock) resource_mock.assert_not_called() self.assertEqual(translated_cfn_property, expected_cfn_property) @patch("samcli.hook_packages.terraform.hooks.prepare.property_builder._resolve_resource_attribute") def test_build_lambda_function_code_property_s3_with_null_bucket_only_in_planned_values( self, mock_resolve_resource_attribute, ): resource_mock = Mock() reference_mock = Mock() mock_resolve_resource_attribute.return_value = reference_mock tf_s3_function_properties = { **self.tf_function_common_properties, "s3_key": "bucket_key", "s3_object_version": "1", } expected_cfn_property = { "S3Bucket": REMOTE_DUMMY_VALUE, "S3Bucket_config_value": reference_mock, "S3Key": "bucket_key", "S3ObjectVersion": "1", } translated_cfn_property = _build_code_property(tf_s3_function_properties, resource_mock) self.assertEqual(translated_cfn_property, expected_cfn_property) mock_resolve_resource_attribute.assert_has_calls( [call(resource_mock, "s3_bucket"), call(resource_mock, "s3_key"), call(resource_mock, "s3_object_version")] ) @patch("samcli.hook_packages.terraform.hooks.prepare.property_builder._resolve_resource_attribute") def test_build_lambda_function_code_property_with_null_imageuri_only_in_planned_values( self, mock_resolve_resource_attribute, ): resource_mock = Mock() reference_mock = Mock() mock_resolve_resource_attribute.return_value = reference_mock tf_image_function_properties = { **self.tf_image_package_type_function_common_properties, "image_config": [ { "command": ["cmd1", "cmd2"], "entry_point": ["entry1", "entry2"], "working_directory": "/working/dir/path", } ], } expected_cfn_property = { "ImageUri": REMOTE_DUMMY_VALUE, } translated_cfn_property = _build_code_property(tf_image_function_properties, resource_mock) self.assertEqual(translated_cfn_property, expected_cfn_property) mock_resolve_resource_attribute.assert_has_calls([call(resource_mock, "image_uri")]) def test_build_lambda_function_code_property_s3(self): resource_mock = Mock() expected_cfn_property = self.expected_cfn_s3_function_properties["Code"] translated_cfn_property = _build_code_property(self.tf_s3_function_properties, resource_mock) resource_mock.assert_not_called() self.assertEqual(translated_cfn_property, expected_cfn_property) def test_build_lambda_function_code_property_image(self): expected_cfn_property = self.expected_cfn_image_package_function_properties["Code"] resource_mock = Mock() translated_cfn_property = _build_code_property(self.tf_image_package_type_function_properties, resource_mock) resource_mock.assert_not_called() self.assertEqual(translated_cfn_property, expected_cfn_property) def test_build_lambda_layer_code_property_zip(self): resource_mock = Mock() expected_cfn_property = self.expected_cfn_lambda_layer_properties_zip["Content"] translated_cfn_property = _build_code_property(self.tf_lambda_layer_properties_zip, resource_mock) resource_mock.assert_not_called() self.assertEqual(translated_cfn_property, expected_cfn_property) def test_build_lambda_layer_code_property_s3(self): resource_mock = Mock() expected_cfn_property = self.expected_cfn_lambda_layer_properties_s3["Content"] translated_cfn_property = _build_code_property(self.tf_lambda_layer_properties_s3, resource_mock) resource_mock.assert_not_called() self.assertEqual(translated_cfn_property, expected_cfn_property) @parameterized.expand(["function_name", "handler"]) def test_get_property_extractor(self, tf_property_name): property_extractor = _get_property_extractor(tf_property_name) self.assertEqual( property_extractor(self.tf_zip_function_properties, None), self.tf_zip_function_properties[tf_property_name] ) def test_build_lambda_function_environment_property(self): expected_cfn_property = self.expected_cfn_zip_function_properties["Environment"] translated_cfn_property = _build_lambda_function_environment_property(self.tf_zip_function_properties, None) self.assertEqual(translated_cfn_property, expected_cfn_property) def test_build_lambda_function_environment_property_no_variables(self): tf_properties = {"function_name": self.zip_function_name} self.assertIsNone(_build_lambda_function_environment_property(tf_properties, None)) tf_properties = {"environment": [], "function_name": self.zip_function_name} self.assertIsNone(_build_lambda_function_environment_property(tf_properties, None)) def test_build_lambda_function_image_config_property(self): expected_cfn_property = self.expected_cfn_image_package_function_properties["ImageConfig"] translated_cfn_property = _build_lambda_function_image_config_property( self.tf_image_package_type_function_properties, None ) self.assertEqual(translated_cfn_property, expected_cfn_property) def test_build_lambda_function_image_config_property_no_image_config(self): tf_properties = {**self.tf_image_package_type_function_properties} del tf_properties["image_config"] translated_cfn_property = _build_lambda_function_image_config_property(tf_properties, None) self.assertEqual(translated_cfn_property, None) def test_build_lambda_function_image_config_property_empty_image_config_list(self): tf_properties = {**self.tf_image_package_type_function_properties} tf_properties["image_config"] = [] translated_cfn_property = _build_lambda_function_image_config_property(tf_properties, None) self.assertEqual(translated_cfn_property, None) @parameterized.expand( [("command", "Command"), ("entry_point", "EntryPoint"), ("working_directory", "WorkingDirectory")] ) def test_build_lambda_function_image_config_property_not_all_properties_exist( self, missing_tf_property, missing_cfn_property ): expected_cfn_property = {**self.expected_cfn_image_package_function_properties["ImageConfig"]} del expected_cfn_property[missing_cfn_property] tf_properties = {**self.tf_image_package_type_function_properties} del tf_properties["image_config"][0][missing_tf_property] translated_cfn_property = _build_lambda_function_image_config_property(tf_properties, None) self.assertEqual(translated_cfn_property, expected_cfn_property) def test_check_image_config_value_valid(self): image_config = [ { "command": ["cmd1", "cmd2"], "entry_point": ["entry1", "entry2"], "working_directory": "/working/dir/path", } ] res = _check_image_config_value(image_config) self.assertTrue(res) def test_check_image_config_value_invalid_type(self): image_config = { "command": ["cmd1", "cmd2"], "entry_point": ["entry1", "entry2"], "working_directory": "/working/dir/path", } expected_message = f"AWS SAM CLI expects that the value of image_config of aws_lambda_function resource in " f"the terraform plan output to be of type list instead of {type(image_config)}" with self.assertRaises(PrepareHookException, msg=expected_message): _check_image_config_value(image_config) def test_check_image_config_value_invalid_length(self): image_config = [ { "command": ["cmd1", "cmd2"], "entry_point": ["entry1", "entry2"], "working_directory": "/working/dir/path", }, { "command": ["cmd1", "cmd2"], "entry_point": ["entry1", "entry2"], "working_directory": "/working/dir/path", }, ] expected_message = f"AWS SAM CLI expects that there is only one item in the image_config property of " f"aws_lambda_function resource in the terraform plan output, but there are {len(image_config)} items" with self.assertRaises(PrepareHookException, msg=expected_message): _check_image_config_value(image_config) def test_get_json_body(self): body_object = {"foo": "bar"} result = _get_json_body({"body": json.dumps(body_object)}, Mock()) self.assertEqual(result, body_object) @parameterized.expand( [ (Mock(),), # wrong type ("not valid json",), # not valid json ] ) def test_get_json_body_invalid(self, invalid_value): result = _get_json_body({"body": invalid_value}, Mock()) self.assertEqual(result, invalid_value)