github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/plans/objchange/normalize_obj_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package objchange 5 6 import ( 7 "testing" 8 9 "github.com/apparentlymart/go-dump/dump" 10 "github.com/terramate-io/tf/configs/configschema" 11 "github.com/zclconf/go-cty/cty" 12 ) 13 14 func TestNormalizeObjectFromLegacySDK(t *testing.T) { 15 tests := map[string]struct { 16 Schema *configschema.Block 17 Input cty.Value 18 Want cty.Value 19 }{ 20 "empty": { 21 &configschema.Block{}, 22 cty.EmptyObjectVal, 23 cty.EmptyObjectVal, 24 }, 25 "attributes only": { 26 &configschema.Block{ 27 Attributes: map[string]*configschema.Attribute{ 28 "a": {Type: cty.String, Required: true}, 29 "b": {Type: cty.String, Optional: true}, 30 }, 31 }, 32 cty.ObjectVal(map[string]cty.Value{ 33 "a": cty.StringVal("a value"), 34 "b": cty.StringVal("b value"), 35 }), 36 cty.ObjectVal(map[string]cty.Value{ 37 "a": cty.StringVal("a value"), 38 "b": cty.StringVal("b value"), 39 }), 40 }, 41 "null block single": { 42 &configschema.Block{ 43 BlockTypes: map[string]*configschema.NestedBlock{ 44 "a": { 45 Nesting: configschema.NestingSingle, 46 Block: configschema.Block{ 47 Attributes: map[string]*configschema.Attribute{ 48 "b": {Type: cty.String, Optional: true}, 49 }, 50 }, 51 }, 52 }, 53 }, 54 cty.ObjectVal(map[string]cty.Value{ 55 "a": cty.NullVal(cty.Object(map[string]cty.Type{ 56 "b": cty.String, 57 })), 58 }), 59 cty.ObjectVal(map[string]cty.Value{ 60 "a": cty.NullVal(cty.Object(map[string]cty.Type{ 61 "b": cty.String, 62 })), 63 }), 64 }, 65 "unknown block single": { 66 &configschema.Block{ 67 BlockTypes: map[string]*configschema.NestedBlock{ 68 "a": { 69 Nesting: configschema.NestingSingle, 70 Block: configschema.Block{ 71 Attributes: map[string]*configschema.Attribute{ 72 "b": {Type: cty.String, Optional: true}, 73 }, 74 BlockTypes: map[string]*configschema.NestedBlock{ 75 "c": {Nesting: configschema.NestingSingle}, 76 }, 77 }, 78 }, 79 }, 80 }, 81 cty.ObjectVal(map[string]cty.Value{ 82 "a": cty.UnknownVal(cty.Object(map[string]cty.Type{ 83 "b": cty.String, 84 "c": cty.EmptyObject, 85 })), 86 }), 87 cty.ObjectVal(map[string]cty.Value{ 88 "a": cty.ObjectVal(map[string]cty.Value{ 89 "b": cty.UnknownVal(cty.String), 90 "c": cty.EmptyObjectVal, 91 }), 92 }), 93 }, 94 "null block list": { 95 &configschema.Block{ 96 BlockTypes: map[string]*configschema.NestedBlock{ 97 "a": { 98 Nesting: configschema.NestingList, 99 Block: configschema.Block{ 100 Attributes: map[string]*configschema.Attribute{ 101 "b": {Type: cty.String, Optional: true}, 102 }, 103 BlockTypes: map[string]*configschema.NestedBlock{ 104 "c": {Nesting: configschema.NestingSingle}, 105 }, 106 }, 107 }, 108 }, 109 }, 110 cty.ObjectVal(map[string]cty.Value{ 111 "a": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ 112 "b": cty.String, 113 "c": cty.EmptyObject, 114 }))), 115 }), 116 cty.ObjectVal(map[string]cty.Value{ 117 "a": cty.ListValEmpty(cty.Object(map[string]cty.Type{ 118 "b": cty.String, 119 "c": cty.EmptyObject, 120 })), 121 }), 122 }, 123 "unknown block list": { 124 &configschema.Block{ 125 BlockTypes: map[string]*configschema.NestedBlock{ 126 "a": { 127 Nesting: configschema.NestingList, 128 Block: configschema.Block{ 129 Attributes: map[string]*configschema.Attribute{ 130 "b": {Type: cty.String, Optional: true}, 131 }, 132 }, 133 }, 134 }, 135 }, 136 cty.ObjectVal(map[string]cty.Value{ 137 "a": cty.UnknownVal(cty.List(cty.Object(map[string]cty.Type{ 138 "b": cty.String, 139 }))), 140 }), 141 cty.ObjectVal(map[string]cty.Value{ 142 "a": cty.ListVal([]cty.Value{ 143 cty.ObjectVal(map[string]cty.Value{ 144 "b": cty.UnknownVal(cty.String), 145 }), 146 }), 147 }), 148 }, 149 "null block set": { 150 &configschema.Block{ 151 BlockTypes: map[string]*configschema.NestedBlock{ 152 "a": { 153 Nesting: configschema.NestingSet, 154 Block: configschema.Block{ 155 Attributes: map[string]*configschema.Attribute{ 156 "b": {Type: cty.String, Optional: true}, 157 }, 158 }, 159 }, 160 }, 161 }, 162 cty.ObjectVal(map[string]cty.Value{ 163 "a": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ 164 "b": cty.String, 165 }))), 166 }), 167 cty.ObjectVal(map[string]cty.Value{ 168 "a": cty.SetValEmpty(cty.Object(map[string]cty.Type{ 169 "b": cty.String, 170 })), 171 }), 172 }, 173 "unknown block set": { 174 &configschema.Block{ 175 BlockTypes: map[string]*configschema.NestedBlock{ 176 "a": { 177 Nesting: configschema.NestingSet, 178 Block: configschema.Block{ 179 Attributes: map[string]*configschema.Attribute{ 180 "b": {Type: cty.String, Optional: true}, 181 }, 182 }, 183 }, 184 }, 185 }, 186 cty.ObjectVal(map[string]cty.Value{ 187 "a": cty.UnknownVal(cty.Set(cty.Object(map[string]cty.Type{ 188 "b": cty.String, 189 }))), 190 }), 191 cty.ObjectVal(map[string]cty.Value{ 192 "a": cty.SetVal([]cty.Value{ 193 cty.ObjectVal(map[string]cty.Value{ 194 "b": cty.UnknownVal(cty.String), 195 }), 196 }), 197 }), 198 }, 199 "map block passes through": { 200 // Legacy SDK doesn't use NestingMap, so we don't do any transforms 201 // related to it but we still need to verify that map blocks pass 202 // through unscathed. 203 &configschema.Block{ 204 BlockTypes: map[string]*configschema.NestedBlock{ 205 "a": { 206 Nesting: configschema.NestingMap, 207 Block: configschema.Block{ 208 Attributes: map[string]*configschema.Attribute{ 209 "b": {Type: cty.String, Optional: true}, 210 }, 211 }, 212 }, 213 }, 214 }, 215 cty.ObjectVal(map[string]cty.Value{ 216 "a": cty.MapVal(map[string]cty.Value{ 217 "foo": cty.ObjectVal(map[string]cty.Value{ 218 "b": cty.StringVal("b value"), 219 }), 220 }), 221 }), 222 cty.ObjectVal(map[string]cty.Value{ 223 "a": cty.MapVal(map[string]cty.Value{ 224 "foo": cty.ObjectVal(map[string]cty.Value{ 225 "b": cty.StringVal("b value"), 226 }), 227 }), 228 }), 229 }, 230 "block list with dynamic type": { 231 &configschema.Block{ 232 BlockTypes: map[string]*configschema.NestedBlock{ 233 "a": { 234 Nesting: configschema.NestingList, 235 Block: configschema.Block{ 236 Attributes: map[string]*configschema.Attribute{ 237 "b": {Type: cty.DynamicPseudoType, Optional: true}, 238 }, 239 }, 240 }, 241 }, 242 }, 243 cty.ObjectVal(map[string]cty.Value{ 244 "a": cty.TupleVal([]cty.Value{ 245 cty.ObjectVal(map[string]cty.Value{ 246 "b": cty.StringVal("hello"), 247 }), 248 cty.ObjectVal(map[string]cty.Value{ 249 "b": cty.True, 250 }), 251 }), 252 }), 253 cty.ObjectVal(map[string]cty.Value{ 254 "a": cty.TupleVal([]cty.Value{ 255 cty.ObjectVal(map[string]cty.Value{ 256 "b": cty.StringVal("hello"), 257 }), 258 cty.ObjectVal(map[string]cty.Value{ 259 "b": cty.True, 260 }), 261 }), 262 }), 263 }, 264 "block map with dynamic type": { 265 &configschema.Block{ 266 BlockTypes: map[string]*configschema.NestedBlock{ 267 "a": { 268 Nesting: configschema.NestingMap, 269 Block: configschema.Block{ 270 Attributes: map[string]*configschema.Attribute{ 271 "b": {Type: cty.DynamicPseudoType, Optional: true}, 272 }, 273 }, 274 }, 275 }, 276 }, 277 cty.ObjectVal(map[string]cty.Value{ 278 "a": cty.ObjectVal(map[string]cty.Value{ 279 "one": cty.ObjectVal(map[string]cty.Value{ 280 "b": cty.StringVal("hello"), 281 }), 282 "another": cty.ObjectVal(map[string]cty.Value{ 283 "b": cty.True, 284 }), 285 }), 286 }), 287 cty.ObjectVal(map[string]cty.Value{ 288 "a": cty.ObjectVal(map[string]cty.Value{ 289 "one": cty.ObjectVal(map[string]cty.Value{ 290 "b": cty.StringVal("hello"), 291 }), 292 "another": cty.ObjectVal(map[string]cty.Value{ 293 "b": cty.True, 294 }), 295 }), 296 }), 297 }, 298 } 299 300 for name, test := range tests { 301 t.Run(name, func(t *testing.T) { 302 got := NormalizeObjectFromLegacySDK(test.Input, test.Schema) 303 if !got.RawEquals(test.Want) { 304 t.Errorf( 305 "wrong result\ngot: %s\nwant: %s", 306 dump.Value(got), dump.Value(test.Want), 307 ) 308 } 309 }) 310 } 311 }