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