github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/jsonformat/differ/change_test.go (about) 1 package differ 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "testing" 7 8 "github.com/zclconf/go-cty/cty" 9 ctyjson "github.com/zclconf/go-cty/cty/json" 10 11 "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" 12 "github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path" 13 "github.com/hashicorp/terraform/internal/command/jsonprovider" 14 "github.com/hashicorp/terraform/internal/plans" 15 ) 16 17 type SetDiff struct { 18 Before SetDiffEntry 19 After SetDiffEntry 20 } 21 22 type SetDiffEntry struct { 23 SingleDiff renderers.ValidateDiffFunction 24 ObjectDiff map[string]renderers.ValidateDiffFunction 25 26 Replace bool 27 Action plans.Action 28 } 29 30 func (entry SetDiffEntry) Validate(obj func(attributes map[string]renderers.ValidateDiffFunction, action plans.Action, replace bool) renderers.ValidateDiffFunction) renderers.ValidateDiffFunction { 31 if entry.SingleDiff != nil { 32 return entry.SingleDiff 33 } 34 35 return obj(entry.ObjectDiff, entry.Action, entry.Replace) 36 } 37 38 func TestValue_SimpleBlocks(t *testing.T) { 39 // Most of the other test functions wrap the test cases in various 40 // collections or blocks. This function just very simply lets you specify 41 // individual test cases within blocks for some simple tests. 42 43 tcs := map[string]struct { 44 input Change 45 block *jsonprovider.Block 46 validate renderers.ValidateDiffFunction 47 }{ 48 "delete_with_null_sensitive_value": { 49 input: Change{ 50 Before: map[string]interface{}{ 51 "normal_attribute": "some value", 52 }, 53 After: nil, 54 BeforeSensitive: map[string]interface{}{ 55 "sensitive_attribute": true, 56 }, 57 AfterSensitive: false, 58 }, 59 block: &jsonprovider.Block{ 60 Attributes: map[string]*jsonprovider.Attribute{ 61 "normal_attribute": { 62 AttributeType: unmarshalType(t, cty.String), 63 }, 64 "sensitive_attribute": { 65 AttributeType: unmarshalType(t, cty.String), 66 }, 67 }, 68 }, 69 validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 70 "normal_attribute": renderers.ValidatePrimitive("some value", nil, plans.Delete, false), 71 }, nil, nil, nil, nil, plans.Delete, false), 72 }, 73 "create_with_null_sensitive_value": { 74 input: Change{ 75 Before: nil, 76 After: map[string]interface{}{ 77 "normal_attribute": "some value", 78 }, 79 BeforeSensitive: map[string]interface{}{ 80 "sensitive_attribute": true, 81 }, 82 AfterSensitive: false, 83 }, 84 block: &jsonprovider.Block{ 85 Attributes: map[string]*jsonprovider.Attribute{ 86 "normal_attribute": { 87 AttributeType: unmarshalType(t, cty.String), 88 }, 89 "sensitive_attribute": { 90 AttributeType: unmarshalType(t, cty.String), 91 }, 92 }, 93 }, 94 validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 95 "normal_attribute": renderers.ValidatePrimitive(nil, "some value", plans.Create, false), 96 }, nil, nil, nil, nil, plans.Create, false), 97 }, 98 } 99 for name, tc := range tcs { 100 // Set some default values 101 if tc.input.ReplacePaths == nil { 102 tc.input.ReplacePaths = &attribute_path.PathMatcher{} 103 } 104 105 if tc.input.RelevantAttributes == nil { 106 tc.input.RelevantAttributes = attribute_path.AlwaysMatcher() 107 } 108 109 t.Run(name, func(t *testing.T) { 110 tc.validate(t, tc.input.ComputeDiffForBlock(tc.block)) 111 }) 112 } 113 } 114 115 func TestValue_ObjectAttributes(t *testing.T) { 116 // This function holds a range of test cases creating, deleting and editing 117 // objects. It is built in such a way that it can automatically test these 118 // operations on objects both directly and nested, as well as within all 119 // types of collections. 120 121 tcs := map[string]struct { 122 input Change 123 attributes map[string]cty.Type 124 validateSingleDiff renderers.ValidateDiffFunction 125 validateObject renderers.ValidateDiffFunction 126 validateNestedObject renderers.ValidateDiffFunction 127 validateDiffs map[string]renderers.ValidateDiffFunction 128 validateList renderers.ValidateDiffFunction 129 validateReplace bool 130 validateAction plans.Action 131 // Sets break changes out differently to the other collections, so they 132 // have their own entry. 133 validateSetDiffs *SetDiff 134 }{ 135 "create": { 136 input: Change{ 137 Before: nil, 138 After: map[string]interface{}{ 139 "attribute_one": "new", 140 }, 141 }, 142 attributes: map[string]cty.Type{ 143 "attribute_one": cty.String, 144 }, 145 validateDiffs: map[string]renderers.ValidateDiffFunction{ 146 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 147 }, 148 validateAction: plans.Create, 149 validateReplace: false, 150 }, 151 "delete": { 152 input: Change{ 153 Before: map[string]interface{}{ 154 "attribute_one": "old", 155 }, 156 After: nil, 157 }, 158 attributes: map[string]cty.Type{ 159 "attribute_one": cty.String, 160 }, 161 validateDiffs: map[string]renderers.ValidateDiffFunction{ 162 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 163 }, 164 validateAction: plans.Delete, 165 validateReplace: false, 166 }, 167 "create_sensitive": { 168 input: Change{ 169 Before: nil, 170 After: map[string]interface{}{ 171 "attribute_one": "new", 172 }, 173 AfterSensitive: true, 174 }, 175 attributes: map[string]cty.Type{ 176 "attribute_one": cty.String, 177 }, 178 validateSingleDiff: renderers.ValidateSensitive(renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 179 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 180 }, plans.Create, false), 181 false, 182 true, 183 plans.Create, 184 false), 185 validateNestedObject: renderers.ValidateSensitive(renderers.ValidateNestedObject(map[string]renderers.ValidateDiffFunction{ 186 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 187 }, plans.Create, false), 188 false, 189 true, 190 plans.Create, 191 false), 192 }, 193 "delete_sensitive": { 194 input: Change{ 195 Before: map[string]interface{}{ 196 "attribute_one": "old", 197 }, 198 BeforeSensitive: true, 199 After: nil, 200 }, 201 attributes: map[string]cty.Type{ 202 "attribute_one": cty.String, 203 }, 204 validateSingleDiff: renderers.ValidateSensitive(renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 205 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 206 }, plans.Delete, false), true, false, plans.Delete, false), 207 validateNestedObject: renderers.ValidateSensitive(renderers.ValidateNestedObject(map[string]renderers.ValidateDiffFunction{ 208 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 209 }, plans.Delete, false), true, false, plans.Delete, false), 210 }, 211 "create_unknown": { 212 input: Change{ 213 Before: nil, 214 After: nil, 215 Unknown: true, 216 }, 217 attributes: map[string]cty.Type{ 218 "attribute_one": cty.String, 219 }, 220 validateSingleDiff: renderers.ValidateUnknown(nil, plans.Create, false), 221 }, 222 "update_unknown": { 223 input: Change{ 224 Before: map[string]interface{}{ 225 "attribute_one": "old", 226 }, 227 After: nil, 228 Unknown: true, 229 }, 230 attributes: map[string]cty.Type{ 231 "attribute_one": cty.String, 232 }, 233 validateObject: renderers.ValidateUnknown(renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 234 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 235 }, plans.Delete, false), plans.Update, false), 236 validateNestedObject: renderers.ValidateUnknown(renderers.ValidateNestedObject(map[string]renderers.ValidateDiffFunction{ 237 "attribute_one": renderers.ValidateUnknown(renderers.ValidatePrimitive("old", nil, plans.Delete, false), plans.Update, false), 238 }, plans.Update, false), plans.Update, false), 239 validateSetDiffs: &SetDiff{ 240 Before: SetDiffEntry{ 241 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 242 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 243 }, 244 Action: plans.Delete, 245 Replace: false, 246 }, 247 After: SetDiffEntry{ 248 SingleDiff: renderers.ValidateUnknown(nil, plans.Create, false), 249 }, 250 }, 251 }, 252 "create_attribute": { 253 input: Change{ 254 Before: map[string]interface{}{}, 255 After: map[string]interface{}{ 256 "attribute_one": "new", 257 }, 258 }, 259 attributes: map[string]cty.Type{ 260 "attribute_one": cty.String, 261 }, 262 validateDiffs: map[string]renderers.ValidateDiffFunction{ 263 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 264 }, 265 validateAction: plans.Update, 266 validateReplace: false, 267 validateSetDiffs: &SetDiff{ 268 Before: SetDiffEntry{ 269 ObjectDiff: nil, 270 Action: plans.Delete, 271 Replace: false, 272 }, 273 After: SetDiffEntry{ 274 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 275 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 276 }, 277 Action: plans.Create, 278 Replace: false, 279 }, 280 }, 281 }, 282 "create_attribute_from_explicit_null": { 283 input: Change{ 284 Before: map[string]interface{}{ 285 "attribute_one": nil, 286 }, 287 After: map[string]interface{}{ 288 "attribute_one": "new", 289 }, 290 }, 291 attributes: map[string]cty.Type{ 292 "attribute_one": cty.String, 293 }, 294 validateDiffs: map[string]renderers.ValidateDiffFunction{ 295 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 296 }, 297 validateAction: plans.Update, 298 validateReplace: false, 299 validateSetDiffs: &SetDiff{ 300 Before: SetDiffEntry{ 301 ObjectDiff: nil, 302 Action: plans.Delete, 303 Replace: false, 304 }, 305 After: SetDiffEntry{ 306 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 307 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 308 }, 309 Action: plans.Create, 310 Replace: false, 311 }, 312 }, 313 }, 314 "delete_attribute": { 315 input: Change{ 316 Before: map[string]interface{}{ 317 "attribute_one": "old", 318 }, 319 After: map[string]interface{}{}, 320 }, 321 attributes: map[string]cty.Type{ 322 "attribute_one": cty.String, 323 }, 324 validateDiffs: map[string]renderers.ValidateDiffFunction{ 325 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 326 }, 327 validateAction: plans.Update, 328 validateReplace: false, 329 validateSetDiffs: &SetDiff{ 330 Before: SetDiffEntry{ 331 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 332 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 333 }, 334 Action: plans.Delete, 335 Replace: false, 336 }, 337 After: SetDiffEntry{ 338 ObjectDiff: nil, 339 Action: plans.Create, 340 Replace: false, 341 }, 342 }, 343 }, 344 "delete_attribute_to_explicit_null": { 345 input: Change{ 346 Before: map[string]interface{}{ 347 "attribute_one": "old", 348 }, 349 After: map[string]interface{}{ 350 "attribute_one": nil, 351 }, 352 }, 353 attributes: map[string]cty.Type{ 354 "attribute_one": cty.String, 355 }, 356 validateDiffs: map[string]renderers.ValidateDiffFunction{ 357 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 358 }, 359 validateAction: plans.Update, 360 validateReplace: false, 361 validateSetDiffs: &SetDiff{ 362 Before: SetDiffEntry{ 363 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 364 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 365 }, 366 Action: plans.Delete, 367 Replace: false, 368 }, 369 After: SetDiffEntry{ 370 ObjectDiff: nil, 371 Action: plans.Create, 372 Replace: false, 373 }, 374 }, 375 }, 376 "update_attribute": { 377 input: Change{ 378 Before: map[string]interface{}{ 379 "attribute_one": "old", 380 }, 381 After: map[string]interface{}{ 382 "attribute_one": "new", 383 }, 384 }, 385 attributes: map[string]cty.Type{ 386 "attribute_one": cty.String, 387 }, 388 validateDiffs: map[string]renderers.ValidateDiffFunction{ 389 "attribute_one": renderers.ValidatePrimitive("old", "new", plans.Update, false), 390 }, 391 validateAction: plans.Update, 392 validateReplace: false, 393 validateSetDiffs: &SetDiff{ 394 Before: SetDiffEntry{ 395 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 396 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 397 }, 398 Action: plans.Delete, 399 Replace: false, 400 }, 401 After: SetDiffEntry{ 402 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 403 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 404 }, 405 Action: plans.Create, 406 Replace: false, 407 }, 408 }, 409 }, 410 "create_sensitive_attribute": { 411 input: Change{ 412 Before: map[string]interface{}{}, 413 After: map[string]interface{}{ 414 "attribute_one": "new", 415 }, 416 AfterSensitive: map[string]interface{}{ 417 "attribute_one": true, 418 }, 419 }, 420 attributes: map[string]cty.Type{ 421 "attribute_one": cty.String, 422 }, 423 validateDiffs: map[string]renderers.ValidateDiffFunction{ 424 "attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "new", plans.Create, false), false, true, plans.Create, false), 425 }, 426 validateAction: plans.Update, 427 validateReplace: false, 428 validateSetDiffs: &SetDiff{ 429 Before: SetDiffEntry{ 430 ObjectDiff: nil, 431 Action: plans.Delete, 432 Replace: false, 433 }, 434 After: SetDiffEntry{ 435 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 436 "attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "new", plans.Create, false), false, true, plans.Create, false), 437 }, 438 Action: plans.Create, 439 Replace: false, 440 }, 441 }, 442 }, 443 "delete_sensitive_attribute": { 444 input: Change{ 445 Before: map[string]interface{}{ 446 "attribute_one": "old", 447 }, 448 BeforeSensitive: map[string]interface{}{ 449 "attribute_one": true, 450 }, 451 After: map[string]interface{}{}, 452 }, 453 attributes: map[string]cty.Type{ 454 "attribute_one": cty.String, 455 }, 456 validateDiffs: map[string]renderers.ValidateDiffFunction{ 457 "attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive("old", nil, plans.Delete, false), true, false, plans.Delete, false), 458 }, 459 validateAction: plans.Update, 460 validateReplace: false, 461 validateSetDiffs: &SetDiff{ 462 Before: SetDiffEntry{ 463 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 464 "attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive("old", nil, plans.Delete, false), true, false, plans.Delete, false), 465 }, 466 Action: plans.Delete, 467 Replace: false, 468 }, 469 After: SetDiffEntry{ 470 ObjectDiff: nil, 471 Action: plans.Create, 472 Replace: false, 473 }, 474 }, 475 }, 476 "update_sensitive_attribute": { 477 input: Change{ 478 Before: map[string]interface{}{ 479 "attribute_one": "old", 480 }, 481 BeforeSensitive: map[string]interface{}{ 482 "attribute_one": true, 483 }, 484 After: map[string]interface{}{ 485 "attribute_one": "new", 486 }, 487 AfterSensitive: map[string]interface{}{ 488 "attribute_one": true, 489 }, 490 }, 491 attributes: map[string]cty.Type{ 492 "attribute_one": cty.String, 493 }, 494 validateDiffs: map[string]renderers.ValidateDiffFunction{ 495 "attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive("old", "new", plans.Update, false), true, true, plans.Update, false), 496 }, 497 validateAction: plans.Update, 498 validateReplace: false, 499 validateSetDiffs: &SetDiff{ 500 Before: SetDiffEntry{ 501 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 502 "attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive("old", nil, plans.Delete, false), true, false, plans.Delete, false), 503 }, 504 Action: plans.Delete, 505 Replace: false, 506 }, 507 After: SetDiffEntry{ 508 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 509 "attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "new", plans.Create, false), false, true, plans.Create, false), 510 }, 511 Action: plans.Create, 512 Replace: false, 513 }, 514 }, 515 }, 516 "create_computed_attribute": { 517 input: Change{ 518 Before: map[string]interface{}{}, 519 After: map[string]interface{}{}, 520 Unknown: map[string]interface{}{ 521 "attribute_one": true, 522 }, 523 }, 524 attributes: map[string]cty.Type{ 525 "attribute_one": cty.String, 526 }, 527 validateDiffs: map[string]renderers.ValidateDiffFunction{ 528 "attribute_one": renderers.ValidateUnknown(nil, plans.Create, false), 529 }, 530 validateAction: plans.Update, 531 validateReplace: false, 532 }, 533 "update_computed_attribute": { 534 input: Change{ 535 Before: map[string]interface{}{ 536 "attribute_one": "old", 537 }, 538 After: map[string]interface{}{}, 539 Unknown: map[string]interface{}{ 540 "attribute_one": true, 541 }, 542 }, 543 attributes: map[string]cty.Type{ 544 "attribute_one": cty.String, 545 }, 546 validateDiffs: map[string]renderers.ValidateDiffFunction{ 547 "attribute_one": renderers.ValidateUnknown( 548 renderers.ValidatePrimitive("old", nil, plans.Delete, false), 549 plans.Update, 550 false), 551 }, 552 validateAction: plans.Update, 553 validateReplace: false, 554 validateSetDiffs: &SetDiff{ 555 Before: SetDiffEntry{ 556 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 557 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 558 }, 559 Action: plans.Delete, 560 Replace: false, 561 }, 562 After: SetDiffEntry{ 563 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 564 "attribute_one": renderers.ValidateUnknown(nil, plans.Create, false), 565 }, 566 Action: plans.Create, 567 Replace: false, 568 }, 569 }, 570 }, 571 "ignores_unset_fields": { 572 input: Change{ 573 Before: map[string]interface{}{}, 574 After: map[string]interface{}{}, 575 }, 576 attributes: map[string]cty.Type{ 577 "attribute_one": cty.String, 578 }, 579 validateDiffs: map[string]renderers.ValidateDiffFunction{}, 580 validateAction: plans.NoOp, 581 validateReplace: false, 582 }, 583 "update_replace_self": { 584 input: Change{ 585 Before: map[string]interface{}{ 586 "attribute_one": "old", 587 }, 588 After: map[string]interface{}{ 589 "attribute_one": "new", 590 }, 591 ReplacePaths: &attribute_path.PathMatcher{ 592 Paths: [][]interface{}{ 593 {}, 594 }, 595 }, 596 }, 597 attributes: map[string]cty.Type{ 598 "attribute_one": cty.String, 599 }, 600 validateDiffs: map[string]renderers.ValidateDiffFunction{ 601 "attribute_one": renderers.ValidatePrimitive("old", "new", plans.Update, false), 602 }, 603 validateAction: plans.Update, 604 validateReplace: true, 605 validateSetDiffs: &SetDiff{ 606 Before: SetDiffEntry{ 607 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 608 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 609 }, 610 Action: plans.Delete, 611 Replace: true, 612 }, 613 After: SetDiffEntry{ 614 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 615 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 616 }, 617 Action: plans.Create, 618 Replace: true, 619 }, 620 }, 621 }, 622 "update_replace_attribute": { 623 input: Change{ 624 Before: map[string]interface{}{ 625 "attribute_one": "old", 626 }, 627 After: map[string]interface{}{ 628 "attribute_one": "new", 629 }, 630 ReplacePaths: &attribute_path.PathMatcher{ 631 Paths: [][]interface{}{ 632 {"attribute_one"}, 633 }, 634 }, 635 }, 636 attributes: map[string]cty.Type{ 637 "attribute_one": cty.String, 638 }, 639 validateDiffs: map[string]renderers.ValidateDiffFunction{ 640 "attribute_one": renderers.ValidatePrimitive("old", "new", plans.Update, true), 641 }, 642 validateAction: plans.Update, 643 validateReplace: false, 644 validateSetDiffs: &SetDiff{ 645 Before: SetDiffEntry{ 646 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 647 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, true), 648 }, 649 Action: plans.Delete, 650 Replace: false, 651 }, 652 After: SetDiffEntry{ 653 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 654 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, true), 655 }, 656 Action: plans.Create, 657 Replace: false, 658 }, 659 }, 660 }, 661 "update_includes_relevant_attributes": { 662 input: Change{ 663 Before: map[string]interface{}{ 664 "attribute_one": "old_one", 665 "attribute_two": "old_two", 666 }, 667 After: map[string]interface{}{ 668 "attribute_one": "new_one", 669 "attribute_two": "new_two", 670 }, 671 RelevantAttributes: &attribute_path.PathMatcher{ 672 Paths: [][]interface{}{ 673 {"attribute_one"}, 674 }, 675 }, 676 }, 677 attributes: map[string]cty.Type{ 678 "attribute_one": cty.String, 679 "attribute_two": cty.String, 680 }, 681 validateDiffs: map[string]renderers.ValidateDiffFunction{ 682 "attribute_one": renderers.ValidatePrimitive("old_one", "new_one", plans.Update, false), 683 "attribute_two": renderers.ValidatePrimitive("old_two", "old_two", plans.NoOp, false), 684 }, 685 validateList: renderers.ValidateList([]renderers.ValidateDiffFunction{ 686 renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 687 // Lists are a bit special, and in this case is actually 688 // going to ignore the relevant attributes. This is 689 // deliberate. See the comments in list.go for an 690 // explanation. 691 "attribute_one": renderers.ValidatePrimitive("old_one", "new_one", plans.Update, false), 692 "attribute_two": renderers.ValidatePrimitive("old_two", "new_two", plans.Update, false), 693 }, plans.Update, false), 694 }, plans.Update, false), 695 validateAction: plans.Update, 696 validateReplace: false, 697 validateSetDiffs: &SetDiff{ 698 Before: SetDiffEntry{ 699 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 700 "attribute_one": renderers.ValidatePrimitive("old_one", nil, plans.Delete, false), 701 "attribute_two": renderers.ValidatePrimitive("old_two", nil, plans.Delete, false), 702 }, 703 Action: plans.Delete, 704 Replace: false, 705 }, 706 After: SetDiffEntry{ 707 ObjectDiff: map[string]renderers.ValidateDiffFunction{ 708 "attribute_one": renderers.ValidatePrimitive(nil, "new_one", plans.Create, false), 709 "attribute_two": renderers.ValidatePrimitive(nil, "new_two", plans.Create, false), 710 }, 711 Action: plans.Create, 712 Replace: false, 713 }, 714 }, 715 }, 716 } 717 718 for name, tmp := range tcs { 719 tc := tmp 720 721 // Let's set some default values on the input. 722 if tc.input.RelevantAttributes == nil { 723 tc.input.RelevantAttributes = attribute_path.AlwaysMatcher() 724 } 725 if tc.input.ReplacePaths == nil { 726 tc.input.ReplacePaths = &attribute_path.PathMatcher{} 727 } 728 729 collectionDefaultAction := plans.Update 730 if name == "ignores_unset_fields" { 731 // Special case for this test, as it is the only one that doesn't 732 // have the collection types return an update. 733 collectionDefaultAction = plans.NoOp 734 } 735 736 t.Run(name, func(t *testing.T) { 737 t.Run("object", func(t *testing.T) { 738 attribute := &jsonprovider.Attribute{ 739 AttributeType: unmarshalType(t, cty.Object(tc.attributes)), 740 } 741 742 if tc.validateObject != nil { 743 tc.validateObject(t, tc.input.ComputeDiffForAttribute(attribute)) 744 return 745 } 746 747 if tc.validateSingleDiff != nil { 748 tc.validateSingleDiff(t, tc.input.ComputeDiffForAttribute(attribute)) 749 return 750 } 751 752 validate := renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace) 753 validate(t, tc.input.ComputeDiffForAttribute(attribute)) 754 }) 755 756 t.Run("map", func(t *testing.T) { 757 attribute := &jsonprovider.Attribute{ 758 AttributeType: unmarshalType(t, cty.Map(cty.Object(tc.attributes))), 759 } 760 761 input := wrapChangeInMap(tc.input) 762 763 if tc.validateObject != nil { 764 validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ 765 "element": tc.validateObject, 766 }, collectionDefaultAction, false) 767 validate(t, input.ComputeDiffForAttribute(attribute)) 768 return 769 } 770 771 if tc.validateSingleDiff != nil { 772 validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ 773 "element": tc.validateSingleDiff, 774 }, collectionDefaultAction, false) 775 validate(t, input.ComputeDiffForAttribute(attribute)) 776 return 777 } 778 779 validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ 780 "element": renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), 781 }, collectionDefaultAction, false) 782 validate(t, input.ComputeDiffForAttribute(attribute)) 783 }) 784 785 t.Run("list", func(t *testing.T) { 786 attribute := &jsonprovider.Attribute{ 787 AttributeType: unmarshalType(t, cty.List(cty.Object(tc.attributes))), 788 } 789 790 input := wrapChangeInSlice(tc.input) 791 792 if tc.validateList != nil { 793 tc.validateList(t, input.ComputeDiffForAttribute(attribute)) 794 return 795 } 796 797 if tc.validateObject != nil { 798 validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ 799 tc.validateObject, 800 }, collectionDefaultAction, false) 801 validate(t, input.ComputeDiffForAttribute(attribute)) 802 return 803 } 804 805 if tc.validateSingleDiff != nil { 806 validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ 807 tc.validateSingleDiff, 808 }, collectionDefaultAction, false) 809 validate(t, input.ComputeDiffForAttribute(attribute)) 810 return 811 } 812 813 validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ 814 renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), 815 }, collectionDefaultAction, false) 816 validate(t, input.ComputeDiffForAttribute(attribute)) 817 }) 818 819 t.Run("set", func(t *testing.T) { 820 attribute := &jsonprovider.Attribute{ 821 AttributeType: unmarshalType(t, cty.Set(cty.Object(tc.attributes))), 822 } 823 824 input := wrapChangeInSlice(tc.input) 825 826 if tc.validateSetDiffs != nil { 827 validate := renderers.ValidateSet(func() []renderers.ValidateDiffFunction { 828 var ret []renderers.ValidateDiffFunction 829 ret = append(ret, tc.validateSetDiffs.Before.Validate(renderers.ValidateObject)) 830 ret = append(ret, tc.validateSetDiffs.After.Validate(renderers.ValidateObject)) 831 return ret 832 }(), collectionDefaultAction, false) 833 validate(t, input.ComputeDiffForAttribute(attribute)) 834 return 835 } 836 837 if tc.validateObject != nil { 838 validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ 839 tc.validateObject, 840 }, collectionDefaultAction, false) 841 validate(t, input.ComputeDiffForAttribute(attribute)) 842 return 843 } 844 845 if tc.validateSingleDiff != nil { 846 validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ 847 tc.validateSingleDiff, 848 }, collectionDefaultAction, false) 849 validate(t, input.ComputeDiffForAttribute(attribute)) 850 return 851 } 852 853 validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ 854 renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), 855 }, collectionDefaultAction, false) 856 validate(t, input.ComputeDiffForAttribute(attribute)) 857 }) 858 }) 859 860 t.Run(fmt.Sprintf("nested_%s", name), func(t *testing.T) { 861 t.Run("object", func(t *testing.T) { 862 attribute := &jsonprovider.Attribute{ 863 AttributeNestedType: &jsonprovider.NestedType{ 864 Attributes: func() map[string]*jsonprovider.Attribute { 865 attributes := make(map[string]*jsonprovider.Attribute) 866 for key, attribute := range tc.attributes { 867 attributes[key] = &jsonprovider.Attribute{ 868 AttributeType: unmarshalType(t, attribute), 869 } 870 } 871 return attributes 872 }(), 873 NestingMode: "single", 874 }, 875 } 876 877 if tc.validateNestedObject != nil { 878 tc.validateNestedObject(t, tc.input.ComputeDiffForAttribute(attribute)) 879 return 880 } 881 882 if tc.validateSingleDiff != nil { 883 tc.validateSingleDiff(t, tc.input.ComputeDiffForAttribute(attribute)) 884 return 885 } 886 887 validate := renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace) 888 validate(t, tc.input.ComputeDiffForAttribute(attribute)) 889 }) 890 891 t.Run("map", func(t *testing.T) { 892 attribute := &jsonprovider.Attribute{ 893 AttributeNestedType: &jsonprovider.NestedType{ 894 Attributes: func() map[string]*jsonprovider.Attribute { 895 attributes := make(map[string]*jsonprovider.Attribute) 896 for key, attribute := range tc.attributes { 897 attributes[key] = &jsonprovider.Attribute{ 898 AttributeType: unmarshalType(t, attribute), 899 } 900 } 901 return attributes 902 }(), 903 NestingMode: "map", 904 }, 905 } 906 907 input := wrapChangeInMap(tc.input) 908 909 if tc.validateNestedObject != nil { 910 validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ 911 "element": tc.validateNestedObject, 912 }, collectionDefaultAction, false) 913 validate(t, input.ComputeDiffForAttribute(attribute)) 914 return 915 } 916 917 if tc.validateSingleDiff != nil { 918 validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ 919 "element": tc.validateSingleDiff, 920 }, collectionDefaultAction, false) 921 validate(t, input.ComputeDiffForAttribute(attribute)) 922 return 923 } 924 925 validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ 926 "element": renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), 927 }, collectionDefaultAction, false) 928 validate(t, input.ComputeDiffForAttribute(attribute)) 929 }) 930 931 t.Run("list", func(t *testing.T) { 932 attribute := &jsonprovider.Attribute{ 933 AttributeNestedType: &jsonprovider.NestedType{ 934 Attributes: func() map[string]*jsonprovider.Attribute { 935 attributes := make(map[string]*jsonprovider.Attribute) 936 for key, attribute := range tc.attributes { 937 attributes[key] = &jsonprovider.Attribute{ 938 AttributeType: unmarshalType(t, attribute), 939 } 940 } 941 return attributes 942 }(), 943 NestingMode: "list", 944 }, 945 } 946 947 input := wrapChangeInSlice(tc.input) 948 949 if tc.validateNestedObject != nil { 950 validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{ 951 tc.validateNestedObject, 952 }, collectionDefaultAction, false) 953 validate(t, input.ComputeDiffForAttribute(attribute)) 954 return 955 } 956 957 if tc.validateSingleDiff != nil { 958 validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{ 959 tc.validateSingleDiff, 960 }, collectionDefaultAction, false) 961 validate(t, input.ComputeDiffForAttribute(attribute)) 962 return 963 } 964 965 validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{ 966 renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), 967 }, collectionDefaultAction, false) 968 validate(t, input.ComputeDiffForAttribute(attribute)) 969 }) 970 971 t.Run("set", func(t *testing.T) { 972 attribute := &jsonprovider.Attribute{ 973 AttributeNestedType: &jsonprovider.NestedType{ 974 Attributes: func() map[string]*jsonprovider.Attribute { 975 attributes := make(map[string]*jsonprovider.Attribute) 976 for key, attribute := range tc.attributes { 977 attributes[key] = &jsonprovider.Attribute{ 978 AttributeType: unmarshalType(t, attribute), 979 } 980 } 981 return attributes 982 }(), 983 NestingMode: "set", 984 }, 985 } 986 987 input := wrapChangeInSlice(tc.input) 988 989 if tc.validateSetDiffs != nil { 990 validate := renderers.ValidateSet(func() []renderers.ValidateDiffFunction { 991 var ret []renderers.ValidateDiffFunction 992 ret = append(ret, tc.validateSetDiffs.Before.Validate(renderers.ValidateNestedObject)) 993 ret = append(ret, tc.validateSetDiffs.After.Validate(renderers.ValidateNestedObject)) 994 return ret 995 }(), collectionDefaultAction, false) 996 validate(t, input.ComputeDiffForAttribute(attribute)) 997 return 998 } 999 1000 if tc.validateNestedObject != nil { 1001 validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ 1002 tc.validateNestedObject, 1003 }, collectionDefaultAction, false) 1004 validate(t, input.ComputeDiffForAttribute(attribute)) 1005 return 1006 } 1007 1008 if tc.validateSingleDiff != nil { 1009 validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ 1010 tc.validateSingleDiff, 1011 }, collectionDefaultAction, false) 1012 validate(t, input.ComputeDiffForAttribute(attribute)) 1013 return 1014 } 1015 1016 validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ 1017 renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), 1018 }, collectionDefaultAction, false) 1019 validate(t, input.ComputeDiffForAttribute(attribute)) 1020 }) 1021 }) 1022 } 1023 } 1024 1025 func TestValue_BlockAttributesAndNestedBlocks(t *testing.T) { 1026 // This function tests manipulating simple attributes and blocks within 1027 // blocks. It automatically tests these operations within the contexts of 1028 // different block types. 1029 1030 tcs := map[string]struct { 1031 before interface{} 1032 after interface{} 1033 block *jsonprovider.Block 1034 validate renderers.ValidateDiffFunction 1035 validateSet []renderers.ValidateDiffFunction 1036 }{ 1037 "create_attribute": { 1038 before: map[string]interface{}{}, 1039 after: map[string]interface{}{ 1040 "attribute_one": "new", 1041 }, 1042 block: &jsonprovider.Block{ 1043 Attributes: map[string]*jsonprovider.Attribute{ 1044 "attribute_one": { 1045 AttributeType: unmarshalType(t, cty.String), 1046 }, 1047 }, 1048 }, 1049 validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1050 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1051 }, nil, nil, nil, nil, plans.Update, false), 1052 validateSet: []renderers.ValidateDiffFunction{ 1053 renderers.ValidateBlock(nil, nil, nil, nil, nil, plans.Delete, false), 1054 renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1055 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1056 }, nil, nil, nil, nil, plans.Create, false), 1057 }, 1058 }, 1059 "update_attribute": { 1060 before: map[string]interface{}{ 1061 "attribute_one": "old", 1062 }, 1063 after: map[string]interface{}{ 1064 "attribute_one": "new", 1065 }, 1066 block: &jsonprovider.Block{ 1067 Attributes: map[string]*jsonprovider.Attribute{ 1068 "attribute_one": { 1069 AttributeType: unmarshalType(t, cty.String), 1070 }, 1071 }, 1072 }, 1073 validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1074 "attribute_one": renderers.ValidatePrimitive("old", "new", plans.Update, false), 1075 }, nil, nil, nil, nil, plans.Update, false), 1076 validateSet: []renderers.ValidateDiffFunction{ 1077 renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1078 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1079 }, nil, nil, nil, nil, plans.Delete, false), 1080 renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1081 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1082 }, nil, nil, nil, nil, plans.Create, false), 1083 }, 1084 }, 1085 "delete_attribute": { 1086 before: map[string]interface{}{ 1087 "attribute_one": "old", 1088 }, 1089 after: map[string]interface{}{}, 1090 block: &jsonprovider.Block{ 1091 Attributes: map[string]*jsonprovider.Attribute{ 1092 "attribute_one": { 1093 AttributeType: unmarshalType(t, cty.String), 1094 }, 1095 }, 1096 }, 1097 validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1098 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1099 }, nil, nil, nil, nil, plans.Update, false), 1100 validateSet: []renderers.ValidateDiffFunction{ 1101 renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1102 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1103 }, nil, nil, nil, nil, plans.Delete, false), 1104 renderers.ValidateBlock(nil, nil, nil, nil, nil, plans.Create, false), 1105 }, 1106 }, 1107 "create_block": { 1108 before: map[string]interface{}{}, 1109 after: map[string]interface{}{ 1110 "block_one": map[string]interface{}{ 1111 "attribute_one": "new", 1112 }, 1113 }, 1114 block: &jsonprovider.Block{ 1115 BlockTypes: map[string]*jsonprovider.BlockType{ 1116 "block_one": { 1117 Block: &jsonprovider.Block{ 1118 Attributes: map[string]*jsonprovider.Attribute{ 1119 "attribute_one": { 1120 AttributeType: unmarshalType(t, cty.String), 1121 }, 1122 }, 1123 }, 1124 NestingMode: "single", 1125 }, 1126 }, 1127 }, 1128 validate: renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{ 1129 "block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1130 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1131 }, nil, nil, nil, nil, plans.Create, false), 1132 }, nil, nil, nil, plans.Update, false), 1133 validateSet: []renderers.ValidateDiffFunction{ 1134 renderers.ValidateBlock(nil, nil, nil, nil, nil, plans.Delete, false), 1135 renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{ 1136 "block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1137 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1138 }, nil, nil, nil, nil, plans.Create, false), 1139 }, nil, nil, nil, plans.Create, false), 1140 }, 1141 }, 1142 "update_block": { 1143 before: map[string]interface{}{ 1144 "block_one": map[string]interface{}{ 1145 "attribute_one": "old", 1146 }, 1147 }, 1148 after: map[string]interface{}{ 1149 "block_one": map[string]interface{}{ 1150 "attribute_one": "new", 1151 }, 1152 }, 1153 block: &jsonprovider.Block{ 1154 BlockTypes: map[string]*jsonprovider.BlockType{ 1155 "block_one": { 1156 Block: &jsonprovider.Block{ 1157 Attributes: map[string]*jsonprovider.Attribute{ 1158 "attribute_one": { 1159 AttributeType: unmarshalType(t, cty.String), 1160 }, 1161 }, 1162 }, 1163 NestingMode: "single", 1164 }, 1165 }, 1166 }, 1167 validate: renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{ 1168 "block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1169 "attribute_one": renderers.ValidatePrimitive("old", "new", plans.Update, false), 1170 }, nil, nil, nil, nil, plans.Update, false), 1171 }, nil, nil, nil, plans.Update, false), 1172 validateSet: []renderers.ValidateDiffFunction{ 1173 renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{ 1174 "block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1175 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1176 }, nil, nil, nil, nil, plans.Delete, false), 1177 }, nil, nil, nil, plans.Delete, false), 1178 renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{ 1179 "block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1180 "attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1181 }, nil, nil, nil, nil, plans.Create, false), 1182 }, nil, nil, nil, plans.Create, false), 1183 }, 1184 }, 1185 "delete_block": { 1186 before: map[string]interface{}{ 1187 "block_one": map[string]interface{}{ 1188 "attribute_one": "old", 1189 }, 1190 }, 1191 after: map[string]interface{}{}, 1192 block: &jsonprovider.Block{ 1193 BlockTypes: map[string]*jsonprovider.BlockType{ 1194 "block_one": { 1195 Block: &jsonprovider.Block{ 1196 Attributes: map[string]*jsonprovider.Attribute{ 1197 "attribute_one": { 1198 AttributeType: unmarshalType(t, cty.String), 1199 }, 1200 }, 1201 }, 1202 NestingMode: "single", 1203 }, 1204 }, 1205 }, 1206 validate: renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{ 1207 "block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1208 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1209 }, nil, nil, nil, nil, plans.Delete, false), 1210 }, nil, nil, nil, plans.Update, false), 1211 validateSet: []renderers.ValidateDiffFunction{ 1212 renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{ 1213 "block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 1214 "attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1215 }, nil, nil, nil, nil, plans.Delete, false), 1216 }, nil, nil, nil, plans.Delete, false), 1217 renderers.ValidateBlock(nil, nil, nil, nil, nil, plans.Create, false), 1218 }, 1219 }, 1220 } 1221 for name, tmp := range tcs { 1222 tc := tmp 1223 1224 t.Run(name, func(t *testing.T) { 1225 t.Run("single", func(t *testing.T) { 1226 input := Change{ 1227 Before: map[string]interface{}{ 1228 "block_type": tc.before, 1229 }, 1230 After: map[string]interface{}{ 1231 "block_type": tc.after, 1232 }, 1233 ReplacePaths: &attribute_path.PathMatcher{}, 1234 RelevantAttributes: attribute_path.AlwaysMatcher(), 1235 } 1236 1237 block := &jsonprovider.Block{ 1238 BlockTypes: map[string]*jsonprovider.BlockType{ 1239 "block_type": { 1240 Block: tc.block, 1241 NestingMode: "single", 1242 }, 1243 }, 1244 } 1245 1246 validate := renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{ 1247 "block_type": tc.validate, 1248 }, nil, nil, nil, plans.Update, false) 1249 validate(t, input.ComputeDiffForBlock(block)) 1250 }) 1251 t.Run("map", func(t *testing.T) { 1252 input := Change{ 1253 Before: map[string]interface{}{ 1254 "block_type": map[string]interface{}{ 1255 "one": tc.before, 1256 }, 1257 }, 1258 After: map[string]interface{}{ 1259 "block_type": map[string]interface{}{ 1260 "one": tc.after, 1261 }, 1262 }, 1263 ReplacePaths: &attribute_path.PathMatcher{}, 1264 RelevantAttributes: attribute_path.AlwaysMatcher(), 1265 } 1266 1267 block := &jsonprovider.Block{ 1268 BlockTypes: map[string]*jsonprovider.BlockType{ 1269 "block_type": { 1270 Block: tc.block, 1271 NestingMode: "map", 1272 }, 1273 }, 1274 } 1275 1276 validate := renderers.ValidateBlock(nil, nil, nil, map[string]map[string]renderers.ValidateDiffFunction{ 1277 "block_type": { 1278 "one": tc.validate, 1279 }, 1280 }, nil, plans.Update, false) 1281 validate(t, input.ComputeDiffForBlock(block)) 1282 }) 1283 t.Run("list", func(t *testing.T) { 1284 input := Change{ 1285 Before: map[string]interface{}{ 1286 "block_type": []interface{}{ 1287 tc.before, 1288 }, 1289 }, 1290 After: map[string]interface{}{ 1291 "block_type": []interface{}{ 1292 tc.after, 1293 }, 1294 }, 1295 ReplacePaths: &attribute_path.PathMatcher{}, 1296 RelevantAttributes: attribute_path.AlwaysMatcher(), 1297 } 1298 1299 block := &jsonprovider.Block{ 1300 BlockTypes: map[string]*jsonprovider.BlockType{ 1301 "block_type": { 1302 Block: tc.block, 1303 NestingMode: "list", 1304 }, 1305 }, 1306 } 1307 1308 validate := renderers.ValidateBlock(nil, nil, map[string][]renderers.ValidateDiffFunction{ 1309 "block_type": { 1310 tc.validate, 1311 }, 1312 }, nil, nil, plans.Update, false) 1313 validate(t, input.ComputeDiffForBlock(block)) 1314 }) 1315 t.Run("set", func(t *testing.T) { 1316 input := Change{ 1317 Before: map[string]interface{}{ 1318 "block_type": []interface{}{ 1319 tc.before, 1320 }, 1321 }, 1322 After: map[string]interface{}{ 1323 "block_type": []interface{}{ 1324 tc.after, 1325 }, 1326 }, 1327 ReplacePaths: &attribute_path.PathMatcher{}, 1328 RelevantAttributes: attribute_path.AlwaysMatcher(), 1329 } 1330 1331 block := &jsonprovider.Block{ 1332 BlockTypes: map[string]*jsonprovider.BlockType{ 1333 "block_type": { 1334 Block: tc.block, 1335 NestingMode: "set", 1336 }, 1337 }, 1338 } 1339 1340 validate := renderers.ValidateBlock(nil, nil, nil, nil, map[string][]renderers.ValidateDiffFunction{ 1341 "block_type": func() []renderers.ValidateDiffFunction { 1342 if tc.validateSet != nil { 1343 return tc.validateSet 1344 } 1345 return []renderers.ValidateDiffFunction{tc.validate} 1346 }(), 1347 }, plans.Update, false) 1348 validate(t, input.ComputeDiffForBlock(block)) 1349 }) 1350 }) 1351 } 1352 } 1353 1354 func TestValue_Outputs(t *testing.T) { 1355 tcs := map[string]struct { 1356 input Change 1357 validateDiff renderers.ValidateDiffFunction 1358 }{ 1359 "primitive_create": { 1360 input: Change{ 1361 Before: nil, 1362 After: "new", 1363 }, 1364 validateDiff: renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1365 }, 1366 "object_create": { 1367 input: Change{ 1368 Before: nil, 1369 After: map[string]interface{}{ 1370 "element_one": "new_one", 1371 "element_two": "new_two", 1372 }, 1373 }, 1374 validateDiff: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 1375 "element_one": renderers.ValidatePrimitive(nil, "new_one", plans.Create, false), 1376 "element_two": renderers.ValidatePrimitive(nil, "new_two", plans.Create, false), 1377 }, plans.Create, false), 1378 }, 1379 "list_create": { 1380 input: Change{ 1381 Before: nil, 1382 After: []interface{}{ 1383 "new_one", 1384 "new_two", 1385 }, 1386 }, 1387 validateDiff: renderers.ValidateList([]renderers.ValidateDiffFunction{ 1388 renderers.ValidatePrimitive(nil, "new_one", plans.Create, false), 1389 renderers.ValidatePrimitive(nil, "new_two", plans.Create, false), 1390 }, plans.Create, false), 1391 }, 1392 "primitive_update": { 1393 input: Change{ 1394 Before: "old", 1395 After: "new", 1396 }, 1397 validateDiff: renderers.ValidatePrimitive("old", "new", plans.Update, false), 1398 }, 1399 "object_update": { 1400 input: Change{ 1401 Before: map[string]interface{}{ 1402 "element_one": "old_one", 1403 "element_two": "old_two", 1404 }, 1405 After: map[string]interface{}{ 1406 "element_one": "new_one", 1407 "element_two": "new_two", 1408 }, 1409 }, 1410 validateDiff: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 1411 "element_one": renderers.ValidatePrimitive("old_one", "new_one", plans.Update, false), 1412 "element_two": renderers.ValidatePrimitive("old_two", "new_two", plans.Update, false), 1413 }, plans.Update, false), 1414 }, 1415 "list_update": { 1416 input: Change{ 1417 Before: []interface{}{ 1418 "old_one", 1419 "old_two", 1420 }, 1421 After: []interface{}{ 1422 "new_one", 1423 "new_two", 1424 }, 1425 }, 1426 validateDiff: renderers.ValidateList([]renderers.ValidateDiffFunction{ 1427 renderers.ValidatePrimitive("old_one", nil, plans.Delete, false), 1428 renderers.ValidatePrimitive("old_two", nil, plans.Delete, false), 1429 renderers.ValidatePrimitive(nil, "new_one", plans.Create, false), 1430 renderers.ValidatePrimitive(nil, "new_two", plans.Create, false), 1431 }, plans.Update, false), 1432 }, 1433 "primitive_delete": { 1434 input: Change{ 1435 Before: "old", 1436 After: nil, 1437 }, 1438 validateDiff: renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1439 }, 1440 "object_delete": { 1441 input: Change{ 1442 Before: map[string]interface{}{ 1443 "element_one": "old_one", 1444 "element_two": "old_two", 1445 }, 1446 After: nil, 1447 }, 1448 validateDiff: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 1449 "element_one": renderers.ValidatePrimitive("old_one", nil, plans.Delete, false), 1450 "element_two": renderers.ValidatePrimitive("old_two", nil, plans.Delete, false), 1451 }, plans.Delete, false), 1452 }, 1453 "list_delete": { 1454 input: Change{ 1455 Before: []interface{}{ 1456 "old_one", 1457 "old_two", 1458 }, 1459 After: nil, 1460 }, 1461 validateDiff: renderers.ValidateList([]renderers.ValidateDiffFunction{ 1462 renderers.ValidatePrimitive("old_one", nil, plans.Delete, false), 1463 renderers.ValidatePrimitive("old_two", nil, plans.Delete, false), 1464 }, plans.Delete, false), 1465 }, 1466 "primitive_to_list": { 1467 input: Change{ 1468 Before: "old", 1469 After: []interface{}{ 1470 "new_one", 1471 "new_two", 1472 }, 1473 }, 1474 validateDiff: renderers.ValidateTypeChange( 1475 renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1476 renderers.ValidateList([]renderers.ValidateDiffFunction{ 1477 renderers.ValidatePrimitive(nil, "new_one", plans.Create, false), 1478 renderers.ValidatePrimitive(nil, "new_two", plans.Create, false), 1479 }, plans.Create, false), plans.Update, false), 1480 }, 1481 "primitive_to_object": { 1482 input: Change{ 1483 Before: "old", 1484 After: map[string]interface{}{ 1485 "element_one": "new_one", 1486 "element_two": "new_two", 1487 }, 1488 }, 1489 validateDiff: renderers.ValidateTypeChange( 1490 renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1491 renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 1492 "element_one": renderers.ValidatePrimitive(nil, "new_one", plans.Create, false), 1493 "element_two": renderers.ValidatePrimitive(nil, "new_two", plans.Create, false), 1494 }, plans.Create, false), plans.Update, false), 1495 }, 1496 "list_to_primitive": { 1497 input: Change{ 1498 Before: []interface{}{ 1499 "old_one", 1500 "old_two", 1501 }, 1502 After: "new", 1503 }, 1504 validateDiff: renderers.ValidateTypeChange( 1505 renderers.ValidateList([]renderers.ValidateDiffFunction{ 1506 renderers.ValidatePrimitive("old_one", nil, plans.Delete, false), 1507 renderers.ValidatePrimitive("old_two", nil, plans.Delete, false), 1508 }, plans.Delete, false), 1509 renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1510 plans.Update, false), 1511 }, 1512 "list_to_object": { 1513 input: Change{ 1514 Before: []interface{}{ 1515 "old_one", 1516 "old_two", 1517 }, 1518 After: map[string]interface{}{ 1519 "element_one": "new_one", 1520 "element_two": "new_two", 1521 }, 1522 }, 1523 validateDiff: renderers.ValidateTypeChange( 1524 renderers.ValidateList([]renderers.ValidateDiffFunction{ 1525 renderers.ValidatePrimitive("old_one", nil, plans.Delete, false), 1526 renderers.ValidatePrimitive("old_two", nil, plans.Delete, false), 1527 }, plans.Delete, false), 1528 renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 1529 "element_one": renderers.ValidatePrimitive(nil, "new_one", plans.Create, false), 1530 "element_two": renderers.ValidatePrimitive(nil, "new_two", plans.Create, false), 1531 }, plans.Create, false), plans.Update, false), 1532 }, 1533 "object_to_primitive": { 1534 input: Change{ 1535 Before: map[string]interface{}{ 1536 "element_one": "old_one", 1537 "element_two": "old_two", 1538 }, 1539 After: "new", 1540 }, 1541 validateDiff: renderers.ValidateTypeChange( 1542 renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 1543 "element_one": renderers.ValidatePrimitive("old_one", nil, plans.Delete, false), 1544 "element_two": renderers.ValidatePrimitive("old_two", nil, plans.Delete, false), 1545 }, plans.Delete, false), 1546 renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1547 plans.Update, false), 1548 }, 1549 "object_to_list": { 1550 input: Change{ 1551 Before: map[string]interface{}{ 1552 "element_one": "old_one", 1553 "element_two": "old_two", 1554 }, 1555 After: []interface{}{ 1556 "new_one", 1557 "new_two", 1558 }, 1559 }, 1560 validateDiff: renderers.ValidateTypeChange( 1561 renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 1562 "element_one": renderers.ValidatePrimitive("old_one", nil, plans.Delete, false), 1563 "element_two": renderers.ValidatePrimitive("old_two", nil, plans.Delete, false), 1564 }, plans.Delete, false), 1565 renderers.ValidateList([]renderers.ValidateDiffFunction{ 1566 renderers.ValidatePrimitive(nil, "new_one", plans.Create, false), 1567 renderers.ValidatePrimitive(nil, "new_two", plans.Create, false), 1568 }, plans.Create, false), plans.Update, false), 1569 }, 1570 } 1571 1572 for name, tc := range tcs { 1573 1574 // Let's set some default values on the input. 1575 if tc.input.RelevantAttributes == nil { 1576 tc.input.RelevantAttributes = attribute_path.AlwaysMatcher() 1577 } 1578 if tc.input.ReplacePaths == nil { 1579 tc.input.ReplacePaths = &attribute_path.PathMatcher{} 1580 } 1581 1582 t.Run(name, func(t *testing.T) { 1583 tc.validateDiff(t, tc.input.ComputeDiffForOutput()) 1584 }) 1585 } 1586 } 1587 1588 func TestValue_PrimitiveAttributes(t *testing.T) { 1589 // This function tests manipulating primitives: creating, deleting and 1590 // updating. It also automatically tests these operations within the 1591 // contexts of collections. 1592 1593 tcs := map[string]struct { 1594 input Change 1595 attribute cty.Type 1596 validateDiff renderers.ValidateDiffFunction 1597 validateSliceDiffs []renderers.ValidateDiffFunction // Lists are special in some cases. 1598 }{ 1599 "primitive_create": { 1600 input: Change{ 1601 After: "new", 1602 }, 1603 attribute: cty.String, 1604 validateDiff: renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1605 }, 1606 "primitive_delete": { 1607 input: Change{ 1608 Before: "old", 1609 }, 1610 attribute: cty.String, 1611 validateDiff: renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1612 }, 1613 "primitive_update": { 1614 input: Change{ 1615 Before: "old", 1616 After: "new", 1617 }, 1618 attribute: cty.String, 1619 validateDiff: renderers.ValidatePrimitive("old", "new", plans.Update, false), 1620 validateSliceDiffs: []renderers.ValidateDiffFunction{ 1621 renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1622 renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1623 }, 1624 }, 1625 "primitive_set_explicit_null": { 1626 input: Change{ 1627 Before: "old", 1628 After: nil, 1629 AfterExplicit: true, 1630 }, 1631 attribute: cty.String, 1632 validateDiff: renderers.ValidatePrimitive("old", nil, plans.Update, false), 1633 validateSliceDiffs: []renderers.ValidateDiffFunction{ 1634 renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1635 renderers.ValidatePrimitive(nil, nil, plans.Create, false), 1636 }, 1637 }, 1638 "primitive_unset_explicit_null": { 1639 input: Change{ 1640 BeforeExplicit: true, 1641 Before: nil, 1642 After: "new", 1643 }, 1644 attribute: cty.String, 1645 validateDiff: renderers.ValidatePrimitive(nil, "new", plans.Update, false), 1646 validateSliceDiffs: []renderers.ValidateDiffFunction{ 1647 renderers.ValidatePrimitive(nil, nil, plans.Delete, false), 1648 renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1649 }, 1650 }, 1651 "primitive_create_sensitive": { 1652 input: Change{ 1653 Before: nil, 1654 After: "new", 1655 AfterSensitive: true, 1656 }, 1657 attribute: cty.String, 1658 validateDiff: renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "new", plans.Create, false), false, true, plans.Create, false), 1659 }, 1660 "primitive_delete_sensitive": { 1661 input: Change{ 1662 Before: "old", 1663 BeforeSensitive: true, 1664 After: nil, 1665 }, 1666 attribute: cty.String, 1667 validateDiff: renderers.ValidateSensitive(renderers.ValidatePrimitive("old", nil, plans.Delete, false), true, false, plans.Delete, false), 1668 }, 1669 "primitive_update_sensitive": { 1670 input: Change{ 1671 Before: "old", 1672 BeforeSensitive: true, 1673 After: "new", 1674 AfterSensitive: true, 1675 }, 1676 attribute: cty.String, 1677 validateDiff: renderers.ValidateSensitive(renderers.ValidatePrimitive("old", "new", plans.Update, false), true, true, plans.Update, false), 1678 validateSliceDiffs: []renderers.ValidateDiffFunction{ 1679 renderers.ValidateSensitive(renderers.ValidatePrimitive("old", nil, plans.Delete, false), true, false, plans.Delete, false), 1680 renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "new", plans.Create, false), false, true, plans.Create, false), 1681 }, 1682 }, 1683 "primitive_create_computed": { 1684 input: Change{ 1685 Before: nil, 1686 After: nil, 1687 Unknown: true, 1688 }, 1689 attribute: cty.String, 1690 validateDiff: renderers.ValidateUnknown(nil, plans.Create, false), 1691 }, 1692 "primitive_update_computed": { 1693 input: Change{ 1694 Before: "old", 1695 After: nil, 1696 Unknown: true, 1697 }, 1698 attribute: cty.String, 1699 validateDiff: renderers.ValidateUnknown(renderers.ValidatePrimitive("old", nil, plans.Delete, false), plans.Update, false), 1700 validateSliceDiffs: []renderers.ValidateDiffFunction{ 1701 renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1702 renderers.ValidateUnknown(nil, plans.Create, false), 1703 }, 1704 }, 1705 "primitive_update_replace": { 1706 input: Change{ 1707 Before: "old", 1708 After: "new", 1709 ReplacePaths: &attribute_path.PathMatcher{ 1710 Paths: [][]interface{}{ 1711 {}, // An empty path suggests replace should be true. 1712 }, 1713 }, 1714 }, 1715 attribute: cty.String, 1716 validateDiff: renderers.ValidatePrimitive("old", "new", plans.Update, true), 1717 validateSliceDiffs: []renderers.ValidateDiffFunction{ 1718 renderers.ValidatePrimitive("old", nil, plans.Delete, true), 1719 renderers.ValidatePrimitive(nil, "new", plans.Create, true), 1720 }, 1721 }, 1722 "noop": { 1723 input: Change{ 1724 Before: "old", 1725 After: "old", 1726 }, 1727 attribute: cty.String, 1728 validateDiff: renderers.ValidatePrimitive("old", "old", plans.NoOp, false), 1729 }, 1730 "dynamic": { 1731 input: Change{ 1732 Before: "old", 1733 After: "new", 1734 }, 1735 attribute: cty.DynamicPseudoType, 1736 validateDiff: renderers.ValidatePrimitive("old", "new", plans.Update, false), 1737 validateSliceDiffs: []renderers.ValidateDiffFunction{ 1738 renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1739 renderers.ValidatePrimitive(nil, "new", plans.Create, false), 1740 }, 1741 }, 1742 "dynamic_type_change": { 1743 input: Change{ 1744 Before: "old", 1745 After: 4.0, 1746 }, 1747 attribute: cty.DynamicPseudoType, 1748 validateDiff: renderers.ValidateTypeChange( 1749 renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1750 renderers.ValidatePrimitive(nil, 4.0, plans.Create, false), 1751 plans.Update, false), 1752 validateSliceDiffs: []renderers.ValidateDiffFunction{ 1753 renderers.ValidatePrimitive("old", nil, plans.Delete, false), 1754 renderers.ValidatePrimitive(nil, 4.0, plans.Create, false), 1755 }, 1756 }, 1757 } 1758 for name, tmp := range tcs { 1759 tc := tmp 1760 1761 // Let's set some default values on the input. 1762 if tc.input.RelevantAttributes == nil { 1763 tc.input.RelevantAttributes = attribute_path.AlwaysMatcher() 1764 } 1765 if tc.input.ReplacePaths == nil { 1766 tc.input.ReplacePaths = &attribute_path.PathMatcher{} 1767 } 1768 1769 defaultCollectionsAction := plans.Update 1770 if name == "noop" { 1771 defaultCollectionsAction = plans.NoOp 1772 } 1773 1774 t.Run(name, func(t *testing.T) { 1775 t.Run("direct", func(t *testing.T) { 1776 tc.validateDiff(t, tc.input.ComputeDiffForAttribute(&jsonprovider.Attribute{ 1777 AttributeType: unmarshalType(t, tc.attribute), 1778 })) 1779 }) 1780 1781 t.Run("map", func(t *testing.T) { 1782 input := wrapChangeInMap(tc.input) 1783 attribute := &jsonprovider.Attribute{ 1784 AttributeType: unmarshalType(t, cty.Map(tc.attribute)), 1785 } 1786 1787 validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ 1788 "element": tc.validateDiff, 1789 }, defaultCollectionsAction, false) 1790 validate(t, input.ComputeDiffForAttribute(attribute)) 1791 }) 1792 1793 t.Run("list", func(t *testing.T) { 1794 input := wrapChangeInSlice(tc.input) 1795 attribute := &jsonprovider.Attribute{ 1796 AttributeType: unmarshalType(t, cty.List(tc.attribute)), 1797 } 1798 1799 if tc.validateSliceDiffs != nil { 1800 validate := renderers.ValidateList(tc.validateSliceDiffs, defaultCollectionsAction, false) 1801 validate(t, input.ComputeDiffForAttribute(attribute)) 1802 return 1803 } 1804 1805 validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ 1806 tc.validateDiff, 1807 }, defaultCollectionsAction, false) 1808 validate(t, input.ComputeDiffForAttribute(attribute)) 1809 }) 1810 1811 t.Run("set", func(t *testing.T) { 1812 input := wrapChangeInSlice(tc.input) 1813 attribute := &jsonprovider.Attribute{ 1814 AttributeType: unmarshalType(t, cty.Set(tc.attribute)), 1815 } 1816 1817 if tc.validateSliceDiffs != nil { 1818 validate := renderers.ValidateSet(tc.validateSliceDiffs, defaultCollectionsAction, false) 1819 validate(t, input.ComputeDiffForAttribute(attribute)) 1820 return 1821 } 1822 1823 validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ 1824 tc.validateDiff, 1825 }, defaultCollectionsAction, false) 1826 validate(t, input.ComputeDiffForAttribute(attribute)) 1827 }) 1828 }) 1829 } 1830 } 1831 1832 func TestValue_CollectionAttributes(t *testing.T) { 1833 // This function tests creating and deleting collections. Note, it does not 1834 // generally cover editing collections except in special cases as editing 1835 // collections is handled automatically by other functions. 1836 tcs := map[string]struct { 1837 input Change 1838 attribute *jsonprovider.Attribute 1839 validateDiff renderers.ValidateDiffFunction 1840 }{ 1841 "map_create_empty": { 1842 input: Change{ 1843 Before: nil, 1844 After: map[string]interface{}{}, 1845 }, 1846 attribute: &jsonprovider.Attribute{ 1847 AttributeType: unmarshalType(t, cty.Map(cty.String)), 1848 }, 1849 validateDiff: renderers.ValidateMap(nil, plans.Create, false), 1850 }, 1851 "map_create_populated": { 1852 input: Change{ 1853 Before: nil, 1854 After: map[string]interface{}{ 1855 "element_one": "one", 1856 "element_two": "two", 1857 }, 1858 }, 1859 attribute: &jsonprovider.Attribute{ 1860 AttributeType: unmarshalType(t, cty.Map(cty.String)), 1861 }, 1862 validateDiff: renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ 1863 "element_one": renderers.ValidatePrimitive(nil, "one", plans.Create, false), 1864 "element_two": renderers.ValidatePrimitive(nil, "two", plans.Create, false), 1865 }, plans.Create, false), 1866 }, 1867 "map_delete_empty": { 1868 input: Change{ 1869 Before: map[string]interface{}{}, 1870 After: nil, 1871 }, 1872 attribute: &jsonprovider.Attribute{ 1873 AttributeType: unmarshalType(t, cty.Map(cty.String)), 1874 }, 1875 validateDiff: renderers.ValidateMap(nil, plans.Delete, false), 1876 }, 1877 "map_delete_populated": { 1878 input: Change{ 1879 Before: map[string]interface{}{ 1880 "element_one": "one", 1881 "element_two": "two", 1882 }, 1883 After: nil, 1884 }, 1885 attribute: &jsonprovider.Attribute{ 1886 AttributeType: unmarshalType(t, cty.Map(cty.String)), 1887 }, 1888 validateDiff: renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ 1889 "element_one": renderers.ValidatePrimitive("one", nil, plans.Delete, false), 1890 "element_two": renderers.ValidatePrimitive("two", nil, plans.Delete, false), 1891 }, plans.Delete, false), 1892 }, 1893 "map_create_sensitive": { 1894 input: Change{ 1895 Before: nil, 1896 After: map[string]interface{}{}, 1897 AfterSensitive: true, 1898 }, 1899 attribute: &jsonprovider.Attribute{ 1900 AttributeType: unmarshalType(t, cty.Map(cty.String)), 1901 }, 1902 validateDiff: renderers.ValidateSensitive(renderers.ValidateMap(nil, plans.Create, false), false, true, plans.Create, false), 1903 }, 1904 "map_update_sensitive": { 1905 input: Change{ 1906 Before: map[string]interface{}{ 1907 "element": "one", 1908 }, 1909 BeforeSensitive: true, 1910 After: map[string]interface{}{}, 1911 AfterSensitive: true, 1912 }, 1913 attribute: &jsonprovider.Attribute{ 1914 AttributeType: unmarshalType(t, cty.Map(cty.String)), 1915 }, 1916 validateDiff: renderers.ValidateSensitive(renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ 1917 "element": renderers.ValidatePrimitive("one", nil, plans.Delete, false), 1918 }, plans.Update, false), true, true, plans.Update, false), 1919 }, 1920 "map_delete_sensitive": { 1921 input: Change{ 1922 Before: map[string]interface{}{}, 1923 BeforeSensitive: true, 1924 After: nil, 1925 }, 1926 attribute: &jsonprovider.Attribute{ 1927 AttributeType: unmarshalType(t, cty.Map(cty.String)), 1928 }, 1929 validateDiff: renderers.ValidateSensitive(renderers.ValidateMap(nil, plans.Delete, false), true, false, plans.Delete, false), 1930 }, 1931 "map_create_unknown": { 1932 input: Change{ 1933 Before: nil, 1934 After: map[string]interface{}{}, 1935 Unknown: true, 1936 }, 1937 attribute: &jsonprovider.Attribute{ 1938 AttributeType: unmarshalType(t, cty.Map(cty.String)), 1939 }, 1940 validateDiff: renderers.ValidateUnknown(nil, plans.Create, false), 1941 }, 1942 "map_update_unknown": { 1943 input: Change{ 1944 Before: map[string]interface{}{}, 1945 After: map[string]interface{}{ 1946 "element": "one", 1947 }, 1948 Unknown: true, 1949 }, 1950 attribute: &jsonprovider.Attribute{ 1951 AttributeType: unmarshalType(t, cty.Map(cty.String)), 1952 }, 1953 validateDiff: renderers.ValidateUnknown(renderers.ValidateMap(nil, plans.Delete, false), plans.Update, false), 1954 }, 1955 "list_create_empty": { 1956 input: Change{ 1957 Before: nil, 1958 After: []interface{}{}, 1959 }, 1960 attribute: &jsonprovider.Attribute{ 1961 AttributeType: unmarshalType(t, cty.List(cty.String)), 1962 }, 1963 validateDiff: renderers.ValidateList(nil, plans.Create, false), 1964 }, 1965 "list_create_populated": { 1966 input: Change{ 1967 Before: nil, 1968 After: []interface{}{"one", "two"}, 1969 }, 1970 attribute: &jsonprovider.Attribute{ 1971 AttributeType: unmarshalType(t, cty.List(cty.String)), 1972 }, 1973 validateDiff: renderers.ValidateList([]renderers.ValidateDiffFunction{ 1974 renderers.ValidatePrimitive(nil, "one", plans.Create, false), 1975 renderers.ValidatePrimitive(nil, "two", plans.Create, false), 1976 }, plans.Create, false), 1977 }, 1978 "list_delete_empty": { 1979 input: Change{ 1980 Before: []interface{}{}, 1981 After: nil, 1982 }, 1983 attribute: &jsonprovider.Attribute{ 1984 AttributeType: unmarshalType(t, cty.List(cty.String)), 1985 }, 1986 validateDiff: renderers.ValidateList(nil, plans.Delete, false), 1987 }, 1988 "list_delete_populated": { 1989 input: Change{ 1990 Before: []interface{}{"one", "two"}, 1991 After: nil, 1992 }, 1993 attribute: &jsonprovider.Attribute{ 1994 AttributeType: unmarshalType(t, cty.List(cty.String)), 1995 }, 1996 validateDiff: renderers.ValidateList([]renderers.ValidateDiffFunction{ 1997 renderers.ValidatePrimitive("one", nil, plans.Delete, false), 1998 renderers.ValidatePrimitive("two", nil, plans.Delete, false), 1999 }, plans.Delete, false), 2000 }, 2001 "list_create_sensitive": { 2002 input: Change{ 2003 Before: nil, 2004 After: []interface{}{}, 2005 AfterSensitive: true, 2006 }, 2007 attribute: &jsonprovider.Attribute{ 2008 AttributeType: unmarshalType(t, cty.List(cty.String)), 2009 }, 2010 validateDiff: renderers.ValidateSensitive(renderers.ValidateList(nil, plans.Create, false), false, true, plans.Create, false), 2011 }, 2012 "list_update_sensitive": { 2013 input: Change{ 2014 Before: []interface{}{"one"}, 2015 BeforeSensitive: true, 2016 After: []interface{}{}, 2017 AfterSensitive: true, 2018 }, 2019 attribute: &jsonprovider.Attribute{ 2020 AttributeType: unmarshalType(t, cty.List(cty.String)), 2021 }, 2022 validateDiff: renderers.ValidateSensitive(renderers.ValidateList([]renderers.ValidateDiffFunction{ 2023 renderers.ValidatePrimitive("one", nil, plans.Delete, false), 2024 }, plans.Update, false), true, true, plans.Update, false), 2025 }, 2026 "list_delete_sensitive": { 2027 input: Change{ 2028 Before: []interface{}{}, 2029 BeforeSensitive: true, 2030 After: nil, 2031 }, 2032 attribute: &jsonprovider.Attribute{ 2033 AttributeType: unmarshalType(t, cty.List(cty.String)), 2034 }, 2035 validateDiff: renderers.ValidateSensitive(renderers.ValidateList(nil, plans.Delete, false), true, false, plans.Delete, false), 2036 }, 2037 "list_create_unknown": { 2038 input: Change{ 2039 Before: nil, 2040 After: []interface{}{}, 2041 Unknown: true, 2042 }, 2043 attribute: &jsonprovider.Attribute{ 2044 AttributeType: unmarshalType(t, cty.List(cty.String)), 2045 }, 2046 validateDiff: renderers.ValidateUnknown(nil, plans.Create, false), 2047 }, 2048 "list_update_unknown": { 2049 input: Change{ 2050 Before: []interface{}{}, 2051 After: []interface{}{"one"}, 2052 Unknown: true, 2053 }, 2054 attribute: &jsonprovider.Attribute{ 2055 AttributeType: unmarshalType(t, cty.List(cty.String)), 2056 }, 2057 validateDiff: renderers.ValidateUnknown(renderers.ValidateList(nil, plans.Delete, false), plans.Update, false), 2058 }, 2059 "set_create_empty": { 2060 input: Change{ 2061 Before: nil, 2062 After: []interface{}{}, 2063 }, 2064 attribute: &jsonprovider.Attribute{ 2065 AttributeType: unmarshalType(t, cty.Set(cty.String)), 2066 }, 2067 validateDiff: renderers.ValidateSet(nil, plans.Create, false), 2068 }, 2069 "set_create_populated": { 2070 input: Change{ 2071 Before: nil, 2072 After: []interface{}{"one", "two"}, 2073 }, 2074 attribute: &jsonprovider.Attribute{ 2075 AttributeType: unmarshalType(t, cty.Set(cty.String)), 2076 }, 2077 validateDiff: renderers.ValidateSet([]renderers.ValidateDiffFunction{ 2078 renderers.ValidatePrimitive(nil, "one", plans.Create, false), 2079 renderers.ValidatePrimitive(nil, "two", plans.Create, false), 2080 }, plans.Create, false), 2081 }, 2082 "set_delete_empty": { 2083 input: Change{ 2084 Before: []interface{}{}, 2085 After: nil, 2086 }, 2087 attribute: &jsonprovider.Attribute{ 2088 AttributeType: unmarshalType(t, cty.Set(cty.String)), 2089 }, 2090 validateDiff: renderers.ValidateSet(nil, plans.Delete, false), 2091 }, 2092 "set_delete_populated": { 2093 input: Change{ 2094 Before: []interface{}{"one", "two"}, 2095 After: nil, 2096 }, 2097 attribute: &jsonprovider.Attribute{ 2098 AttributeType: unmarshalType(t, cty.Set(cty.String)), 2099 }, 2100 validateDiff: renderers.ValidateSet([]renderers.ValidateDiffFunction{ 2101 renderers.ValidatePrimitive("one", nil, plans.Delete, false), 2102 renderers.ValidatePrimitive("two", nil, plans.Delete, false), 2103 }, plans.Delete, false), 2104 }, 2105 "set_create_sensitive": { 2106 input: Change{ 2107 Before: nil, 2108 After: []interface{}{}, 2109 AfterSensitive: true, 2110 }, 2111 attribute: &jsonprovider.Attribute{ 2112 AttributeType: unmarshalType(t, cty.Set(cty.String)), 2113 }, 2114 validateDiff: renderers.ValidateSensitive(renderers.ValidateSet(nil, plans.Create, false), false, true, plans.Create, false), 2115 }, 2116 "set_update_sensitive": { 2117 input: Change{ 2118 Before: []interface{}{"one"}, 2119 BeforeSensitive: true, 2120 After: []interface{}{}, 2121 AfterSensitive: true, 2122 }, 2123 attribute: &jsonprovider.Attribute{ 2124 AttributeType: unmarshalType(t, cty.Set(cty.String)), 2125 }, 2126 validateDiff: renderers.ValidateSensitive(renderers.ValidateSet([]renderers.ValidateDiffFunction{ 2127 renderers.ValidatePrimitive("one", nil, plans.Delete, false), 2128 }, plans.Update, false), true, true, plans.Update, false), 2129 }, 2130 "set_delete_sensitive": { 2131 input: Change{ 2132 Before: []interface{}{}, 2133 BeforeSensitive: true, 2134 After: nil, 2135 }, 2136 attribute: &jsonprovider.Attribute{ 2137 AttributeType: unmarshalType(t, cty.Set(cty.String)), 2138 }, 2139 validateDiff: renderers.ValidateSensitive(renderers.ValidateSet(nil, plans.Delete, false), true, false, plans.Delete, false), 2140 }, 2141 "set_create_unknown": { 2142 input: Change{ 2143 Before: nil, 2144 After: []interface{}{}, 2145 Unknown: true, 2146 }, 2147 attribute: &jsonprovider.Attribute{ 2148 AttributeType: unmarshalType(t, cty.Set(cty.String)), 2149 }, 2150 validateDiff: renderers.ValidateUnknown(nil, plans.Create, false), 2151 }, 2152 "set_update_unknown": { 2153 input: Change{ 2154 Before: []interface{}{}, 2155 After: []interface{}{"one"}, 2156 Unknown: true, 2157 }, 2158 attribute: &jsonprovider.Attribute{ 2159 AttributeType: unmarshalType(t, cty.Set(cty.String)), 2160 }, 2161 validateDiff: renderers.ValidateUnknown(renderers.ValidateSet(nil, plans.Delete, false), plans.Update, false), 2162 }, 2163 "tuple_primitive": { 2164 input: Change{ 2165 Before: []interface{}{ 2166 "one", 2167 2.0, 2168 "three", 2169 }, 2170 After: []interface{}{ 2171 "one", 2172 4.0, 2173 "three", 2174 }, 2175 }, 2176 attribute: &jsonprovider.Attribute{ 2177 AttributeType: unmarshalType(t, cty.Tuple([]cty.Type{cty.String, cty.Number, cty.String})), 2178 }, 2179 validateDiff: renderers.ValidateList([]renderers.ValidateDiffFunction{ 2180 renderers.ValidatePrimitive("one", "one", plans.NoOp, false), 2181 renderers.ValidatePrimitive(2.0, 4.0, plans.Update, false), 2182 renderers.ValidatePrimitive("three", "three", plans.NoOp, false), 2183 }, plans.Update, false), 2184 }, 2185 } 2186 2187 for name, tc := range tcs { 2188 2189 // Let's set some default values on the input. 2190 if tc.input.RelevantAttributes == nil { 2191 tc.input.RelevantAttributes = attribute_path.AlwaysMatcher() 2192 } 2193 if tc.input.ReplacePaths == nil { 2194 tc.input.ReplacePaths = &attribute_path.PathMatcher{} 2195 } 2196 2197 t.Run(name, func(t *testing.T) { 2198 tc.validateDiff(t, tc.input.ComputeDiffForAttribute(tc.attribute)) 2199 }) 2200 } 2201 } 2202 2203 func TestRelevantAttributes(t *testing.T) { 2204 tcs := map[string]struct { 2205 input Change 2206 block *jsonprovider.Block 2207 validate renderers.ValidateDiffFunction 2208 }{ 2209 "simple_attributes": { 2210 input: Change{ 2211 Before: map[string]interface{}{ 2212 "id": "old_id", 2213 "ignore": "doesn't matter", 2214 }, 2215 After: map[string]interface{}{ 2216 "id": "new_id", 2217 "ignore": "doesn't matter but modified", 2218 }, 2219 RelevantAttributes: &attribute_path.PathMatcher{ 2220 Paths: [][]interface{}{ 2221 { 2222 "id", 2223 }, 2224 }, 2225 }, 2226 }, 2227 block: &jsonprovider.Block{ 2228 Attributes: map[string]*jsonprovider.Attribute{ 2229 "id": { 2230 AttributeType: unmarshalType(t, cty.String), 2231 }, 2232 "ignore": { 2233 AttributeType: unmarshalType(t, cty.String), 2234 }, 2235 }, 2236 }, 2237 validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 2238 "id": renderers.ValidatePrimitive("old_id", "new_id", plans.Update, false), 2239 "ignore": renderers.ValidatePrimitive("doesn't matter", "doesn't matter", plans.NoOp, false), 2240 }, nil, nil, nil, nil, plans.Update, false), 2241 }, 2242 "nested_attributes": { 2243 input: Change{ 2244 Before: map[string]interface{}{ 2245 "list_block": []interface{}{ 2246 map[string]interface{}{ 2247 "id": "old_one", 2248 }, 2249 map[string]interface{}{ 2250 "id": "ignored", 2251 }, 2252 }, 2253 }, 2254 After: map[string]interface{}{ 2255 "list_block": []interface{}{ 2256 map[string]interface{}{ 2257 "id": "new_one", 2258 }, 2259 map[string]interface{}{ 2260 "id": "ignored_but_changed", 2261 }, 2262 }, 2263 }, 2264 RelevantAttributes: &attribute_path.PathMatcher{ 2265 Paths: [][]interface{}{ 2266 { 2267 "list_block", 2268 float64(0), 2269 "id", 2270 }, 2271 }, 2272 }, 2273 }, 2274 block: &jsonprovider.Block{ 2275 BlockTypes: map[string]*jsonprovider.BlockType{ 2276 "list_block": { 2277 Block: &jsonprovider.Block{ 2278 Attributes: map[string]*jsonprovider.Attribute{ 2279 "id": { 2280 AttributeType: unmarshalType(t, cty.String), 2281 }, 2282 }, 2283 }, 2284 NestingMode: "list", 2285 }, 2286 }, 2287 }, 2288 validate: renderers.ValidateBlock(nil, nil, map[string][]renderers.ValidateDiffFunction{ 2289 "list_block": { 2290 renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 2291 "id": renderers.ValidatePrimitive("old_one", "new_one", plans.Update, false), 2292 }, nil, nil, nil, nil, plans.Update, false), 2293 renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 2294 "id": renderers.ValidatePrimitive("ignored", "ignored", plans.NoOp, false), 2295 }, nil, nil, nil, nil, plans.NoOp, false), 2296 }, 2297 }, nil, nil, plans.Update, false), 2298 }, 2299 "nested_attributes_in_object": { 2300 input: Change{ 2301 Before: map[string]interface{}{ 2302 "object": map[string]interface{}{ 2303 "id": "old_id", 2304 }, 2305 }, 2306 After: map[string]interface{}{ 2307 "object": map[string]interface{}{ 2308 "id": "new_id", 2309 }, 2310 }, 2311 RelevantAttributes: &attribute_path.PathMatcher{ 2312 Propagate: true, 2313 Paths: [][]interface{}{ 2314 { 2315 "object", // Even though we just specify object, it should now include every below object as well. 2316 }, 2317 }, 2318 }, 2319 }, 2320 block: &jsonprovider.Block{ 2321 Attributes: map[string]*jsonprovider.Attribute{ 2322 "object": { 2323 AttributeType: unmarshalType(t, cty.Object(map[string]cty.Type{ 2324 "id": cty.String, 2325 })), 2326 }, 2327 }, 2328 }, 2329 validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 2330 "object": renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 2331 "id": renderers.ValidatePrimitive("old_id", "new_id", plans.Update, false), 2332 }, plans.Update, false), 2333 }, nil, nil, nil, nil, plans.Update, false), 2334 }, 2335 "elements_in_list": { 2336 input: Change{ 2337 Before: map[string]interface{}{ 2338 "list": []interface{}{ 2339 0, 1, 2, 3, 4, 2340 }, 2341 }, 2342 After: map[string]interface{}{ 2343 "list": []interface{}{ 2344 0, 5, 6, 7, 4, 2345 }, 2346 }, 2347 RelevantAttributes: &attribute_path.PathMatcher{ 2348 Paths: [][]interface{}{ // The list is actually just going to ignore this. 2349 { 2350 "list", 2351 float64(0), 2352 }, 2353 { 2354 "list", 2355 float64(2), 2356 }, 2357 { 2358 "list", 2359 float64(4), 2360 }, 2361 }, 2362 }, 2363 }, 2364 block: &jsonprovider.Block{ 2365 Attributes: map[string]*jsonprovider.Attribute{ 2366 "list": { 2367 AttributeType: unmarshalType(t, cty.List(cty.Number)), 2368 }, 2369 }, 2370 }, 2371 validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 2372 // The list validator below just ignores our relevant 2373 // attributes. This is deliberate. 2374 "list": renderers.ValidateList([]renderers.ValidateDiffFunction{ 2375 renderers.ValidatePrimitive(0, 0, plans.NoOp, false), 2376 renderers.ValidatePrimitive(1, nil, plans.Delete, false), 2377 renderers.ValidatePrimitive(2, nil, plans.Delete, false), 2378 renderers.ValidatePrimitive(3, nil, plans.Delete, false), 2379 renderers.ValidatePrimitive(nil, 5, plans.Create, false), 2380 renderers.ValidatePrimitive(nil, 6, plans.Create, false), 2381 renderers.ValidatePrimitive(nil, 7, plans.Create, false), 2382 renderers.ValidatePrimitive(4, 4, plans.NoOp, false), 2383 }, plans.Update, false), 2384 }, nil, nil, nil, nil, plans.Update, false), 2385 }, 2386 "elements_in_map": { 2387 input: Change{ 2388 Before: map[string]interface{}{ 2389 "map": map[string]interface{}{ 2390 "key_one": "value_one", 2391 "key_two": "value_two", 2392 "key_three": "value_three", 2393 }, 2394 }, 2395 After: map[string]interface{}{ 2396 "map": map[string]interface{}{ 2397 "key_one": "value_three", 2398 "key_two": "value_seven", 2399 "key_four": "value_four", 2400 }, 2401 }, 2402 RelevantAttributes: &attribute_path.PathMatcher{ 2403 Paths: [][]interface{}{ 2404 { 2405 "map", 2406 "key_one", 2407 }, 2408 { 2409 "map", 2410 "key_three", 2411 }, 2412 { 2413 "map", 2414 "key_four", 2415 }, 2416 }, 2417 }, 2418 }, 2419 block: &jsonprovider.Block{ 2420 Attributes: map[string]*jsonprovider.Attribute{ 2421 "map": { 2422 AttributeType: unmarshalType(t, cty.Map(cty.String)), 2423 }, 2424 }, 2425 }, 2426 validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 2427 "map": renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ 2428 "key_one": renderers.ValidatePrimitive("value_one", "value_three", plans.Update, false), 2429 "key_two": renderers.ValidatePrimitive("value_two", "value_two", plans.NoOp, false), 2430 "key_three": renderers.ValidatePrimitive("value_three", nil, plans.Delete, false), 2431 "key_four": renderers.ValidatePrimitive(nil, "value_four", plans.Create, false), 2432 }, plans.Update, false), 2433 }, nil, nil, nil, nil, plans.Update, false), 2434 }, 2435 "elements_in_set": { 2436 input: Change{ 2437 Before: map[string]interface{}{ 2438 "set": []interface{}{ 2439 0, 1, 2, 3, 4, 2440 }, 2441 }, 2442 After: map[string]interface{}{ 2443 "set": []interface{}{ 2444 0, 2, 4, 5, 6, 2445 }, 2446 }, 2447 RelevantAttributes: &attribute_path.PathMatcher{ 2448 Propagate: true, 2449 Paths: [][]interface{}{ 2450 { 2451 "set", 2452 }, 2453 }, 2454 }, 2455 }, 2456 block: &jsonprovider.Block{ 2457 Attributes: map[string]*jsonprovider.Attribute{ 2458 "set": { 2459 AttributeType: unmarshalType(t, cty.Set(cty.Number)), 2460 }, 2461 }, 2462 }, 2463 validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 2464 "set": renderers.ValidateSet([]renderers.ValidateDiffFunction{ 2465 renderers.ValidatePrimitive(0, 0, plans.NoOp, false), 2466 renderers.ValidatePrimitive(1, nil, plans.Delete, false), 2467 renderers.ValidatePrimitive(2, 2, plans.NoOp, false), 2468 renderers.ValidatePrimitive(3, nil, plans.Delete, false), 2469 renderers.ValidatePrimitive(4, 4, plans.NoOp, false), 2470 renderers.ValidatePrimitive(nil, 5, plans.Create, false), 2471 renderers.ValidatePrimitive(nil, 6, plans.Create, false), 2472 }, plans.Update, false), 2473 }, nil, nil, nil, nil, plans.Update, false), 2474 }, 2475 "dynamic_types": { 2476 input: Change{ 2477 Before: map[string]interface{}{ 2478 "dynamic_nested_type": map[string]interface{}{ 2479 "nested_id": "nomatch", 2480 "nested_object": map[string]interface{}{ 2481 "nested_nested_id": "matched", 2482 }, 2483 }, 2484 "dynamic_nested_type_match": map[string]interface{}{ 2485 "nested_id": "allmatch", 2486 "nested_object": map[string]interface{}{ 2487 "nested_nested_id": "allmatch", 2488 }, 2489 }, 2490 }, 2491 After: map[string]interface{}{ 2492 "dynamic_nested_type": map[string]interface{}{ 2493 "nested_id": "nomatch_changed", 2494 "nested_object": map[string]interface{}{ 2495 "nested_nested_id": "matched", 2496 }, 2497 }, 2498 "dynamic_nested_type_match": map[string]interface{}{ 2499 "nested_id": "allmatch", 2500 "nested_object": map[string]interface{}{ 2501 "nested_nested_id": "allmatch", 2502 }, 2503 }, 2504 }, 2505 RelevantAttributes: &attribute_path.PathMatcher{ 2506 Propagate: true, 2507 Paths: [][]interface{}{ 2508 { 2509 "dynamic_nested_type", 2510 "nested_object", 2511 "nested_nested_id", 2512 }, 2513 { 2514 "dynamic_nested_type_match", 2515 }, 2516 }, 2517 }, 2518 }, 2519 block: &jsonprovider.Block{ 2520 Attributes: map[string]*jsonprovider.Attribute{ 2521 "dynamic_nested_type": { 2522 AttributeType: unmarshalType(t, cty.DynamicPseudoType), 2523 }, 2524 "dynamic_nested_type_match": { 2525 AttributeType: unmarshalType(t, cty.DynamicPseudoType), 2526 }, 2527 }, 2528 }, 2529 validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{ 2530 "dynamic_nested_type": renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 2531 "nested_id": renderers.ValidatePrimitive("nomatch", "nomatch", plans.NoOp, false), 2532 "nested_object": renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 2533 "nested_nested_id": renderers.ValidatePrimitive("matched", "matched", plans.NoOp, false), 2534 }, plans.NoOp, false), 2535 }, plans.NoOp, false), 2536 "dynamic_nested_type_match": renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 2537 "nested_id": renderers.ValidatePrimitive("allmatch", "allmatch", plans.NoOp, false), 2538 "nested_object": renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{ 2539 "nested_nested_id": renderers.ValidatePrimitive("allmatch", "allmatch", plans.NoOp, false), 2540 }, plans.NoOp, false), 2541 }, plans.NoOp, false), 2542 }, nil, nil, nil, nil, plans.NoOp, false), 2543 }, 2544 } 2545 for name, tc := range tcs { 2546 if tc.input.ReplacePaths == nil { 2547 tc.input.ReplacePaths = &attribute_path.PathMatcher{} 2548 } 2549 t.Run(name, func(t *testing.T) { 2550 tc.validate(t, tc.input.ComputeDiffForBlock(tc.block)) 2551 }) 2552 } 2553 } 2554 2555 // unmarshalType converts a cty.Type into a json.RawMessage understood by the 2556 // schema. It also lets the testing framework handle any errors to keep the API 2557 // clean. 2558 func unmarshalType(t *testing.T, ctyType cty.Type) json.RawMessage { 2559 msg, err := ctyjson.MarshalType(ctyType) 2560 if err != nil { 2561 t.Fatalf("invalid type: %s", ctyType.FriendlyName()) 2562 } 2563 return msg 2564 } 2565 2566 // wrapChangeInSlice does the same as wrapChangeInMap, except it wraps it into a 2567 // slice internally. 2568 func wrapChangeInSlice(input Change) Change { 2569 return wrapChange(input, float64(0), func(value interface{}, unknown interface{}, explicit bool) interface{} { 2570 switch value.(type) { 2571 case nil: 2572 if set, ok := unknown.(bool); (set && ok) || explicit { 2573 return []interface{}{nil} 2574 2575 } 2576 return []interface{}{} 2577 default: 2578 return []interface{}{value} 2579 } 2580 }) 2581 } 2582 2583 // wrapChangeInMap access a single Change and returns a new Change that represents 2584 // a map with a single element. That single element is the input value. 2585 func wrapChangeInMap(input Change) Change { 2586 return wrapChange(input, "element", func(value interface{}, unknown interface{}, explicit bool) interface{} { 2587 switch value.(type) { 2588 case nil: 2589 if set, ok := unknown.(bool); (set && ok) || explicit { 2590 return map[string]interface{}{ 2591 "element": nil, 2592 } 2593 } 2594 return map[string]interface{}{} 2595 default: 2596 return map[string]interface{}{ 2597 "element": value, 2598 } 2599 } 2600 }) 2601 } 2602 2603 func wrapChange(input Change, step interface{}, wrap func(interface{}, interface{}, bool) interface{}) Change { 2604 2605 replacePaths := &attribute_path.PathMatcher{} 2606 for _, path := range input.ReplacePaths.(*attribute_path.PathMatcher).Paths { 2607 var updated []interface{} 2608 updated = append(updated, step) 2609 updated = append(updated, path...) 2610 replacePaths.Paths = append(replacePaths.Paths, updated) 2611 } 2612 2613 // relevantAttributes usually default to AlwaysMatcher, which means we can 2614 // just ignore it. But if we have had some paths specified we need to wrap 2615 // those as well. 2616 relevantAttributes := input.RelevantAttributes 2617 if concrete, ok := relevantAttributes.(*attribute_path.PathMatcher); ok { 2618 2619 newRelevantAttributes := &attribute_path.PathMatcher{} 2620 for _, path := range concrete.Paths { 2621 var updated []interface{} 2622 updated = append(updated, step) 2623 updated = append(updated, path...) 2624 newRelevantAttributes.Paths = append(newRelevantAttributes.Paths, updated) 2625 } 2626 relevantAttributes = newRelevantAttributes 2627 } 2628 2629 return Change{ 2630 Before: wrap(input.Before, nil, input.BeforeExplicit), 2631 After: wrap(input.After, input.Unknown, input.AfterExplicit), 2632 Unknown: wrap(input.Unknown, nil, false), 2633 BeforeSensitive: wrap(input.BeforeSensitive, nil, false), 2634 AfterSensitive: wrap(input.AfterSensitive, nil, false), 2635 ReplacePaths: replacePaths, 2636 RelevantAttributes: relevantAttributes, 2637 } 2638 }