github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/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  }