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