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