github.com/richardmarshall/terraform@v0.9.5-0.20170429023105-15704cc6ee35/helper/schema/resource_test.go (about) 1 package schema 2 3 import ( 4 "fmt" 5 "reflect" 6 "strconv" 7 "testing" 8 "time" 9 10 "github.com/hashicorp/terraform/config" 11 "github.com/hashicorp/terraform/terraform" 12 ) 13 14 func TestResourceApply_create(t *testing.T) { 15 r := &Resource{ 16 SchemaVersion: 2, 17 Schema: map[string]*Schema{ 18 "foo": &Schema{ 19 Type: TypeInt, 20 Optional: true, 21 }, 22 }, 23 } 24 25 called := false 26 r.Create = func(d *ResourceData, m interface{}) error { 27 called = true 28 d.SetId("foo") 29 return nil 30 } 31 32 var s *terraform.InstanceState = nil 33 34 d := &terraform.InstanceDiff{ 35 Attributes: map[string]*terraform.ResourceAttrDiff{ 36 "foo": &terraform.ResourceAttrDiff{ 37 New: "42", 38 }, 39 }, 40 } 41 42 actual, err := r.Apply(s, d, nil) 43 if err != nil { 44 t.Fatalf("err: %s", err) 45 } 46 47 if !called { 48 t.Fatal("not called") 49 } 50 51 expected := &terraform.InstanceState{ 52 ID: "foo", 53 Attributes: map[string]string{ 54 "id": "foo", 55 "foo": "42", 56 }, 57 Meta: map[string]interface{}{ 58 "schema_version": "2", 59 }, 60 } 61 62 if !reflect.DeepEqual(actual, expected) { 63 t.Fatalf("bad: %#v", actual) 64 } 65 } 66 67 func TestResourceApply_Timeout_state(t *testing.T) { 68 r := &Resource{ 69 SchemaVersion: 2, 70 Schema: map[string]*Schema{ 71 "foo": &Schema{ 72 Type: TypeInt, 73 Optional: true, 74 }, 75 }, 76 Timeouts: &ResourceTimeout{ 77 Create: DefaultTimeout(40 * time.Minute), 78 Update: DefaultTimeout(80 * time.Minute), 79 Delete: DefaultTimeout(40 * time.Minute), 80 }, 81 } 82 83 called := false 84 r.Create = func(d *ResourceData, m interface{}) error { 85 called = true 86 d.SetId("foo") 87 return nil 88 } 89 90 var s *terraform.InstanceState = nil 91 92 d := &terraform.InstanceDiff{ 93 Attributes: map[string]*terraform.ResourceAttrDiff{ 94 "foo": &terraform.ResourceAttrDiff{ 95 New: "42", 96 }, 97 }, 98 } 99 100 diffTimeout := &ResourceTimeout{ 101 Create: DefaultTimeout(40 * time.Minute), 102 Update: DefaultTimeout(80 * time.Minute), 103 Delete: DefaultTimeout(40 * time.Minute), 104 } 105 106 if err := diffTimeout.DiffEncode(d); err != nil { 107 t.Fatalf("Error encoding timeout to diff: %s", err) 108 } 109 110 actual, err := r.Apply(s, d, nil) 111 if err != nil { 112 t.Fatalf("err: %s", err) 113 } 114 115 if !called { 116 t.Fatal("not called") 117 } 118 119 expected := &terraform.InstanceState{ 120 ID: "foo", 121 Attributes: map[string]string{ 122 "id": "foo", 123 "foo": "42", 124 }, 125 Meta: map[string]interface{}{ 126 "schema_version": "2", 127 TimeoutKey: expectedForValues(40, 0, 80, 40, 0), 128 }, 129 } 130 131 if !reflect.DeepEqual(actual, expected) { 132 t.Fatalf("Not equal in Timeout State:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) 133 } 134 } 135 136 func TestResourceDiff_Timeout_diff(t *testing.T) { 137 r := &Resource{ 138 Schema: map[string]*Schema{ 139 "foo": &Schema{ 140 Type: TypeInt, 141 Optional: true, 142 }, 143 }, 144 Timeouts: &ResourceTimeout{ 145 Create: DefaultTimeout(40 * time.Minute), 146 Update: DefaultTimeout(80 * time.Minute), 147 Delete: DefaultTimeout(40 * time.Minute), 148 }, 149 } 150 151 r.Create = func(d *ResourceData, m interface{}) error { 152 d.SetId("foo") 153 return nil 154 } 155 156 raw, err := config.NewRawConfig( 157 map[string]interface{}{ 158 "foo": 42, 159 "timeouts": []map[string]interface{}{ 160 map[string]interface{}{ 161 "create": "2h", 162 }}, 163 }) 164 if err != nil { 165 t.Fatalf("err: %s", err) 166 } 167 168 var s *terraform.InstanceState = nil 169 conf := terraform.NewResourceConfig(raw) 170 171 actual, err := r.Diff(s, conf) 172 if err != nil { 173 t.Fatalf("err: %s", err) 174 } 175 176 expected := &terraform.InstanceDiff{ 177 Attributes: map[string]*terraform.ResourceAttrDiff{ 178 "foo": &terraform.ResourceAttrDiff{ 179 New: "42", 180 }, 181 }, 182 } 183 184 diffTimeout := &ResourceTimeout{ 185 Create: DefaultTimeout(120 * time.Minute), 186 Update: DefaultTimeout(80 * time.Minute), 187 Delete: DefaultTimeout(40 * time.Minute), 188 } 189 190 if err := diffTimeout.DiffEncode(expected); err != nil { 191 t.Fatalf("Error encoding timeout to diff: %s", err) 192 } 193 194 if !reflect.DeepEqual(actual, expected) { 195 t.Fatalf("Not equal in Timeout Diff:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) 196 } 197 } 198 199 func TestResourceApply_destroy(t *testing.T) { 200 r := &Resource{ 201 Schema: map[string]*Schema{ 202 "foo": &Schema{ 203 Type: TypeInt, 204 Optional: true, 205 }, 206 }, 207 } 208 209 called := false 210 r.Delete = func(d *ResourceData, m interface{}) error { 211 called = true 212 return nil 213 } 214 215 s := &terraform.InstanceState{ 216 ID: "bar", 217 } 218 219 d := &terraform.InstanceDiff{ 220 Destroy: true, 221 } 222 223 actual, err := r.Apply(s, d, nil) 224 if err != nil { 225 t.Fatalf("err: %s", err) 226 } 227 228 if !called { 229 t.Fatal("delete not called") 230 } 231 232 if actual != nil { 233 t.Fatalf("bad: %#v", actual) 234 } 235 } 236 237 func TestResourceApply_destroyCreate(t *testing.T) { 238 r := &Resource{ 239 Schema: map[string]*Schema{ 240 "foo": &Schema{ 241 Type: TypeInt, 242 Optional: true, 243 }, 244 245 "tags": &Schema{ 246 Type: TypeMap, 247 Optional: true, 248 Computed: true, 249 }, 250 }, 251 } 252 253 change := false 254 r.Create = func(d *ResourceData, m interface{}) error { 255 change = d.HasChange("tags") 256 d.SetId("foo") 257 return nil 258 } 259 r.Delete = func(d *ResourceData, m interface{}) error { 260 return nil 261 } 262 263 var s *terraform.InstanceState = &terraform.InstanceState{ 264 ID: "bar", 265 Attributes: map[string]string{ 266 "foo": "bar", 267 "tags.Name": "foo", 268 }, 269 } 270 271 d := &terraform.InstanceDiff{ 272 Attributes: map[string]*terraform.ResourceAttrDiff{ 273 "foo": &terraform.ResourceAttrDiff{ 274 New: "42", 275 RequiresNew: true, 276 }, 277 "tags.Name": &terraform.ResourceAttrDiff{ 278 Old: "foo", 279 New: "foo", 280 RequiresNew: true, 281 }, 282 }, 283 } 284 285 actual, err := r.Apply(s, d, nil) 286 if err != nil { 287 t.Fatalf("err: %s", err) 288 } 289 290 if !change { 291 t.Fatal("should have change") 292 } 293 294 expected := &terraform.InstanceState{ 295 ID: "foo", 296 Attributes: map[string]string{ 297 "id": "foo", 298 "foo": "42", 299 "tags.%": "1", 300 "tags.Name": "foo", 301 }, 302 } 303 304 if !reflect.DeepEqual(actual, expected) { 305 t.Fatalf("bad: %#v", actual) 306 } 307 } 308 309 func TestResourceApply_destroyPartial(t *testing.T) { 310 r := &Resource{ 311 Schema: map[string]*Schema{ 312 "foo": &Schema{ 313 Type: TypeInt, 314 Optional: true, 315 }, 316 }, 317 SchemaVersion: 3, 318 } 319 320 r.Delete = func(d *ResourceData, m interface{}) error { 321 d.Set("foo", 42) 322 return fmt.Errorf("some error") 323 } 324 325 s := &terraform.InstanceState{ 326 ID: "bar", 327 Attributes: map[string]string{ 328 "foo": "12", 329 }, 330 } 331 332 d := &terraform.InstanceDiff{ 333 Destroy: true, 334 } 335 336 actual, err := r.Apply(s, d, nil) 337 if err == nil { 338 t.Fatal("should error") 339 } 340 341 expected := &terraform.InstanceState{ 342 ID: "bar", 343 Attributes: map[string]string{ 344 "id": "bar", 345 "foo": "42", 346 }, 347 Meta: map[string]interface{}{ 348 "schema_version": "3", 349 }, 350 } 351 352 if !reflect.DeepEqual(actual, expected) { 353 t.Fatalf("expected:\n%#v\n\ngot:\n%#v", expected, actual) 354 } 355 } 356 357 func TestResourceApply_update(t *testing.T) { 358 r := &Resource{ 359 Schema: map[string]*Schema{ 360 "foo": &Schema{ 361 Type: TypeInt, 362 Optional: true, 363 }, 364 }, 365 } 366 367 r.Update = func(d *ResourceData, m interface{}) error { 368 d.Set("foo", 42) 369 return nil 370 } 371 372 s := &terraform.InstanceState{ 373 ID: "foo", 374 Attributes: map[string]string{ 375 "foo": "12", 376 }, 377 } 378 379 d := &terraform.InstanceDiff{ 380 Attributes: map[string]*terraform.ResourceAttrDiff{ 381 "foo": &terraform.ResourceAttrDiff{ 382 New: "13", 383 }, 384 }, 385 } 386 387 actual, err := r.Apply(s, d, nil) 388 if err != nil { 389 t.Fatalf("err: %s", err) 390 } 391 392 expected := &terraform.InstanceState{ 393 ID: "foo", 394 Attributes: map[string]string{ 395 "id": "foo", 396 "foo": "42", 397 }, 398 } 399 400 if !reflect.DeepEqual(actual, expected) { 401 t.Fatalf("bad: %#v", actual) 402 } 403 } 404 405 func TestResourceApply_updateNoCallback(t *testing.T) { 406 r := &Resource{ 407 Schema: map[string]*Schema{ 408 "foo": &Schema{ 409 Type: TypeInt, 410 Optional: true, 411 }, 412 }, 413 } 414 415 r.Update = nil 416 417 s := &terraform.InstanceState{ 418 ID: "foo", 419 Attributes: map[string]string{ 420 "foo": "12", 421 }, 422 } 423 424 d := &terraform.InstanceDiff{ 425 Attributes: map[string]*terraform.ResourceAttrDiff{ 426 "foo": &terraform.ResourceAttrDiff{ 427 New: "13", 428 }, 429 }, 430 } 431 432 actual, err := r.Apply(s, d, nil) 433 if err == nil { 434 t.Fatal("should error") 435 } 436 437 expected := &terraform.InstanceState{ 438 ID: "foo", 439 Attributes: map[string]string{ 440 "foo": "12", 441 }, 442 } 443 444 if !reflect.DeepEqual(actual, expected) { 445 t.Fatalf("bad: %#v", actual) 446 } 447 } 448 449 func TestResourceApply_isNewResource(t *testing.T) { 450 r := &Resource{ 451 Schema: map[string]*Schema{ 452 "foo": &Schema{ 453 Type: TypeString, 454 Optional: true, 455 }, 456 }, 457 } 458 459 updateFunc := func(d *ResourceData, m interface{}) error { 460 d.Set("foo", "updated") 461 if d.IsNewResource() { 462 d.Set("foo", "new-resource") 463 } 464 return nil 465 } 466 r.Create = func(d *ResourceData, m interface{}) error { 467 d.SetId("foo") 468 d.Set("foo", "created") 469 return updateFunc(d, m) 470 } 471 r.Update = updateFunc 472 473 d := &terraform.InstanceDiff{ 474 Attributes: map[string]*terraform.ResourceAttrDiff{ 475 "foo": &terraform.ResourceAttrDiff{ 476 New: "bla-blah", 477 }, 478 }, 479 } 480 481 // positive test 482 var s *terraform.InstanceState = nil 483 484 actual, err := r.Apply(s, d, nil) 485 if err != nil { 486 t.Fatalf("err: %s", err) 487 } 488 489 expected := &terraform.InstanceState{ 490 ID: "foo", 491 Attributes: map[string]string{ 492 "id": "foo", 493 "foo": "new-resource", 494 }, 495 } 496 497 if !reflect.DeepEqual(actual, expected) { 498 t.Fatalf("actual: %#v\nexpected: %#v", 499 actual, expected) 500 } 501 502 // negative test 503 s = &terraform.InstanceState{ 504 ID: "foo", 505 Attributes: map[string]string{ 506 "id": "foo", 507 "foo": "new-resource", 508 }, 509 } 510 511 actual, err = r.Apply(s, d, nil) 512 if err != nil { 513 t.Fatalf("err: %s", err) 514 } 515 516 expected = &terraform.InstanceState{ 517 ID: "foo", 518 Attributes: map[string]string{ 519 "id": "foo", 520 "foo": "updated", 521 }, 522 } 523 524 if !reflect.DeepEqual(actual, expected) { 525 t.Fatalf("actual: %#v\nexpected: %#v", 526 actual, expected) 527 } 528 } 529 530 func TestResourceInternalValidate(t *testing.T) { 531 cases := []struct { 532 In *Resource 533 Writable bool 534 Err bool 535 }{ 536 { 537 nil, 538 true, 539 true, 540 }, 541 542 // No optional and no required 543 { 544 &Resource{ 545 Schema: map[string]*Schema{ 546 "foo": &Schema{ 547 Type: TypeInt, 548 Optional: true, 549 Required: true, 550 }, 551 }, 552 }, 553 true, 554 true, 555 }, 556 557 // Update undefined for non-ForceNew field 558 { 559 &Resource{ 560 Create: func(d *ResourceData, meta interface{}) error { return nil }, 561 Schema: map[string]*Schema{ 562 "boo": &Schema{ 563 Type: TypeInt, 564 Optional: true, 565 }, 566 }, 567 }, 568 true, 569 true, 570 }, 571 572 // Update defined for ForceNew field 573 { 574 &Resource{ 575 Create: func(d *ResourceData, meta interface{}) error { return nil }, 576 Update: func(d *ResourceData, meta interface{}) error { return nil }, 577 Schema: map[string]*Schema{ 578 "goo": &Schema{ 579 Type: TypeInt, 580 Optional: true, 581 ForceNew: true, 582 }, 583 }, 584 }, 585 true, 586 true, 587 }, 588 589 // non-writable doesn't need Update, Create or Delete 590 { 591 &Resource{ 592 Schema: map[string]*Schema{ 593 "goo": &Schema{ 594 Type: TypeInt, 595 Optional: true, 596 }, 597 }, 598 }, 599 false, 600 false, 601 }, 602 603 // non-writable *must not* have Create 604 { 605 &Resource{ 606 Create: func(d *ResourceData, meta interface{}) error { return nil }, 607 Schema: map[string]*Schema{ 608 "goo": &Schema{ 609 Type: TypeInt, 610 Optional: true, 611 }, 612 }, 613 }, 614 false, 615 true, 616 }, 617 618 // writable must have Read 619 { 620 &Resource{ 621 Create: func(d *ResourceData, meta interface{}) error { return nil }, 622 Update: func(d *ResourceData, meta interface{}) error { return nil }, 623 Delete: func(d *ResourceData, meta interface{}) error { return nil }, 624 Schema: map[string]*Schema{ 625 "goo": &Schema{ 626 Type: TypeInt, 627 Optional: true, 628 }, 629 }, 630 }, 631 true, 632 true, 633 }, 634 635 // writable must have Delete 636 { 637 &Resource{ 638 Create: func(d *ResourceData, meta interface{}) error { return nil }, 639 Read: func(d *ResourceData, meta interface{}) error { return nil }, 640 Update: func(d *ResourceData, meta interface{}) error { return nil }, 641 Schema: map[string]*Schema{ 642 "goo": &Schema{ 643 Type: TypeInt, 644 Optional: true, 645 }, 646 }, 647 }, 648 true, 649 true, 650 }, 651 } 652 653 for i, tc := range cases { 654 t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { 655 err := tc.In.InternalValidate(schemaMap{}, tc.Writable) 656 if err != nil != tc.Err { 657 t.Fatalf("%d: bad: %s", i, err) 658 } 659 }) 660 } 661 } 662 663 func TestResourceRefresh(t *testing.T) { 664 r := &Resource{ 665 SchemaVersion: 2, 666 Schema: map[string]*Schema{ 667 "foo": &Schema{ 668 Type: TypeInt, 669 Optional: true, 670 }, 671 }, 672 } 673 674 r.Read = func(d *ResourceData, m interface{}) error { 675 if m != 42 { 676 return fmt.Errorf("meta not passed") 677 } 678 679 return d.Set("foo", d.Get("foo").(int)+1) 680 } 681 682 s := &terraform.InstanceState{ 683 ID: "bar", 684 Attributes: map[string]string{ 685 "foo": "12", 686 }, 687 } 688 689 expected := &terraform.InstanceState{ 690 ID: "bar", 691 Attributes: map[string]string{ 692 "id": "bar", 693 "foo": "13", 694 }, 695 Meta: map[string]interface{}{ 696 "schema_version": "2", 697 }, 698 } 699 700 actual, err := r.Refresh(s, 42) 701 if err != nil { 702 t.Fatalf("err: %s", err) 703 } 704 705 if !reflect.DeepEqual(actual, expected) { 706 t.Fatalf("bad: %#v", actual) 707 } 708 } 709 710 func TestResourceRefresh_blankId(t *testing.T) { 711 r := &Resource{ 712 Schema: map[string]*Schema{ 713 "foo": &Schema{ 714 Type: TypeInt, 715 Optional: true, 716 }, 717 }, 718 } 719 720 r.Read = func(d *ResourceData, m interface{}) error { 721 d.SetId("foo") 722 return nil 723 } 724 725 s := &terraform.InstanceState{ 726 ID: "", 727 Attributes: map[string]string{}, 728 } 729 730 actual, err := r.Refresh(s, 42) 731 if err != nil { 732 t.Fatalf("err: %s", err) 733 } 734 if actual != nil { 735 t.Fatalf("bad: %#v", actual) 736 } 737 } 738 739 func TestResourceRefresh_delete(t *testing.T) { 740 r := &Resource{ 741 Schema: map[string]*Schema{ 742 "foo": &Schema{ 743 Type: TypeInt, 744 Optional: true, 745 }, 746 }, 747 } 748 749 r.Read = func(d *ResourceData, m interface{}) error { 750 d.SetId("") 751 return nil 752 } 753 754 s := &terraform.InstanceState{ 755 ID: "bar", 756 Attributes: map[string]string{ 757 "foo": "12", 758 }, 759 } 760 761 actual, err := r.Refresh(s, 42) 762 if err != nil { 763 t.Fatalf("err: %s", err) 764 } 765 766 if actual != nil { 767 t.Fatalf("bad: %#v", actual) 768 } 769 } 770 771 func TestResourceRefresh_existsError(t *testing.T) { 772 r := &Resource{ 773 Schema: map[string]*Schema{ 774 "foo": &Schema{ 775 Type: TypeInt, 776 Optional: true, 777 }, 778 }, 779 } 780 781 r.Exists = func(*ResourceData, interface{}) (bool, error) { 782 return false, fmt.Errorf("error") 783 } 784 785 r.Read = func(d *ResourceData, m interface{}) error { 786 panic("shouldn't be called") 787 } 788 789 s := &terraform.InstanceState{ 790 ID: "bar", 791 Attributes: map[string]string{ 792 "foo": "12", 793 }, 794 } 795 796 actual, err := r.Refresh(s, 42) 797 if err == nil { 798 t.Fatalf("should error") 799 } 800 if !reflect.DeepEqual(actual, s) { 801 t.Fatalf("bad: %#v", actual) 802 } 803 } 804 805 func TestResourceRefresh_noExists(t *testing.T) { 806 r := &Resource{ 807 Schema: map[string]*Schema{ 808 "foo": &Schema{ 809 Type: TypeInt, 810 Optional: true, 811 }, 812 }, 813 } 814 815 r.Exists = func(*ResourceData, interface{}) (bool, error) { 816 return false, nil 817 } 818 819 r.Read = func(d *ResourceData, m interface{}) error { 820 panic("shouldn't be called") 821 } 822 823 s := &terraform.InstanceState{ 824 ID: "bar", 825 Attributes: map[string]string{ 826 "foo": "12", 827 }, 828 } 829 830 actual, err := r.Refresh(s, 42) 831 if err != nil { 832 t.Fatalf("err: %s", err) 833 } 834 if actual != nil { 835 t.Fatalf("should have no state") 836 } 837 } 838 839 func TestResourceRefresh_needsMigration(t *testing.T) { 840 // Schema v2 it deals only in newfoo, which tracks foo as an int 841 r := &Resource{ 842 SchemaVersion: 2, 843 Schema: map[string]*Schema{ 844 "newfoo": &Schema{ 845 Type: TypeInt, 846 Optional: true, 847 }, 848 }, 849 } 850 851 r.Read = func(d *ResourceData, m interface{}) error { 852 return d.Set("newfoo", d.Get("newfoo").(int)+1) 853 } 854 855 r.MigrateState = func( 856 v int, 857 s *terraform.InstanceState, 858 meta interface{}) (*terraform.InstanceState, error) { 859 // Real state migration functions will probably switch on this value, 860 // but we'll just assert on it for now. 861 if v != 1 { 862 t.Fatalf("Expected StateSchemaVersion to be 1, got %d", v) 863 } 864 865 if meta != 42 { 866 t.Fatal("Expected meta to be passed through to the migration function") 867 } 868 869 oldfoo, err := strconv.ParseFloat(s.Attributes["oldfoo"], 64) 870 if err != nil { 871 t.Fatalf("err: %#v", err) 872 } 873 s.Attributes["newfoo"] = strconv.Itoa(int(oldfoo * 10)) 874 delete(s.Attributes, "oldfoo") 875 876 return s, nil 877 } 878 879 // State is v1 and deals in oldfoo, which tracked foo as a float at 1/10th 880 // the scale of newfoo 881 s := &terraform.InstanceState{ 882 ID: "bar", 883 Attributes: map[string]string{ 884 "oldfoo": "1.2", 885 }, 886 Meta: map[string]interface{}{ 887 "schema_version": "1", 888 }, 889 } 890 891 actual, err := r.Refresh(s, 42) 892 if err != nil { 893 t.Fatalf("err: %s", err) 894 } 895 896 expected := &terraform.InstanceState{ 897 ID: "bar", 898 Attributes: map[string]string{ 899 "id": "bar", 900 "newfoo": "13", 901 }, 902 Meta: map[string]interface{}{ 903 "schema_version": "2", 904 }, 905 } 906 907 if !reflect.DeepEqual(actual, expected) { 908 t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual) 909 } 910 } 911 912 func TestResourceRefresh_noMigrationNeeded(t *testing.T) { 913 r := &Resource{ 914 SchemaVersion: 2, 915 Schema: map[string]*Schema{ 916 "newfoo": &Schema{ 917 Type: TypeInt, 918 Optional: true, 919 }, 920 }, 921 } 922 923 r.Read = func(d *ResourceData, m interface{}) error { 924 return d.Set("newfoo", d.Get("newfoo").(int)+1) 925 } 926 927 r.MigrateState = func( 928 v int, 929 s *terraform.InstanceState, 930 meta interface{}) (*terraform.InstanceState, error) { 931 t.Fatal("Migrate function shouldn't be called!") 932 return nil, nil 933 } 934 935 s := &terraform.InstanceState{ 936 ID: "bar", 937 Attributes: map[string]string{ 938 "newfoo": "12", 939 }, 940 Meta: map[string]interface{}{ 941 "schema_version": "2", 942 }, 943 } 944 945 actual, err := r.Refresh(s, nil) 946 if err != nil { 947 t.Fatalf("err: %s", err) 948 } 949 950 expected := &terraform.InstanceState{ 951 ID: "bar", 952 Attributes: map[string]string{ 953 "id": "bar", 954 "newfoo": "13", 955 }, 956 Meta: map[string]interface{}{ 957 "schema_version": "2", 958 }, 959 } 960 961 if !reflect.DeepEqual(actual, expected) { 962 t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual) 963 } 964 } 965 966 func TestResourceRefresh_stateSchemaVersionUnset(t *testing.T) { 967 r := &Resource{ 968 // Version 1 > Version 0 969 SchemaVersion: 1, 970 Schema: map[string]*Schema{ 971 "newfoo": &Schema{ 972 Type: TypeInt, 973 Optional: true, 974 }, 975 }, 976 } 977 978 r.Read = func(d *ResourceData, m interface{}) error { 979 return d.Set("newfoo", d.Get("newfoo").(int)+1) 980 } 981 982 r.MigrateState = func( 983 v int, 984 s *terraform.InstanceState, 985 meta interface{}) (*terraform.InstanceState, error) { 986 s.Attributes["newfoo"] = s.Attributes["oldfoo"] 987 return s, nil 988 } 989 990 s := &terraform.InstanceState{ 991 ID: "bar", 992 Attributes: map[string]string{ 993 "oldfoo": "12", 994 }, 995 } 996 997 actual, err := r.Refresh(s, nil) 998 if err != nil { 999 t.Fatalf("err: %s", err) 1000 } 1001 1002 expected := &terraform.InstanceState{ 1003 ID: "bar", 1004 Attributes: map[string]string{ 1005 "id": "bar", 1006 "newfoo": "13", 1007 }, 1008 Meta: map[string]interface{}{ 1009 "schema_version": "1", 1010 }, 1011 } 1012 1013 if !reflect.DeepEqual(actual, expected) { 1014 t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual) 1015 } 1016 } 1017 1018 func TestResourceRefresh_migrateStateErr(t *testing.T) { 1019 r := &Resource{ 1020 SchemaVersion: 2, 1021 Schema: map[string]*Schema{ 1022 "newfoo": &Schema{ 1023 Type: TypeInt, 1024 Optional: true, 1025 }, 1026 }, 1027 } 1028 1029 r.Read = func(d *ResourceData, m interface{}) error { 1030 t.Fatal("Read should never be called!") 1031 return nil 1032 } 1033 1034 r.MigrateState = func( 1035 v int, 1036 s *terraform.InstanceState, 1037 meta interface{}) (*terraform.InstanceState, error) { 1038 return s, fmt.Errorf("triggering an error") 1039 } 1040 1041 s := &terraform.InstanceState{ 1042 ID: "bar", 1043 Attributes: map[string]string{ 1044 "oldfoo": "12", 1045 }, 1046 } 1047 1048 _, err := r.Refresh(s, nil) 1049 if err == nil { 1050 t.Fatal("expected error, but got none!") 1051 } 1052 } 1053 1054 func TestResourceData(t *testing.T) { 1055 r := &Resource{ 1056 SchemaVersion: 2, 1057 Schema: map[string]*Schema{ 1058 "foo": &Schema{ 1059 Type: TypeInt, 1060 Optional: true, 1061 }, 1062 }, 1063 } 1064 1065 state := &terraform.InstanceState{ 1066 ID: "foo", 1067 Attributes: map[string]string{ 1068 "id": "foo", 1069 "foo": "42", 1070 }, 1071 } 1072 1073 data := r.Data(state) 1074 if data.Id() != "foo" { 1075 t.Fatalf("err: %s", data.Id()) 1076 } 1077 if v := data.Get("foo"); v != 42 { 1078 t.Fatalf("bad: %#v", v) 1079 } 1080 1081 // Set expectations 1082 state.Meta = map[string]interface{}{ 1083 "schema_version": "2", 1084 } 1085 1086 result := data.State() 1087 if !reflect.DeepEqual(result, state) { 1088 t.Fatalf("bad: %#v", result) 1089 } 1090 } 1091 1092 func TestResourceData_blank(t *testing.T) { 1093 r := &Resource{ 1094 SchemaVersion: 2, 1095 Schema: map[string]*Schema{ 1096 "foo": &Schema{ 1097 Type: TypeInt, 1098 Optional: true, 1099 }, 1100 }, 1101 } 1102 1103 data := r.Data(nil) 1104 if data.Id() != "" { 1105 t.Fatalf("err: %s", data.Id()) 1106 } 1107 if v := data.Get("foo"); v != 0 { 1108 t.Fatalf("bad: %#v", v) 1109 } 1110 }