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