github.com/hashicorp/terraform-plugin-sdk@v1.17.2/helper/schema/field_writer_map_test.go (about) 1 package schema 2 3 import ( 4 "reflect" 5 "testing" 6 ) 7 8 func TestMapFieldWriter_impl(t *testing.T) { 9 var _ FieldWriter = new(MapFieldWriter) 10 } 11 12 func TestMapFieldWriter(t *testing.T) { 13 schema := map[string]*Schema{ 14 "bool": {Type: TypeBool}, 15 "int": {Type: TypeInt}, 16 "string": {Type: TypeString}, 17 "list": { 18 Type: TypeList, 19 Elem: &Schema{Type: TypeString}, 20 }, 21 "listInt": { 22 Type: TypeList, 23 Elem: &Schema{Type: TypeInt}, 24 }, 25 "listResource": { 26 Type: TypeList, 27 Optional: true, 28 Computed: true, 29 Elem: &Resource{ 30 Schema: map[string]*Schema{ 31 "value": { 32 Type: TypeInt, 33 Optional: true, 34 }, 35 }, 36 }, 37 }, 38 "map": {Type: TypeMap}, 39 "set": { 40 Type: TypeSet, 41 Elem: &Schema{Type: TypeInt}, 42 Set: func(a interface{}) int { 43 return a.(int) 44 }, 45 }, 46 "setDeep": { 47 Type: TypeSet, 48 Elem: &Resource{ 49 Schema: map[string]*Schema{ 50 "index": {Type: TypeInt}, 51 "value": {Type: TypeString}, 52 }, 53 }, 54 Set: func(a interface{}) int { 55 return a.(map[string]interface{})["index"].(int) 56 }, 57 }, 58 } 59 60 cases := map[string]struct { 61 Addr []string 62 Value interface{} 63 Err bool 64 Out map[string]string 65 }{ 66 "noexist": { 67 []string{"noexist"}, 68 42, 69 true, 70 map[string]string{}, 71 }, 72 73 "bool": { 74 []string{"bool"}, 75 false, 76 false, 77 map[string]string{ 78 "bool": "false", 79 }, 80 }, 81 82 "int": { 83 []string{"int"}, 84 42, 85 false, 86 map[string]string{ 87 "int": "42", 88 }, 89 }, 90 91 "bigint": { 92 []string{"int"}, 93 7227701560655103598, 94 false, 95 map[string]string{ 96 "int": "7227701560655103598", 97 }, 98 }, 99 100 "string": { 101 []string{"string"}, 102 "42", 103 false, 104 map[string]string{ 105 "string": "42", 106 }, 107 }, 108 109 "string nil": { 110 []string{"string"}, 111 nil, 112 false, 113 map[string]string{ 114 "string": "", 115 }, 116 }, 117 118 "list of resources": { 119 []string{"listResource"}, 120 []interface{}{ 121 map[string]interface{}{ 122 "value": 80, 123 }, 124 }, 125 false, 126 map[string]string{ 127 "listResource.#": "1", 128 "listResource.0.value": "80", 129 }, 130 }, 131 132 "list of resources empty": { 133 []string{"listResource"}, 134 []interface{}{}, 135 false, 136 map[string]string{ 137 "listResource.#": "0", 138 }, 139 }, 140 141 "list of resources nil": { 142 []string{"listResource"}, 143 nil, 144 false, 145 map[string]string{ 146 "listResource.#": "0", 147 }, 148 }, 149 150 "list of strings": { 151 []string{"list"}, 152 []interface{}{"foo", "bar"}, 153 false, 154 map[string]string{ 155 "list.#": "2", 156 "list.0": "foo", 157 "list.1": "bar", 158 }, 159 }, 160 161 "list element": { 162 []string{"list", "0"}, 163 "string", 164 true, 165 map[string]string{}, 166 }, 167 168 "map": { 169 []string{"map"}, 170 map[string]interface{}{"foo": "bar"}, 171 false, 172 map[string]string{ 173 "map.%": "1", 174 "map.foo": "bar", 175 }, 176 }, 177 178 "map delete": { 179 []string{"map"}, 180 nil, 181 false, 182 map[string]string{ 183 "map": "", 184 }, 185 }, 186 187 "map element": { 188 []string{"map", "foo"}, 189 "bar", 190 true, 191 map[string]string{}, 192 }, 193 194 "set": { 195 []string{"set"}, 196 []interface{}{1, 2, 5}, 197 false, 198 map[string]string{ 199 "set.#": "3", 200 "set.1": "1", 201 "set.2": "2", 202 "set.5": "5", 203 }, 204 }, 205 206 "set nil": { 207 []string{"set"}, 208 nil, 209 false, 210 map[string]string{ 211 "set.#": "0", 212 }, 213 }, 214 215 "set typed nil": { 216 []string{"set"}, 217 func() *Set { return nil }(), 218 false, 219 map[string]string{ 220 "set.#": "0", 221 }, 222 }, 223 224 "set resource": { 225 []string{"setDeep"}, 226 []interface{}{ 227 map[string]interface{}{ 228 "index": 10, 229 "value": "foo", 230 }, 231 map[string]interface{}{ 232 "index": 50, 233 "value": "bar", 234 }, 235 }, 236 false, 237 map[string]string{ 238 "setDeep.#": "2", 239 "setDeep.10.index": "10", 240 "setDeep.10.value": "foo", 241 "setDeep.50.index": "50", 242 "setDeep.50.value": "bar", 243 }, 244 }, 245 246 "set element": { 247 []string{"set", "5"}, 248 5, 249 true, 250 map[string]string{}, 251 }, 252 253 "full object": { 254 nil, 255 map[string]interface{}{ 256 "string": "foo", 257 "list": []interface{}{"foo", "bar"}, 258 }, 259 false, 260 map[string]string{ 261 "string": "foo", 262 "list.#": "2", 263 "list.0": "foo", 264 "list.1": "bar", 265 }, 266 }, 267 } 268 269 for name, tc := range cases { 270 w := &MapFieldWriter{Schema: schema} 271 err := w.WriteField(tc.Addr, tc.Value) 272 if err != nil != tc.Err { 273 t.Fatalf("%s: err: %s", name, err) 274 } 275 276 actual := w.Map() 277 if !reflect.DeepEqual(actual, tc.Out) { 278 t.Fatalf("%s: bad: %#v", name, actual) 279 } 280 } 281 } 282 283 func TestMapFieldWriterCleanSet(t *testing.T) { 284 schema := map[string]*Schema{ 285 "setDeep": { 286 Type: TypeSet, 287 Elem: &Resource{ 288 Schema: map[string]*Schema{ 289 "index": {Type: TypeInt}, 290 "value": {Type: TypeString}, 291 }, 292 }, 293 Set: func(a interface{}) int { 294 return a.(map[string]interface{})["index"].(int) 295 }, 296 }, 297 } 298 299 values := []struct { 300 Addr []string 301 Value interface{} 302 Out map[string]string 303 }{ 304 { 305 []string{"setDeep"}, 306 []interface{}{ 307 map[string]interface{}{ 308 "index": 10, 309 "value": "foo", 310 }, 311 map[string]interface{}{ 312 "index": 50, 313 "value": "bar", 314 }, 315 }, 316 map[string]string{ 317 "setDeep.#": "2", 318 "setDeep.10.index": "10", 319 "setDeep.10.value": "foo", 320 "setDeep.50.index": "50", 321 "setDeep.50.value": "bar", 322 }, 323 }, 324 { 325 []string{"setDeep"}, 326 []interface{}{ 327 map[string]interface{}{ 328 "index": 20, 329 "value": "baz", 330 }, 331 map[string]interface{}{ 332 "index": 60, 333 "value": "qux", 334 }, 335 }, 336 map[string]string{ 337 "setDeep.#": "2", 338 "setDeep.20.index": "20", 339 "setDeep.20.value": "baz", 340 "setDeep.60.index": "60", 341 "setDeep.60.value": "qux", 342 }, 343 }, 344 { 345 []string{"setDeep"}, 346 []interface{}{ 347 map[string]interface{}{ 348 "index": 30, 349 "value": "one", 350 }, 351 map[string]interface{}{ 352 "index": 70, 353 "value": "two", 354 }, 355 }, 356 map[string]string{ 357 "setDeep.#": "2", 358 "setDeep.30.index": "30", 359 "setDeep.30.value": "one", 360 "setDeep.70.index": "70", 361 "setDeep.70.value": "two", 362 }, 363 }, 364 } 365 366 w := &MapFieldWriter{Schema: schema} 367 368 for n, tc := range values { 369 err := w.WriteField(tc.Addr, tc.Value) 370 if err != nil { 371 t.Fatalf("%d: err: %s", n, err) 372 } 373 374 actual := w.Map() 375 if !reflect.DeepEqual(actual, tc.Out) { 376 t.Fatalf("%d: bad: %#v", n, actual) 377 } 378 } 379 } 380 381 func TestMapFieldWriterCleanList(t *testing.T) { 382 schema := map[string]*Schema{ 383 "listDeep": { 384 Type: TypeList, 385 Elem: &Resource{ 386 Schema: map[string]*Schema{ 387 "thing1": {Type: TypeString}, 388 "thing2": {Type: TypeString}, 389 }, 390 }, 391 }, 392 } 393 394 values := []struct { 395 Addr []string 396 Value interface{} 397 Out map[string]string 398 }{ 399 { 400 // Base list 401 []string{"listDeep"}, 402 []interface{}{ 403 map[string]interface{}{ 404 "thing1": "a", 405 "thing2": "b", 406 }, 407 map[string]interface{}{ 408 "thing1": "c", 409 "thing2": "d", 410 }, 411 map[string]interface{}{ 412 "thing1": "e", 413 "thing2": "f", 414 }, 415 map[string]interface{}{ 416 "thing1": "g", 417 "thing2": "h", 418 }, 419 }, 420 map[string]string{ 421 "listDeep.#": "4", 422 "listDeep.0.thing1": "a", 423 "listDeep.0.thing2": "b", 424 "listDeep.1.thing1": "c", 425 "listDeep.1.thing2": "d", 426 "listDeep.2.thing1": "e", 427 "listDeep.2.thing2": "f", 428 "listDeep.3.thing1": "g", 429 "listDeep.3.thing2": "h", 430 }, 431 }, 432 { 433 // Remove an element 434 []string{"listDeep"}, 435 []interface{}{ 436 map[string]interface{}{ 437 "thing1": "a", 438 "thing2": "b", 439 }, 440 map[string]interface{}{ 441 "thing1": "c", 442 "thing2": "d", 443 }, 444 map[string]interface{}{ 445 "thing1": "e", 446 "thing2": "f", 447 }, 448 }, 449 map[string]string{ 450 "listDeep.#": "3", 451 "listDeep.0.thing1": "a", 452 "listDeep.0.thing2": "b", 453 "listDeep.1.thing1": "c", 454 "listDeep.1.thing2": "d", 455 "listDeep.2.thing1": "e", 456 "listDeep.2.thing2": "f", 457 }, 458 }, 459 { 460 // Rewrite with missing keys. This should normally not be necessary, as 461 // hopefully the writers are writing zero values as necessary, but for 462 // brevity we want to make sure that what exists in the writer is exactly 463 // what the last write looked like coming from the provider. 464 []string{"listDeep"}, 465 []interface{}{ 466 map[string]interface{}{ 467 "thing1": "a", 468 }, 469 map[string]interface{}{ 470 "thing1": "c", 471 }, 472 map[string]interface{}{ 473 "thing1": "e", 474 }, 475 }, 476 map[string]string{ 477 "listDeep.#": "3", 478 "listDeep.0.thing1": "a", 479 "listDeep.1.thing1": "c", 480 "listDeep.2.thing1": "e", 481 }, 482 }, 483 } 484 485 w := &MapFieldWriter{Schema: schema} 486 487 for n, tc := range values { 488 err := w.WriteField(tc.Addr, tc.Value) 489 if err != nil { 490 t.Fatalf("%d: err: %s", n, err) 491 } 492 493 actual := w.Map() 494 if !reflect.DeepEqual(actual, tc.Out) { 495 t.Fatalf("%d: bad: %#v", n, actual) 496 } 497 } 498 } 499 500 func TestMapFieldWriterCleanMap(t *testing.T) { 501 schema := map[string]*Schema{ 502 "map": { 503 Type: TypeMap, 504 }, 505 } 506 507 values := []struct { 508 Value interface{} 509 Out map[string]string 510 }{ 511 { 512 // Base map 513 map[string]interface{}{ 514 "thing1": "a", 515 "thing2": "b", 516 "thing3": "c", 517 "thing4": "d", 518 }, 519 map[string]string{ 520 "map.%": "4", 521 "map.thing1": "a", 522 "map.thing2": "b", 523 "map.thing3": "c", 524 "map.thing4": "d", 525 }, 526 }, 527 { 528 // Base map 529 map[string]interface{}{ 530 "thing1": "a", 531 "thing2": "b", 532 "thing4": "d", 533 }, 534 map[string]string{ 535 "map.%": "3", 536 "map.thing1": "a", 537 "map.thing2": "b", 538 "map.thing4": "d", 539 }, 540 }, 541 } 542 543 w := &MapFieldWriter{Schema: schema} 544 545 for n, tc := range values { 546 err := w.WriteField([]string{"map"}, tc.Value) 547 if err != nil { 548 t.Fatalf("%d: err: %s", n, err) 549 } 550 551 actual := w.Map() 552 if !reflect.DeepEqual(actual, tc.Out) { 553 t.Fatalf("%d: bad: %#v", n, actual) 554 } 555 } 556 }