@@ -1016,6 +1016,161 @@ def test_template_export(self, yaml_parse_mock):
1016
1016
resource_type2_instance .export .assert_called_once_with (
1017
1017
"Resource2" , mock .ANY , template_dir )
1018
1018
1019
+ @mock .patch ("awscli.customizations.cloudformation.artifact_exporter.yaml_parse" )
1020
+ def test_template_export_foreach_valid (self , yaml_parse_mock ):
1021
+ parent_dir = os .path .sep
1022
+ template_dir = os .path .join (parent_dir , 'foo' , 'bar' )
1023
+ template_path = os .path .join (template_dir , 'path' )
1024
+ template_str = self .example_yaml_template ()
1025
+
1026
+ resource_type1_class = mock .Mock ()
1027
+ resource_type1_class .RESOURCE_TYPE = "resource_type1"
1028
+ resource_type1_instance = mock .Mock ()
1029
+ resource_type1_class .return_value = resource_type1_instance
1030
+ resource_type2_class = mock .Mock ()
1031
+ resource_type2_class .RESOURCE_TYPE = "resource_type2"
1032
+ resource_type2_instance = mock .Mock ()
1033
+ resource_type2_class .return_value = resource_type2_instance
1034
+
1035
+ resources_to_export = [
1036
+ resource_type1_class ,
1037
+ resource_type2_class
1038
+ ]
1039
+
1040
+ properties = {"foo" : "bar" }
1041
+ template_dict = {
1042
+ "Resources" : {
1043
+ "Resource1" : {
1044
+ "Type" : "resource_type1" ,
1045
+ "Properties" : properties
1046
+ },
1047
+ "Resource2" : {
1048
+ "Type" : "resource_type2" ,
1049
+ "Properties" : properties
1050
+ },
1051
+ "Resource3" : {
1052
+ "Type" : "some-other-type" ,
1053
+ "Properties" : properties
1054
+ },
1055
+ "Fn::ForEach::OuterLoopName" : [
1056
+ "Identifier1" ,
1057
+ ["4" , "5" ],
1058
+ {
1059
+ "Fn::ForEach::InnerLoopName" : [
1060
+ "Identifier2" ,
1061
+ ["6" , "7" ],
1062
+ {
1063
+ "Resource${Identifier1}${Identifier2}" : {
1064
+ "Type" : "resource_type2" ,
1065
+ "Properties" : properties
1066
+ }
1067
+ }
1068
+ ],
1069
+ "Resource${Identifier1}" : {
1070
+ "Type" : "resource_type1" ,
1071
+ "Properties" : properties
1072
+ }
1073
+ }
1074
+ ]
1075
+ }
1076
+ }
1077
+
1078
+ open_mock = mock .mock_open ()
1079
+ yaml_parse_mock .return_value = template_dict
1080
+
1081
+ # Patch the file open method to return template string
1082
+ with mock .patch (
1083
+ "awscli.customizations.cloudformation.artifact_exporter.open" ,
1084
+ open_mock (read_data = template_str )) as open_mock :
1085
+
1086
+ template_exporter = Template (
1087
+ template_path , parent_dir , self .s3_uploader_mock ,
1088
+ resources_to_export )
1089
+ exported_template = template_exporter .export ()
1090
+ self .assertEqual (exported_template , template_dict )
1091
+
1092
+ open_mock .assert_called_once_with (
1093
+ make_abs_path (parent_dir , template_path ), "r" )
1094
+
1095
+ self .assertEqual (1 , yaml_parse_mock .call_count )
1096
+
1097
+ resource_type1_class .assert_called_with (self .s3_uploader_mock )
1098
+ self .assertEqual (
1099
+ resource_type1_instance .export .call_args_list ,
1100
+ [
1101
+ mock .call ("Resource1" , properties , template_dir ),
1102
+ mock .call ("Resource${Identifier1}" , properties , template_dir )
1103
+ ]
1104
+ )
1105
+ resource_type2_class .assert_called_with (self .s3_uploader_mock )
1106
+ self .assertEqual (
1107
+ resource_type2_instance .export .call_args_list ,
1108
+ [
1109
+ mock .call ("Resource2" , properties , template_dir ),
1110
+ mock .call ("Resource${Identifier1}${Identifier2}" , properties , template_dir )
1111
+ ]
1112
+ )
1113
+
1114
+ @mock .patch ("awscli.customizations.cloudformation.artifact_exporter.yaml_parse" )
1115
+ def test_template_export_foreach_invalid (self , yaml_parse_mock ):
1116
+ parent_dir = os .path .sep
1117
+ template_dir = os .path .join (parent_dir , 'foo' , 'bar' )
1118
+ template_path = os .path .join (template_dir , 'path' )
1119
+ template_str = self .example_yaml_template ()
1120
+
1121
+ resource_type1_class = mock .Mock ()
1122
+ resource_type1_class .RESOURCE_TYPE = "resource_type1"
1123
+ resource_type1_instance = mock .Mock ()
1124
+ resource_type1_class .return_value = resource_type1_instance
1125
+ resource_type2_class = mock .Mock ()
1126
+ resource_type2_class .RESOURCE_TYPE = "resource_type2"
1127
+ resource_type2_instance = mock .Mock ()
1128
+ resource_type2_class .return_value = resource_type2_instance
1129
+
1130
+ resources_to_export = [
1131
+ resource_type1_class ,
1132
+ resource_type2_class
1133
+ ]
1134
+
1135
+ properties = {"foo" : "bar" }
1136
+ template_dict = {
1137
+ "Resources" : {
1138
+ "Resource1" : {
1139
+ "Type" : "resource_type1" ,
1140
+ "Properties" : properties
1141
+ },
1142
+ "Resource2" : {
1143
+ "Type" : "resource_type2" ,
1144
+ "Properties" : properties
1145
+ },
1146
+ "Resource3" : {
1147
+ "Type" : "some-other-type" ,
1148
+ "Properties" : properties
1149
+ },
1150
+ "Fn::ForEach::OuterLoopName" : [
1151
+ "Identifier1" ,
1152
+ {
1153
+ "Resource${Identifier1}" : {
1154
+ }
1155
+ }
1156
+ ]
1157
+ }
1158
+ }
1159
+
1160
+ open_mock = mock .mock_open ()
1161
+ yaml_parse_mock .return_value = template_dict
1162
+
1163
+ # Patch the file open method to return template string
1164
+ with mock .patch (
1165
+ "awscli.customizations.cloudformation.artifact_exporter.open" ,
1166
+ open_mock (read_data = template_str )) as open_mock :
1167
+ template_exporter = Template (
1168
+ template_path , parent_dir , self .s3_uploader_mock ,
1169
+ resources_to_export )
1170
+ with self .assertRaises (exceptions .InvalidForEachIntrinsicFunctionError ):
1171
+ template_exporter .export ()
1172
+
1173
+
1019
1174
@mock .patch ("awscli.customizations.cloudformation.artifact_exporter.yaml_parse" )
1020
1175
def test_template_global_export (self , yaml_parse_mock ):
1021
1176
parent_dir = os .path .sep
0 commit comments