github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/legacy/helper/schema/field_reader_diff_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package schema 5 6 import ( 7 "reflect" 8 "testing" 9 10 "github.com/terramate-io/tf/legacy/terraform" 11 ) 12 13 func TestDiffFieldReader_impl(t *testing.T) { 14 var _ FieldReader = new(DiffFieldReader) 15 } 16 17 func TestDiffFieldReader_NestedSetUpdate(t *testing.T) { 18 hashFn := func(a interface{}) int { 19 m := a.(map[string]interface{}) 20 return m["val"].(int) 21 } 22 23 schema := map[string]*Schema{ 24 "list_of_sets_1": &Schema{ 25 Type: TypeList, 26 Elem: &Resource{ 27 Schema: map[string]*Schema{ 28 "nested_set": &Schema{ 29 Type: TypeSet, 30 Elem: &Resource{ 31 Schema: map[string]*Schema{ 32 "val": &Schema{ 33 Type: TypeInt, 34 }, 35 }, 36 }, 37 Set: hashFn, 38 }, 39 }, 40 }, 41 }, 42 "list_of_sets_2": &Schema{ 43 Type: TypeList, 44 Elem: &Resource{ 45 Schema: map[string]*Schema{ 46 "nested_set": &Schema{ 47 Type: TypeSet, 48 Elem: &Resource{ 49 Schema: map[string]*Schema{ 50 "val": &Schema{ 51 Type: TypeInt, 52 }, 53 }, 54 }, 55 Set: hashFn, 56 }, 57 }, 58 }, 59 }, 60 } 61 62 r := &DiffFieldReader{ 63 Schema: schema, 64 Diff: &terraform.InstanceDiff{ 65 Attributes: map[string]*terraform.ResourceAttrDiff{ 66 "list_of_sets_1.0.nested_set.1.val": &terraform.ResourceAttrDiff{ 67 Old: "1", 68 New: "0", 69 NewRemoved: true, 70 }, 71 "list_of_sets_1.0.nested_set.2.val": &terraform.ResourceAttrDiff{ 72 New: "2", 73 }, 74 }, 75 }, 76 } 77 78 r.Source = &MultiLevelFieldReader{ 79 Readers: map[string]FieldReader{ 80 "diff": r, 81 "set": &MapFieldReader{Schema: schema}, 82 "state": &MapFieldReader{ 83 Map: &BasicMapReader{ 84 "list_of_sets_1.#": "1", 85 "list_of_sets_1.0.nested_set.#": "1", 86 "list_of_sets_1.0.nested_set.1.val": "1", 87 "list_of_sets_2.#": "1", 88 "list_of_sets_2.0.nested_set.#": "1", 89 "list_of_sets_2.0.nested_set.1.val": "1", 90 }, 91 Schema: schema, 92 }, 93 }, 94 Levels: []string{"state", "config"}, 95 } 96 97 out, err := r.ReadField([]string{"list_of_sets_2"}) 98 if err != nil { 99 t.Fatalf("err: %v", err) 100 } 101 102 s := &Set{F: hashFn} 103 s.Add(map[string]interface{}{"val": 1}) 104 expected := s.List() 105 106 l := out.Value.([]interface{}) 107 i := l[0].(map[string]interface{}) 108 actual := i["nested_set"].(*Set).List() 109 110 if !reflect.DeepEqual(expected, actual) { 111 t.Fatalf("bad: NestedSetUpdate\n\nexpected: %#v\n\ngot: %#v\n\n", expected, actual) 112 } 113 } 114 115 // https://github.com/terramate-io/tf/issues/914 116 func TestDiffFieldReader_MapHandling(t *testing.T) { 117 schema := map[string]*Schema{ 118 "tags": &Schema{ 119 Type: TypeMap, 120 }, 121 } 122 r := &DiffFieldReader{ 123 Schema: schema, 124 Diff: &terraform.InstanceDiff{ 125 Attributes: map[string]*terraform.ResourceAttrDiff{ 126 "tags.%": &terraform.ResourceAttrDiff{ 127 Old: "1", 128 New: "2", 129 }, 130 "tags.baz": &terraform.ResourceAttrDiff{ 131 Old: "", 132 New: "qux", 133 }, 134 }, 135 }, 136 Source: &MapFieldReader{ 137 Schema: schema, 138 Map: BasicMapReader(map[string]string{ 139 "tags.%": "1", 140 "tags.foo": "bar", 141 }), 142 }, 143 } 144 145 result, err := r.ReadField([]string{"tags"}) 146 if err != nil { 147 t.Fatalf("ReadField failed: %#v", err) 148 } 149 150 expected := map[string]interface{}{ 151 "foo": "bar", 152 "baz": "qux", 153 } 154 155 if !reflect.DeepEqual(expected, result.Value) { 156 t.Fatalf("bad: DiffHandling\n\nexpected: %#v\n\ngot: %#v\n\n", expected, result.Value) 157 } 158 } 159 160 func TestDiffFieldReader_extra(t *testing.T) { 161 schema := map[string]*Schema{ 162 "stringComputed": &Schema{Type: TypeString}, 163 164 "listMap": &Schema{ 165 Type: TypeList, 166 Elem: &Schema{ 167 Type: TypeMap, 168 }, 169 }, 170 171 "mapRemove": &Schema{Type: TypeMap}, 172 173 "setChange": &Schema{ 174 Type: TypeSet, 175 Optional: true, 176 Elem: &Resource{ 177 Schema: map[string]*Schema{ 178 "index": &Schema{ 179 Type: TypeInt, 180 Required: true, 181 }, 182 183 "value": &Schema{ 184 Type: TypeString, 185 Required: true, 186 }, 187 }, 188 }, 189 Set: func(a interface{}) int { 190 m := a.(map[string]interface{}) 191 return m["index"].(int) 192 }, 193 }, 194 195 "setEmpty": &Schema{ 196 Type: TypeSet, 197 Optional: true, 198 Elem: &Resource{ 199 Schema: map[string]*Schema{ 200 "index": &Schema{ 201 Type: TypeInt, 202 Required: true, 203 }, 204 205 "value": &Schema{ 206 Type: TypeString, 207 Required: true, 208 }, 209 }, 210 }, 211 Set: func(a interface{}) int { 212 m := a.(map[string]interface{}) 213 return m["index"].(int) 214 }, 215 }, 216 } 217 218 r := &DiffFieldReader{ 219 Schema: schema, 220 Diff: &terraform.InstanceDiff{ 221 Attributes: map[string]*terraform.ResourceAttrDiff{ 222 "stringComputed": &terraform.ResourceAttrDiff{ 223 Old: "foo", 224 New: "bar", 225 NewComputed: true, 226 }, 227 228 "listMap.0.bar": &terraform.ResourceAttrDiff{ 229 NewRemoved: true, 230 }, 231 232 "mapRemove.bar": &terraform.ResourceAttrDiff{ 233 NewRemoved: true, 234 }, 235 236 "setChange.10.value": &terraform.ResourceAttrDiff{ 237 Old: "50", 238 New: "80", 239 }, 240 241 "setEmpty.#": &terraform.ResourceAttrDiff{ 242 Old: "2", 243 New: "0", 244 }, 245 }, 246 }, 247 248 Source: &MapFieldReader{ 249 Schema: schema, 250 Map: BasicMapReader(map[string]string{ 251 "listMap.#": "2", 252 "listMap.0.foo": "bar", 253 "listMap.0.bar": "baz", 254 "listMap.1.baz": "baz", 255 256 "mapRemove.foo": "bar", 257 "mapRemove.bar": "bar", 258 259 "setChange.#": "1", 260 "setChange.10.index": "10", 261 "setChange.10.value": "50", 262 263 "setEmpty.#": "2", 264 "setEmpty.10.index": "10", 265 "setEmpty.10.value": "50", 266 "setEmpty.20.index": "20", 267 "setEmpty.20.value": "50", 268 }), 269 }, 270 } 271 272 cases := map[string]struct { 273 Addr []string 274 Result FieldReadResult 275 Err bool 276 }{ 277 "stringComputed": { 278 []string{"stringComputed"}, 279 FieldReadResult{ 280 Value: "", 281 Exists: true, 282 Computed: true, 283 }, 284 false, 285 }, 286 287 "listMapRemoval": { 288 []string{"listMap"}, 289 FieldReadResult{ 290 Value: []interface{}{ 291 map[string]interface{}{ 292 "foo": "bar", 293 }, 294 map[string]interface{}{ 295 "baz": "baz", 296 }, 297 }, 298 Exists: true, 299 }, 300 false, 301 }, 302 303 "mapRemove": { 304 []string{"mapRemove"}, 305 FieldReadResult{ 306 Value: map[string]interface{}{ 307 "foo": "bar", 308 }, 309 Exists: true, 310 Computed: false, 311 }, 312 false, 313 }, 314 315 "setChange": { 316 []string{"setChange"}, 317 FieldReadResult{ 318 Value: []interface{}{ 319 map[string]interface{}{ 320 "index": 10, 321 "value": "80", 322 }, 323 }, 324 Exists: true, 325 }, 326 false, 327 }, 328 329 "setEmpty": { 330 []string{"setEmpty"}, 331 FieldReadResult{ 332 Value: []interface{}{}, 333 Exists: true, 334 }, 335 false, 336 }, 337 } 338 339 for name, tc := range cases { 340 out, err := r.ReadField(tc.Addr) 341 if err != nil != tc.Err { 342 t.Fatalf("%s: err: %s", name, err) 343 } 344 if s, ok := out.Value.(*Set); ok { 345 // If it is a set, convert to a list so its more easily checked. 346 out.Value = s.List() 347 } 348 if !reflect.DeepEqual(tc.Result, out) { 349 t.Fatalf("%s: bad: %#v", name, out) 350 } 351 } 352 } 353 354 func TestDiffFieldReader(t *testing.T) { 355 testFieldReader(t, func(s map[string]*Schema) FieldReader { 356 return &DiffFieldReader{ 357 Schema: s, 358 Diff: &terraform.InstanceDiff{ 359 Attributes: map[string]*terraform.ResourceAttrDiff{ 360 "bool": &terraform.ResourceAttrDiff{ 361 Old: "", 362 New: "true", 363 }, 364 365 "int": &terraform.ResourceAttrDiff{ 366 Old: "", 367 New: "42", 368 }, 369 370 "float": &terraform.ResourceAttrDiff{ 371 Old: "", 372 New: "3.1415", 373 }, 374 375 "string": &terraform.ResourceAttrDiff{ 376 Old: "", 377 New: "string", 378 }, 379 380 "stringComputed": &terraform.ResourceAttrDiff{ 381 Old: "foo", 382 New: "bar", 383 NewComputed: true, 384 }, 385 386 "list.#": &terraform.ResourceAttrDiff{ 387 Old: "0", 388 New: "2", 389 }, 390 391 "list.0": &terraform.ResourceAttrDiff{ 392 Old: "", 393 New: "foo", 394 }, 395 396 "list.1": &terraform.ResourceAttrDiff{ 397 Old: "", 398 New: "bar", 399 }, 400 401 "listInt.#": &terraform.ResourceAttrDiff{ 402 Old: "0", 403 New: "2", 404 }, 405 406 "listInt.0": &terraform.ResourceAttrDiff{ 407 Old: "", 408 New: "21", 409 }, 410 411 "listInt.1": &terraform.ResourceAttrDiff{ 412 Old: "", 413 New: "42", 414 }, 415 416 "map.foo": &terraform.ResourceAttrDiff{ 417 Old: "", 418 New: "bar", 419 }, 420 421 "map.bar": &terraform.ResourceAttrDiff{ 422 Old: "", 423 New: "baz", 424 }, 425 426 "mapInt.%": &terraform.ResourceAttrDiff{ 427 Old: "", 428 New: "2", 429 }, 430 "mapInt.one": &terraform.ResourceAttrDiff{ 431 Old: "", 432 New: "1", 433 }, 434 "mapInt.two": &terraform.ResourceAttrDiff{ 435 Old: "", 436 New: "2", 437 }, 438 439 "mapIntNestedSchema.%": &terraform.ResourceAttrDiff{ 440 Old: "", 441 New: "2", 442 }, 443 "mapIntNestedSchema.one": &terraform.ResourceAttrDiff{ 444 Old: "", 445 New: "1", 446 }, 447 "mapIntNestedSchema.two": &terraform.ResourceAttrDiff{ 448 Old: "", 449 New: "2", 450 }, 451 452 "mapFloat.%": &terraform.ResourceAttrDiff{ 453 Old: "", 454 New: "1", 455 }, 456 "mapFloat.oneDotTwo": &terraform.ResourceAttrDiff{ 457 Old: "", 458 New: "1.2", 459 }, 460 461 "mapBool.%": &terraform.ResourceAttrDiff{ 462 Old: "", 463 New: "2", 464 }, 465 "mapBool.True": &terraform.ResourceAttrDiff{ 466 Old: "", 467 New: "true", 468 }, 469 "mapBool.False": &terraform.ResourceAttrDiff{ 470 Old: "", 471 New: "false", 472 }, 473 474 "set.#": &terraform.ResourceAttrDiff{ 475 Old: "0", 476 New: "2", 477 }, 478 479 "set.10": &terraform.ResourceAttrDiff{ 480 Old: "", 481 New: "10", 482 }, 483 484 "set.50": &terraform.ResourceAttrDiff{ 485 Old: "", 486 New: "50", 487 }, 488 489 "setDeep.#": &terraform.ResourceAttrDiff{ 490 Old: "0", 491 New: "2", 492 }, 493 494 "setDeep.10.index": &terraform.ResourceAttrDiff{ 495 Old: "", 496 New: "10", 497 }, 498 499 "setDeep.10.value": &terraform.ResourceAttrDiff{ 500 Old: "", 501 New: "foo", 502 }, 503 504 "setDeep.50.index": &terraform.ResourceAttrDiff{ 505 Old: "", 506 New: "50", 507 }, 508 509 "setDeep.50.value": &terraform.ResourceAttrDiff{ 510 Old: "", 511 New: "bar", 512 }, 513 }, 514 }, 515 516 Source: &MapFieldReader{ 517 Schema: s, 518 Map: BasicMapReader(map[string]string{ 519 "listMap.#": "2", 520 "listMap.0.foo": "bar", 521 "listMap.0.bar": "baz", 522 "listMap.1.baz": "baz", 523 }), 524 }, 525 } 526 }) 527 }