github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/configs/hcl2shim/paths_test.go (about) 1 package hcl2shim 2 3 import ( 4 "fmt" 5 "reflect" 6 "strconv" 7 "strings" 8 "testing" 9 10 "github.com/google/go-cmp/cmp/cmpopts" 11 12 "github.com/google/go-cmp/cmp" 13 14 "github.com/zclconf/go-cty/cty" 15 ) 16 17 var ( 18 ignoreUnexported = cmpopts.IgnoreUnexported(cty.GetAttrStep{}, cty.IndexStep{}) 19 valueComparer = cmp.Comparer(cty.Value.RawEquals) 20 ) 21 22 func TestPathFromFlatmap(t *testing.T) { 23 tests := []struct { 24 Flatmap string 25 Type cty.Type 26 Want cty.Path 27 WantErr string 28 }{ 29 { 30 Flatmap: "", 31 Type: cty.EmptyObject, 32 Want: nil, 33 }, 34 { 35 Flatmap: "attr", 36 Type: cty.EmptyObject, 37 Want: nil, 38 WantErr: `attribute "attr" not found`, 39 }, 40 { 41 Flatmap: "foo", 42 Type: cty.Object(map[string]cty.Type{ 43 "foo": cty.String, 44 }), 45 Want: cty.Path{ 46 cty.GetAttrStep{Name: "foo"}, 47 }, 48 }, 49 { 50 Flatmap: "foo.#", 51 Type: cty.Object(map[string]cty.Type{ 52 "foo": cty.List(cty.String), 53 }), 54 Want: cty.Path{ 55 cty.GetAttrStep{Name: "foo"}, 56 }, 57 }, 58 { 59 Flatmap: "foo.1", 60 Type: cty.Object(map[string]cty.Type{ 61 "foo": cty.List(cty.String), 62 }), 63 Want: cty.Path{ 64 cty.GetAttrStep{Name: "foo"}, 65 cty.IndexStep{Key: cty.NumberIntVal(1)}, 66 }, 67 }, 68 { 69 Flatmap: "foo.1", 70 Type: cty.Object(map[string]cty.Type{ 71 "foo": cty.Tuple([]cty.Type{ 72 cty.String, 73 cty.Bool, 74 }), 75 }), 76 Want: cty.Path{ 77 cty.GetAttrStep{Name: "foo"}, 78 cty.IndexStep{Key: cty.NumberIntVal(1)}, 79 }, 80 }, 81 { 82 // a set index returns the set itself, since this being applied to 83 // a diff and the set is changing. 84 Flatmap: "foo.24534534", 85 Type: cty.Object(map[string]cty.Type{ 86 "foo": cty.Set(cty.String), 87 }), 88 Want: cty.Path{ 89 cty.GetAttrStep{Name: "foo"}, 90 }, 91 }, 92 { 93 Flatmap: "foo.%", 94 Type: cty.Object(map[string]cty.Type{ 95 "foo": cty.Map(cty.String), 96 }), 97 Want: cty.Path{ 98 cty.GetAttrStep{Name: "foo"}, 99 }, 100 }, 101 { 102 Flatmap: "foo.baz", 103 Type: cty.Object(map[string]cty.Type{ 104 "foo": cty.Map(cty.Bool), 105 }), 106 Want: cty.Path{ 107 cty.GetAttrStep{Name: "foo"}, 108 cty.IndexStep{Key: cty.StringVal("baz")}, 109 }, 110 }, 111 { 112 Flatmap: "foo.bar.baz", 113 Type: cty.Object(map[string]cty.Type{ 114 "foo": cty.Map( 115 cty.Map(cty.Bool), 116 ), 117 }), 118 Want: cty.Path{ 119 cty.GetAttrStep{Name: "foo"}, 120 cty.IndexStep{Key: cty.StringVal("bar")}, 121 cty.IndexStep{Key: cty.StringVal("baz")}, 122 }, 123 }, 124 { 125 Flatmap: "foo.bar.baz", 126 Type: cty.Object(map[string]cty.Type{ 127 "foo": cty.Map( 128 cty.Object(map[string]cty.Type{ 129 "baz": cty.String, 130 }), 131 ), 132 }), 133 Want: cty.Path{ 134 cty.GetAttrStep{Name: "foo"}, 135 cty.IndexStep{Key: cty.StringVal("bar")}, 136 cty.GetAttrStep{Name: "baz"}, 137 }, 138 }, 139 { 140 Flatmap: "foo.0.bar", 141 Type: cty.Object(map[string]cty.Type{ 142 "foo": cty.List(cty.Object(map[string]cty.Type{ 143 "bar": cty.String, 144 "baz": cty.Bool, 145 })), 146 }), 147 Want: cty.Path{ 148 cty.GetAttrStep{Name: "foo"}, 149 cty.IndexStep{Key: cty.NumberIntVal(0)}, 150 cty.GetAttrStep{Name: "bar"}, 151 }, 152 }, 153 { 154 Flatmap: "foo.34534534.baz", 155 Type: cty.Object(map[string]cty.Type{ 156 "foo": cty.Set(cty.Object(map[string]cty.Type{ 157 "bar": cty.String, 158 "baz": cty.Bool, 159 })), 160 }), 161 Want: cty.Path{ 162 cty.GetAttrStep{Name: "foo"}, 163 }, 164 }, 165 { 166 Flatmap: "foo.bar.bang", 167 Type: cty.Object(map[string]cty.Type{ 168 "foo": cty.String, 169 }), 170 WantErr: `invalid step "bar.bang"`, 171 }, 172 { 173 // there should not be any attribute names with dots 174 Flatmap: "foo.bar.bang", 175 Type: cty.Object(map[string]cty.Type{ 176 "foo.bar": cty.Map(cty.String), 177 }), 178 WantErr: `attribute "foo" not found`, 179 }, 180 { 181 // We can only handle key names with dots if the map elements are a 182 // primitive type. 183 Flatmap: "foo.bar.bop", 184 Type: cty.Object(map[string]cty.Type{ 185 "foo": cty.Map(cty.String), 186 }), 187 Want: cty.Path{ 188 cty.GetAttrStep{Name: "foo"}, 189 cty.IndexStep{Key: cty.StringVal("bar.bop")}, 190 }, 191 }, 192 { 193 Flatmap: "foo.bar.0.baz", 194 Type: cty.Object(map[string]cty.Type{ 195 "foo": cty.Map( 196 cty.List( 197 cty.Map(cty.String), 198 ), 199 ), 200 }), 201 Want: cty.Path{ 202 cty.GetAttrStep{Name: "foo"}, 203 cty.IndexStep{Key: cty.StringVal("bar")}, 204 cty.IndexStep{Key: cty.NumberIntVal(0)}, 205 cty.IndexStep{Key: cty.StringVal("baz")}, 206 }, 207 }, 208 } 209 210 for _, test := range tests { 211 t.Run(fmt.Sprintf("%s as %#v", test.Flatmap, test.Type), func(t *testing.T) { 212 got, err := requiresReplacePath(test.Flatmap, test.Type) 213 214 if test.WantErr != "" { 215 if err == nil { 216 t.Fatalf("succeeded; want error: %s", test.WantErr) 217 } 218 if got, want := err.Error(), test.WantErr; !strings.Contains(got, want) { 219 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 220 } 221 return 222 } else { 223 if err != nil { 224 t.Fatalf("unexpected error: %s", err.Error()) 225 } 226 } 227 228 if !reflect.DeepEqual(got, test.Want) { 229 t.Fatalf("incorrect path\ngot: %#v\nwant: %#v\n", got, test.Want) 230 } 231 }) 232 } 233 } 234 235 func TestRequiresReplace(t *testing.T) { 236 for _, tc := range []struct { 237 name string 238 attrs []string 239 expected []cty.Path 240 ty cty.Type 241 }{ 242 { 243 name: "basic", 244 attrs: []string{ 245 "foo", 246 }, 247 ty: cty.Object(map[string]cty.Type{ 248 "foo": cty.String, 249 }), 250 expected: []cty.Path{ 251 cty.Path{cty.GetAttrStep{Name: "foo"}}, 252 }, 253 }, 254 { 255 name: "two", 256 attrs: []string{ 257 "foo", 258 "bar", 259 }, 260 ty: cty.Object(map[string]cty.Type{ 261 "foo": cty.String, 262 "bar": cty.String, 263 }), 264 expected: []cty.Path{ 265 cty.Path{cty.GetAttrStep{Name: "foo"}}, 266 cty.Path{cty.GetAttrStep{Name: "bar"}}, 267 }, 268 }, 269 { 270 name: "nested object", 271 attrs: []string{ 272 "foo.bar", 273 }, 274 ty: cty.Object(map[string]cty.Type{ 275 "foo": cty.Object(map[string]cty.Type{ 276 "bar": cty.String, 277 }), 278 }), 279 expected: []cty.Path{ 280 cty.Path{cty.GetAttrStep{Name: "foo"}, cty.GetAttrStep{Name: "bar"}}, 281 }, 282 }, 283 { 284 name: "nested objects", 285 attrs: []string{ 286 "foo.bar.baz", 287 }, 288 ty: cty.Object(map[string]cty.Type{ 289 "foo": cty.Object(map[string]cty.Type{ 290 "bar": cty.Object(map[string]cty.Type{ 291 "baz": cty.String, 292 }), 293 }), 294 }), 295 expected: []cty.Path{ 296 cty.Path{cty.GetAttrStep{Name: "foo"}, cty.GetAttrStep{Name: "bar"}, cty.GetAttrStep{Name: "baz"}}, 297 }, 298 }, 299 { 300 name: "nested map", 301 attrs: []string{ 302 "foo.%", 303 "foo.bar", 304 }, 305 ty: cty.Object(map[string]cty.Type{ 306 "foo": cty.Map(cty.String), 307 }), 308 expected: []cty.Path{ 309 cty.Path{cty.GetAttrStep{Name: "foo"}}, 310 }, 311 }, 312 { 313 name: "nested list", 314 attrs: []string{ 315 "foo.#", 316 "foo.1", 317 }, 318 ty: cty.Object(map[string]cty.Type{ 319 "foo": cty.Map(cty.String), 320 }), 321 expected: []cty.Path{ 322 cty.Path{cty.GetAttrStep{Name: "foo"}}, 323 }, 324 }, 325 { 326 name: "object in map", 327 attrs: []string{ 328 "foo.bar.baz", 329 }, 330 ty: cty.Object(map[string]cty.Type{ 331 "foo": cty.Map(cty.Object( 332 map[string]cty.Type{ 333 "baz": cty.String, 334 }, 335 )), 336 }), 337 expected: []cty.Path{ 338 cty.Path{cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.StringVal("bar")}, cty.GetAttrStep{Name: "baz"}}, 339 }, 340 }, 341 { 342 name: "object in list", 343 attrs: []string{ 344 "foo.1.baz", 345 }, 346 ty: cty.Object(map[string]cty.Type{ 347 "foo": cty.List(cty.Object( 348 map[string]cty.Type{ 349 "baz": cty.String, 350 }, 351 )), 352 }), 353 expected: []cty.Path{ 354 cty.Path{cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.NumberIntVal(1)}, cty.GetAttrStep{Name: "baz"}}, 355 }, 356 }, 357 } { 358 t.Run(tc.name, func(t *testing.T) { 359 rp, err := RequiresReplace(tc.attrs, tc.ty) 360 if err != nil { 361 t.Fatal(err) 362 } 363 if !cmp.Equal(tc.expected, rp, ignoreUnexported, valueComparer) { 364 t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.expected, rp) 365 } 366 }) 367 368 } 369 } 370 371 func TestFlatmapKeyFromPath(t *testing.T) { 372 for i, tc := range []struct { 373 path cty.Path 374 attr string 375 }{ 376 { 377 path: cty.Path{ 378 cty.GetAttrStep{Name: "force_new"}, 379 }, 380 attr: "force_new", 381 }, 382 { 383 path: cty.Path{ 384 cty.GetAttrStep{Name: "attr"}, 385 cty.IndexStep{Key: cty.NumberIntVal(0)}, 386 cty.GetAttrStep{Name: "force_new"}, 387 }, 388 attr: "attr.0.force_new", 389 }, 390 { 391 path: cty.Path{ 392 cty.GetAttrStep{Name: "attr"}, 393 cty.IndexStep{Key: cty.StringVal("key")}, 394 cty.GetAttrStep{Name: "obj_attr"}, 395 cty.IndexStep{Key: cty.NumberIntVal(0)}, 396 cty.GetAttrStep{Name: "force_new"}, 397 }, 398 attr: "attr.key.obj_attr.0.force_new", 399 }, 400 } { 401 t.Run(strconv.Itoa(i), func(t *testing.T) { 402 attr := FlatmapKeyFromPath(tc.path) 403 if attr != tc.attr { 404 t.Fatalf("expected:%q got:%q", tc.attr, attr) 405 } 406 }) 407 } 408 }