github.com/bengesoff/terraform@v0.3.1-0.20141018223233-b25a53629922/helper/schema/resource_data_test.go (about)

     1  package schema
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/hashicorp/terraform/terraform"
     8  )
     9  
    10  func TestResourceDataGet(t *testing.T) {
    11  	cases := []struct {
    12  		Schema map[string]*Schema
    13  		State  *terraform.InstanceState
    14  		Diff   *terraform.InstanceDiff
    15  		Key    string
    16  		Value  interface{}
    17  	}{
    18  		{
    19  			Schema: map[string]*Schema{
    20  				"availability_zone": &Schema{
    21  					Type:     TypeString,
    22  					Optional: true,
    23  					Computed: true,
    24  					ForceNew: true,
    25  				},
    26  			},
    27  
    28  			State: nil,
    29  
    30  			Diff: &terraform.InstanceDiff{
    31  				Attributes: map[string]*terraform.ResourceAttrDiff{
    32  					"availability_zone": &terraform.ResourceAttrDiff{
    33  						Old:         "foo",
    34  						New:         "bar",
    35  						NewComputed: true,
    36  					},
    37  				},
    38  			},
    39  
    40  			Key:   "availability_zone",
    41  			Value: "",
    42  		},
    43  
    44  		{
    45  			Schema: map[string]*Schema{
    46  				"availability_zone": &Schema{
    47  					Type:     TypeString,
    48  					Optional: true,
    49  					Computed: true,
    50  					ForceNew: true,
    51  				},
    52  			},
    53  
    54  			State: nil,
    55  
    56  			Diff: &terraform.InstanceDiff{
    57  				Attributes: map[string]*terraform.ResourceAttrDiff{
    58  					"availability_zone": &terraform.ResourceAttrDiff{
    59  						Old:         "",
    60  						New:         "foo",
    61  						RequiresNew: true,
    62  					},
    63  				},
    64  			},
    65  
    66  			Key: "availability_zone",
    67  
    68  			Value: "foo",
    69  		},
    70  
    71  		{
    72  			Schema: map[string]*Schema{
    73  				"availability_zone": &Schema{
    74  					Type:     TypeString,
    75  					Optional: true,
    76  					Computed: true,
    77  					ForceNew: true,
    78  				},
    79  			},
    80  
    81  			State: nil,
    82  
    83  			Diff: &terraform.InstanceDiff{
    84  				Attributes: map[string]*terraform.ResourceAttrDiff{
    85  					"availability_zone": &terraform.ResourceAttrDiff{
    86  						Old:      "",
    87  						New:      "foo!",
    88  						NewExtra: "foo",
    89  					},
    90  				},
    91  			},
    92  
    93  			Key:   "availability_zone",
    94  			Value: "foo",
    95  		},
    96  
    97  		{
    98  			Schema: map[string]*Schema{
    99  				"availability_zone": &Schema{
   100  					Type:     TypeString,
   101  					Optional: true,
   102  					Computed: true,
   103  					ForceNew: true,
   104  				},
   105  			},
   106  
   107  			State: &terraform.InstanceState{
   108  				Attributes: map[string]string{
   109  					"availability_zone": "bar",
   110  				},
   111  			},
   112  
   113  			Diff: nil,
   114  
   115  			Key: "availability_zone",
   116  
   117  			Value: "bar",
   118  		},
   119  
   120  		{
   121  			Schema: map[string]*Schema{
   122  				"availability_zone": &Schema{
   123  					Type:     TypeString,
   124  					Optional: true,
   125  					Computed: true,
   126  					ForceNew: true,
   127  				},
   128  			},
   129  
   130  			State: &terraform.InstanceState{
   131  				Attributes: map[string]string{
   132  					"availability_zone": "foo",
   133  				},
   134  			},
   135  
   136  			Diff: &terraform.InstanceDiff{
   137  				Attributes: map[string]*terraform.ResourceAttrDiff{
   138  					"availability_zone": &terraform.ResourceAttrDiff{
   139  						Old:         "foo",
   140  						New:         "bar",
   141  						NewComputed: true,
   142  					},
   143  				},
   144  			},
   145  
   146  			Key:   "availability_zone",
   147  			Value: "",
   148  		},
   149  
   150  		{
   151  			Schema: map[string]*Schema{
   152  				"port": &Schema{
   153  					Type:     TypeInt,
   154  					Optional: true,
   155  					Computed: true,
   156  					ForceNew: true,
   157  				},
   158  			},
   159  
   160  			State: &terraform.InstanceState{
   161  				Attributes: map[string]string{
   162  					"port": "80",
   163  				},
   164  			},
   165  
   166  			Diff: nil,
   167  
   168  			Key: "port",
   169  
   170  			Value: 80,
   171  		},
   172  
   173  		{
   174  			Schema: map[string]*Schema{
   175  				"ports": &Schema{
   176  					Type:     TypeList,
   177  					Required: true,
   178  					Elem:     &Schema{Type: TypeInt},
   179  				},
   180  			},
   181  
   182  			State: &terraform.InstanceState{
   183  				Attributes: map[string]string{
   184  					"ports.#": "3",
   185  					"ports.0": "1",
   186  					"ports.1": "2",
   187  					"ports.2": "5",
   188  				},
   189  			},
   190  
   191  			Key: "ports.1",
   192  
   193  			Value: 2,
   194  		},
   195  
   196  		{
   197  			Schema: map[string]*Schema{
   198  				"ports": &Schema{
   199  					Type:     TypeList,
   200  					Required: true,
   201  					Elem:     &Schema{Type: TypeInt},
   202  				},
   203  			},
   204  
   205  			State: &terraform.InstanceState{
   206  				Attributes: map[string]string{
   207  					"ports.#": "3",
   208  					"ports.0": "1",
   209  					"ports.1": "2",
   210  					"ports.2": "5",
   211  				},
   212  			},
   213  
   214  			Key: "ports.#",
   215  
   216  			Value: 3,
   217  		},
   218  
   219  		{
   220  			Schema: map[string]*Schema{
   221  				"ports": &Schema{
   222  					Type:     TypeList,
   223  					Required: true,
   224  					Elem:     &Schema{Type: TypeInt},
   225  				},
   226  			},
   227  
   228  			State: nil,
   229  
   230  			Key: "ports.#",
   231  
   232  			Value: 0,
   233  		},
   234  
   235  		{
   236  			Schema: map[string]*Schema{
   237  				"ports": &Schema{
   238  					Type:     TypeList,
   239  					Required: true,
   240  					Elem:     &Schema{Type: TypeInt},
   241  				},
   242  			},
   243  
   244  			State: &terraform.InstanceState{
   245  				Attributes: map[string]string{
   246  					"ports.#": "3",
   247  					"ports.0": "1",
   248  					"ports.1": "2",
   249  					"ports.2": "5",
   250  				},
   251  			},
   252  
   253  			Key: "ports",
   254  
   255  			Value: []interface{}{1, 2, 5},
   256  		},
   257  
   258  		{
   259  			Schema: map[string]*Schema{
   260  				"ingress": &Schema{
   261  					Type:     TypeList,
   262  					Required: true,
   263  					Elem: &Resource{
   264  						Schema: map[string]*Schema{
   265  							"from": &Schema{
   266  								Type:     TypeInt,
   267  								Required: true,
   268  							},
   269  						},
   270  					},
   271  				},
   272  			},
   273  
   274  			State: nil,
   275  
   276  			Diff: &terraform.InstanceDiff{
   277  				Attributes: map[string]*terraform.ResourceAttrDiff{
   278  					"ingress.#": &terraform.ResourceAttrDiff{
   279  						Old: "",
   280  						New: "1",
   281  					},
   282  					"ingress.0.from": &terraform.ResourceAttrDiff{
   283  						Old: "",
   284  						New: "8080",
   285  					},
   286  				},
   287  			},
   288  
   289  			Key: "ingress.0",
   290  
   291  			Value: map[string]interface{}{
   292  				"from": 8080,
   293  			},
   294  		},
   295  
   296  		{
   297  			Schema: map[string]*Schema{
   298  				"ingress": &Schema{
   299  					Type:     TypeList,
   300  					Required: true,
   301  					Elem: &Resource{
   302  						Schema: map[string]*Schema{
   303  							"from": &Schema{
   304  								Type:     TypeInt,
   305  								Required: true,
   306  							},
   307  						},
   308  					},
   309  				},
   310  			},
   311  
   312  			State: nil,
   313  
   314  			Diff: &terraform.InstanceDiff{
   315  				Attributes: map[string]*terraform.ResourceAttrDiff{
   316  					"ingress.#": &terraform.ResourceAttrDiff{
   317  						Old: "",
   318  						New: "1",
   319  					},
   320  					"ingress.0.from": &terraform.ResourceAttrDiff{
   321  						Old: "",
   322  						New: "8080",
   323  					},
   324  				},
   325  			},
   326  
   327  			Key: "ingress",
   328  
   329  			Value: []interface{}{
   330  				map[string]interface{}{
   331  					"from": 8080,
   332  				},
   333  			},
   334  		},
   335  
   336  		// Computed get
   337  		{
   338  			Schema: map[string]*Schema{
   339  				"availability_zone": &Schema{
   340  					Type:     TypeString,
   341  					Computed: true,
   342  				},
   343  			},
   344  
   345  			State: &terraform.InstanceState{
   346  				Attributes: map[string]string{
   347  					"availability_zone": "foo",
   348  				},
   349  			},
   350  
   351  			Key: "availability_zone",
   352  
   353  			Value: "foo",
   354  		},
   355  
   356  		// Full object
   357  		{
   358  			Schema: map[string]*Schema{
   359  				"availability_zone": &Schema{
   360  					Type:     TypeString,
   361  					Optional: true,
   362  					Computed: true,
   363  					ForceNew: true,
   364  				},
   365  			},
   366  
   367  			State: nil,
   368  
   369  			Diff: &terraform.InstanceDiff{
   370  				Attributes: map[string]*terraform.ResourceAttrDiff{
   371  					"availability_zone": &terraform.ResourceAttrDiff{
   372  						Old:         "",
   373  						New:         "foo",
   374  						RequiresNew: true,
   375  					},
   376  				},
   377  			},
   378  
   379  			Key: "",
   380  
   381  			Value: map[string]interface{}{
   382  				"availability_zone": "foo",
   383  			},
   384  		},
   385  
   386  		// List of maps
   387  		{
   388  			Schema: map[string]*Schema{
   389  				"config_vars": &Schema{
   390  					Type:     TypeList,
   391  					Optional: true,
   392  					Computed: true,
   393  					Elem: &Schema{
   394  						Type: TypeMap,
   395  					},
   396  				},
   397  			},
   398  
   399  			State: nil,
   400  
   401  			Diff: &terraform.InstanceDiff{
   402  				Attributes: map[string]*terraform.ResourceAttrDiff{
   403  					"config_vars.#": &terraform.ResourceAttrDiff{
   404  						Old: "0",
   405  						New: "2",
   406  					},
   407  					"config_vars.0.foo": &terraform.ResourceAttrDiff{
   408  						Old: "",
   409  						New: "bar",
   410  					},
   411  					"config_vars.1.bar": &terraform.ResourceAttrDiff{
   412  						Old: "",
   413  						New: "baz",
   414  					},
   415  				},
   416  			},
   417  
   418  			Key: "config_vars",
   419  
   420  			Value: []interface{}{
   421  				map[string]interface{}{
   422  					"foo": "bar",
   423  				},
   424  				map[string]interface{}{
   425  					"bar": "baz",
   426  				},
   427  			},
   428  		},
   429  
   430  		// List of maps in state
   431  		{
   432  			Schema: map[string]*Schema{
   433  				"config_vars": &Schema{
   434  					Type:     TypeList,
   435  					Optional: true,
   436  					Computed: true,
   437  					Elem: &Schema{
   438  						Type: TypeMap,
   439  					},
   440  				},
   441  			},
   442  
   443  			State: &terraform.InstanceState{
   444  				Attributes: map[string]string{
   445  					"config_vars.#":     "2",
   446  					"config_vars.0.foo": "baz",
   447  					"config_vars.1.bar": "bar",
   448  				},
   449  			},
   450  
   451  			Diff: nil,
   452  
   453  			Key: "config_vars",
   454  
   455  			Value: []interface{}{
   456  				map[string]interface{}{
   457  					"foo": "baz",
   458  				},
   459  				map[string]interface{}{
   460  					"bar": "bar",
   461  				},
   462  			},
   463  		},
   464  
   465  		// List of maps with removal in diff
   466  		{
   467  			Schema: map[string]*Schema{
   468  				"config_vars": &Schema{
   469  					Type:     TypeList,
   470  					Optional: true,
   471  					Computed: true,
   472  					Elem: &Schema{
   473  						Type: TypeMap,
   474  					},
   475  				},
   476  			},
   477  
   478  			State: &terraform.InstanceState{
   479  				Attributes: map[string]string{
   480  					"config_vars.#":     "1",
   481  					"config_vars.0.FOO": "bar",
   482  				},
   483  			},
   484  
   485  			Diff: &terraform.InstanceDiff{
   486  				Attributes: map[string]*terraform.ResourceAttrDiff{
   487  					"config_vars.#": &terraform.ResourceAttrDiff{
   488  						Old: "1",
   489  						New: "0",
   490  					},
   491  					"config_vars.0.FOO": &terraform.ResourceAttrDiff{
   492  						Old:        "bar",
   493  						NewRemoved: true,
   494  					},
   495  				},
   496  			},
   497  
   498  			Key: "config_vars",
   499  
   500  			Value: []interface{}{},
   501  		},
   502  
   503  		// Sets
   504  		{
   505  			Schema: map[string]*Schema{
   506  				"ports": &Schema{
   507  					Type:     TypeSet,
   508  					Optional: true,
   509  					Computed: true,
   510  					Elem:     &Schema{Type: TypeInt},
   511  					Set: func(a interface{}) int {
   512  						return a.(int)
   513  					},
   514  				},
   515  			},
   516  
   517  			State: &terraform.InstanceState{
   518  				Attributes: map[string]string{
   519  					"ports.#": "1",
   520  					"ports.0": "80",
   521  				},
   522  			},
   523  
   524  			Diff: nil,
   525  
   526  			Key: "ports",
   527  
   528  			Value: []interface{}{80},
   529  		},
   530  	}
   531  
   532  	for i, tc := range cases {
   533  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
   534  		if err != nil {
   535  			t.Fatalf("err: %s", err)
   536  		}
   537  
   538  		v := d.Get(tc.Key)
   539  		if s, ok := v.(*Set); ok {
   540  			v = s.List()
   541  		}
   542  
   543  		if !reflect.DeepEqual(v, tc.Value) {
   544  			t.Fatalf("Bad: %d\n\n%#v", i, v)
   545  		}
   546  	}
   547  }
   548  
   549  func TestResourceDataGetChange(t *testing.T) {
   550  	cases := []struct {
   551  		Schema   map[string]*Schema
   552  		State    *terraform.InstanceState
   553  		Diff     *terraform.InstanceDiff
   554  		Key      string
   555  		OldValue interface{}
   556  		NewValue interface{}
   557  	}{
   558  		{
   559  			Schema: map[string]*Schema{
   560  				"availability_zone": &Schema{
   561  					Type:     TypeString,
   562  					Optional: true,
   563  					Computed: true,
   564  					ForceNew: true,
   565  				},
   566  			},
   567  
   568  			State: nil,
   569  
   570  			Diff: &terraform.InstanceDiff{
   571  				Attributes: map[string]*terraform.ResourceAttrDiff{
   572  					"availability_zone": &terraform.ResourceAttrDiff{
   573  						Old:         "",
   574  						New:         "foo",
   575  						RequiresNew: true,
   576  					},
   577  				},
   578  			},
   579  
   580  			Key: "availability_zone",
   581  
   582  			OldValue: "",
   583  			NewValue: "foo",
   584  		},
   585  
   586  		{
   587  			Schema: map[string]*Schema{
   588  				"availability_zone": &Schema{
   589  					Type:     TypeString,
   590  					Optional: true,
   591  					Computed: true,
   592  					ForceNew: true,
   593  				},
   594  			},
   595  
   596  			State: &terraform.InstanceState{
   597  				Attributes: map[string]string{
   598  					"availability_zone": "foo",
   599  				},
   600  			},
   601  
   602  			Diff: &terraform.InstanceDiff{
   603  				Attributes: map[string]*terraform.ResourceAttrDiff{
   604  					"availability_zone": &terraform.ResourceAttrDiff{
   605  						Old:         "",
   606  						New:         "foo",
   607  						RequiresNew: true,
   608  					},
   609  				},
   610  			},
   611  
   612  			Key: "availability_zone",
   613  
   614  			OldValue: "foo",
   615  			NewValue: "foo",
   616  		},
   617  	}
   618  
   619  	for i, tc := range cases {
   620  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
   621  		if err != nil {
   622  			t.Fatalf("err: %s", err)
   623  		}
   624  
   625  		o, n := d.GetChange(tc.Key)
   626  		if !reflect.DeepEqual(o, tc.OldValue) {
   627  			t.Fatalf("Old Bad: %d\n\n%#v", i, o)
   628  		}
   629  		if !reflect.DeepEqual(n, tc.NewValue) {
   630  			t.Fatalf("New Bad: %d\n\n%#v", i, n)
   631  		}
   632  	}
   633  }
   634  
   635  func TestResourceDataGetOk(t *testing.T) {
   636  	cases := []struct {
   637  		Schema map[string]*Schema
   638  		State  *terraform.InstanceState
   639  		Diff   *terraform.InstanceDiff
   640  		Key    string
   641  		Value  interface{}
   642  		Ok     bool
   643  	}{
   644  		/*
   645  		 * Primitives
   646  		 */
   647  		{
   648  			Schema: map[string]*Schema{
   649  				"availability_zone": &Schema{
   650  					Type:     TypeString,
   651  					Optional: true,
   652  					Computed: true,
   653  					ForceNew: true,
   654  				},
   655  			},
   656  
   657  			State: nil,
   658  
   659  			Diff: &terraform.InstanceDiff{
   660  				Attributes: map[string]*terraform.ResourceAttrDiff{
   661  					"availability_zone": &terraform.ResourceAttrDiff{
   662  						Old: "",
   663  						New: "",
   664  					},
   665  				},
   666  			},
   667  
   668  			Key:   "availability_zone",
   669  			Value: "",
   670  			Ok:    true,
   671  		},
   672  
   673  		{
   674  			Schema: map[string]*Schema{
   675  				"availability_zone": &Schema{
   676  					Type:     TypeString,
   677  					Optional: true,
   678  					Computed: true,
   679  					ForceNew: true,
   680  				},
   681  			},
   682  
   683  			State: nil,
   684  
   685  			Diff: nil,
   686  
   687  			Key:   "availability_zone",
   688  			Value: "",
   689  			Ok:    false,
   690  		},
   691  
   692  		/*
   693  		 * Lists
   694  		 */
   695  
   696  		{
   697  			Schema: map[string]*Schema{
   698  				"ports": &Schema{
   699  					Type:     TypeList,
   700  					Optional: true,
   701  					Elem:     &Schema{Type: TypeInt},
   702  				},
   703  			},
   704  
   705  			State: nil,
   706  
   707  			Diff: nil,
   708  
   709  			Key:   "ports",
   710  			Value: []interface{}{},
   711  			Ok:    false,
   712  		},
   713  
   714  		/*
   715  		 * Map
   716  		 */
   717  
   718  		{
   719  			Schema: map[string]*Schema{
   720  				"ports": &Schema{
   721  					Type:     TypeMap,
   722  					Optional: true,
   723  				},
   724  			},
   725  
   726  			State: nil,
   727  
   728  			Diff: nil,
   729  
   730  			Key:   "ports",
   731  			Value: map[string]interface{}{},
   732  			Ok:    false,
   733  		},
   734  
   735  		/*
   736  		 * Set
   737  		 */
   738  
   739  		{
   740  			Schema: map[string]*Schema{
   741  				"ports": &Schema{
   742  					Type:     TypeSet,
   743  					Optional: true,
   744  					Elem:     &Schema{Type: TypeInt},
   745  					Set:      func(a interface{}) int { return a.(int) },
   746  				},
   747  			},
   748  
   749  			State: nil,
   750  
   751  			Diff: nil,
   752  
   753  			Key:   "ports",
   754  			Value: []interface{}{},
   755  			Ok:    false,
   756  		},
   757  
   758  		{
   759  			Schema: map[string]*Schema{
   760  				"ports": &Schema{
   761  					Type:     TypeSet,
   762  					Optional: true,
   763  					Elem:     &Schema{Type: TypeInt},
   764  					Set:      func(a interface{}) int { return a.(int) },
   765  				},
   766  			},
   767  
   768  			State: nil,
   769  
   770  			Diff: nil,
   771  
   772  			Key:   "ports.0",
   773  			Value: 0,
   774  			Ok:    false,
   775  		},
   776  	}
   777  
   778  	for i, tc := range cases {
   779  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
   780  		if err != nil {
   781  			t.Fatalf("err: %s", err)
   782  		}
   783  
   784  		v, ok := d.GetOk(tc.Key)
   785  		if s, ok := v.(*Set); ok {
   786  			v = s.List()
   787  		}
   788  
   789  		if !reflect.DeepEqual(v, tc.Value) {
   790  			t.Fatalf("Bad: %d\n\n%#v", i, v)
   791  		}
   792  		if ok != tc.Ok {
   793  			t.Fatalf("Bad: %d\n\n%#v", i, ok)
   794  		}
   795  	}
   796  }
   797  
   798  func TestResourceDataHasChange(t *testing.T) {
   799  	cases := []struct {
   800  		Schema map[string]*Schema
   801  		State  *terraform.InstanceState
   802  		Diff   *terraform.InstanceDiff
   803  		Key    string
   804  		Change bool
   805  	}{
   806  		{
   807  			Schema: map[string]*Schema{
   808  				"availability_zone": &Schema{
   809  					Type:     TypeString,
   810  					Optional: true,
   811  					Computed: true,
   812  					ForceNew: true,
   813  				},
   814  			},
   815  
   816  			State: nil,
   817  
   818  			Diff: &terraform.InstanceDiff{
   819  				Attributes: map[string]*terraform.ResourceAttrDiff{
   820  					"availability_zone": &terraform.ResourceAttrDiff{
   821  						Old:         "",
   822  						New:         "foo",
   823  						RequiresNew: true,
   824  					},
   825  				},
   826  			},
   827  
   828  			Key: "availability_zone",
   829  
   830  			Change: true,
   831  		},
   832  
   833  		{
   834  			Schema: map[string]*Schema{
   835  				"availability_zone": &Schema{
   836  					Type:     TypeString,
   837  					Optional: true,
   838  					Computed: true,
   839  					ForceNew: true,
   840  				},
   841  			},
   842  
   843  			State: &terraform.InstanceState{
   844  				Attributes: map[string]string{
   845  					"availability_zone": "foo",
   846  				},
   847  			},
   848  
   849  			Diff: &terraform.InstanceDiff{
   850  				Attributes: map[string]*terraform.ResourceAttrDiff{
   851  					"availability_zone": &terraform.ResourceAttrDiff{
   852  						Old:         "",
   853  						New:         "foo",
   854  						RequiresNew: true,
   855  					},
   856  				},
   857  			},
   858  
   859  			Key: "availability_zone",
   860  
   861  			Change: false,
   862  		},
   863  	}
   864  
   865  	for i, tc := range cases {
   866  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
   867  		if err != nil {
   868  			t.Fatalf("err: %s", err)
   869  		}
   870  
   871  		actual := d.HasChange(tc.Key)
   872  		if actual != tc.Change {
   873  			t.Fatalf("Bad: %d %#v", i, actual)
   874  		}
   875  	}
   876  }
   877  
   878  func TestResourceDataSet(t *testing.T) {
   879  	cases := []struct {
   880  		Schema   map[string]*Schema
   881  		State    *terraform.InstanceState
   882  		Diff     *terraform.InstanceDiff
   883  		Key      string
   884  		Value    interface{}
   885  		Err      bool
   886  		GetKey   string
   887  		GetValue interface{}
   888  	}{
   889  		// Basic good
   890  		{
   891  			Schema: map[string]*Schema{
   892  				"availability_zone": &Schema{
   893  					Type:     TypeString,
   894  					Optional: true,
   895  					Computed: true,
   896  					ForceNew: true,
   897  				},
   898  			},
   899  
   900  			State: nil,
   901  
   902  			Diff: nil,
   903  
   904  			Key:   "availability_zone",
   905  			Value: "foo",
   906  
   907  			GetKey:   "availability_zone",
   908  			GetValue: "foo",
   909  		},
   910  
   911  		// Basic int
   912  		{
   913  			Schema: map[string]*Schema{
   914  				"port": &Schema{
   915  					Type:     TypeInt,
   916  					Optional: true,
   917  					Computed: true,
   918  					ForceNew: true,
   919  				},
   920  			},
   921  
   922  			State: nil,
   923  
   924  			Diff: nil,
   925  
   926  			Key:   "port",
   927  			Value: 80,
   928  
   929  			GetKey:   "port",
   930  			GetValue: 80,
   931  		},
   932  
   933  		// Basic bool
   934  		{
   935  			Schema: map[string]*Schema{
   936  				"vpc": &Schema{
   937  					Type:     TypeBool,
   938  					Optional: true,
   939  				},
   940  			},
   941  
   942  			State: nil,
   943  
   944  			Diff: nil,
   945  
   946  			Key:   "vpc",
   947  			Value: true,
   948  
   949  			GetKey:   "vpc",
   950  			GetValue: true,
   951  		},
   952  
   953  		{
   954  			Schema: map[string]*Schema{
   955  				"vpc": &Schema{
   956  					Type:     TypeBool,
   957  					Optional: true,
   958  				},
   959  			},
   960  
   961  			State: nil,
   962  
   963  			Diff: nil,
   964  
   965  			Key:   "vpc",
   966  			Value: false,
   967  
   968  			GetKey:   "vpc",
   969  			GetValue: false,
   970  		},
   971  
   972  		// Invalid type
   973  		{
   974  			Schema: map[string]*Schema{
   975  				"availability_zone": &Schema{
   976  					Type:     TypeString,
   977  					Optional: true,
   978  					Computed: true,
   979  					ForceNew: true,
   980  				},
   981  			},
   982  
   983  			State: nil,
   984  
   985  			Diff: nil,
   986  
   987  			Key:   "availability_zone",
   988  			Value: 80,
   989  			Err:   true,
   990  
   991  			GetKey:   "availability_zone",
   992  			GetValue: "",
   993  		},
   994  
   995  		// List of primitives, set element
   996  		{
   997  			Schema: map[string]*Schema{
   998  				"ports": &Schema{
   999  					Type:     TypeList,
  1000  					Computed: true,
  1001  					Elem:     &Schema{Type: TypeInt},
  1002  				},
  1003  			},
  1004  
  1005  			State: &terraform.InstanceState{
  1006  				Attributes: map[string]string{
  1007  					"ports.#": "3",
  1008  					"ports.0": "1",
  1009  					"ports.1": "2",
  1010  					"ports.2": "5",
  1011  				},
  1012  			},
  1013  
  1014  			Diff: nil,
  1015  
  1016  			Key:   "ports.1",
  1017  			Value: 3,
  1018  
  1019  			GetKey:   "ports",
  1020  			GetValue: []interface{}{1, 3, 5},
  1021  		},
  1022  
  1023  		// List of primitives, set list
  1024  		{
  1025  			Schema: map[string]*Schema{
  1026  				"ports": &Schema{
  1027  					Type:     TypeList,
  1028  					Computed: true,
  1029  					Elem:     &Schema{Type: TypeInt},
  1030  				},
  1031  			},
  1032  
  1033  			State: nil,
  1034  
  1035  			Diff: nil,
  1036  
  1037  			Key:   "ports",
  1038  			Value: []int{1, 2, 5},
  1039  
  1040  			GetKey:   "ports",
  1041  			GetValue: []interface{}{1, 2, 5},
  1042  		},
  1043  
  1044  		// List of primitives, set list with error
  1045  		{
  1046  			Schema: map[string]*Schema{
  1047  				"ports": &Schema{
  1048  					Type:     TypeList,
  1049  					Computed: true,
  1050  					Elem:     &Schema{Type: TypeInt},
  1051  				},
  1052  			},
  1053  
  1054  			State: nil,
  1055  
  1056  			Diff: nil,
  1057  
  1058  			Key:   "ports",
  1059  			Value: []interface{}{1, "NOPE", 5},
  1060  			Err:   true,
  1061  
  1062  			GetKey:   "ports",
  1063  			GetValue: []interface{}{},
  1064  		},
  1065  
  1066  		// List of resource, set element
  1067  		{
  1068  			Schema: map[string]*Schema{
  1069  				"ingress": &Schema{
  1070  					Type:     TypeList,
  1071  					Computed: true,
  1072  					Elem: &Resource{
  1073  						Schema: map[string]*Schema{
  1074  							"from": &Schema{
  1075  								Type: TypeInt,
  1076  							},
  1077  						},
  1078  					},
  1079  				},
  1080  			},
  1081  
  1082  			State: &terraform.InstanceState{
  1083  				Attributes: map[string]string{
  1084  					"ingress.#":      "2",
  1085  					"ingress.0.from": "80",
  1086  					"ingress.1.from": "8080",
  1087  				},
  1088  			},
  1089  
  1090  			Diff: nil,
  1091  
  1092  			Key:   "ingress.1.from",
  1093  			Value: 9000,
  1094  
  1095  			GetKey: "ingress",
  1096  			GetValue: []interface{}{
  1097  				map[string]interface{}{
  1098  					"from": 80,
  1099  				},
  1100  				map[string]interface{}{
  1101  					"from": 9000,
  1102  				},
  1103  			},
  1104  		},
  1105  
  1106  		// List of resource, set full resource element
  1107  		{
  1108  			Schema: map[string]*Schema{
  1109  				"ingress": &Schema{
  1110  					Type:     TypeList,
  1111  					Computed: true,
  1112  					Elem: &Resource{
  1113  						Schema: map[string]*Schema{
  1114  							"from": &Schema{
  1115  								Type: TypeInt,
  1116  							},
  1117  						},
  1118  					},
  1119  				},
  1120  			},
  1121  
  1122  			State: &terraform.InstanceState{
  1123  				Attributes: map[string]string{
  1124  					"ingress.#":      "2",
  1125  					"ingress.0.from": "80",
  1126  					"ingress.1.from": "8080",
  1127  				},
  1128  			},
  1129  
  1130  			Diff: nil,
  1131  
  1132  			Key: "ingress.1",
  1133  			Value: map[string]interface{}{
  1134  				"from": 9000,
  1135  			},
  1136  
  1137  			GetKey: "ingress",
  1138  			GetValue: []interface{}{
  1139  				map[string]interface{}{
  1140  					"from": 80,
  1141  				},
  1142  				map[string]interface{}{
  1143  					"from": 9000,
  1144  				},
  1145  			},
  1146  		},
  1147  
  1148  		// List of resource, set full resource element, with error
  1149  		{
  1150  			Schema: map[string]*Schema{
  1151  				"ingress": &Schema{
  1152  					Type:     TypeList,
  1153  					Computed: true,
  1154  					Elem: &Resource{
  1155  						Schema: map[string]*Schema{
  1156  							"from": &Schema{
  1157  								Type: TypeInt,
  1158  							},
  1159  							"to": &Schema{
  1160  								Type: TypeInt,
  1161  							},
  1162  						},
  1163  					},
  1164  				},
  1165  			},
  1166  
  1167  			State: &terraform.InstanceState{
  1168  				Attributes: map[string]string{
  1169  					"ingress.#":      "2",
  1170  					"ingress.0.from": "80",
  1171  					"ingress.0.to":   "10",
  1172  					"ingress.1.from": "8080",
  1173  					"ingress.1.to":   "8080",
  1174  				},
  1175  			},
  1176  
  1177  			Diff: nil,
  1178  
  1179  			Key: "ingress.1",
  1180  			Value: map[string]interface{}{
  1181  				"from": 9000,
  1182  				"to":   "bar",
  1183  			},
  1184  			Err: true,
  1185  
  1186  			GetKey: "ingress",
  1187  			GetValue: []interface{}{
  1188  				map[string]interface{}{
  1189  					"from": 80,
  1190  					"to":   10,
  1191  				},
  1192  				map[string]interface{}{
  1193  					"from": 8080,
  1194  					"to":   8080,
  1195  				},
  1196  			},
  1197  		},
  1198  
  1199  		// Set a list of maps
  1200  		{
  1201  			Schema: map[string]*Schema{
  1202  				"config_vars": &Schema{
  1203  					Type:     TypeList,
  1204  					Optional: true,
  1205  					Computed: true,
  1206  					Elem: &Schema{
  1207  						Type: TypeMap,
  1208  					},
  1209  				},
  1210  			},
  1211  
  1212  			State: nil,
  1213  
  1214  			Diff: nil,
  1215  
  1216  			Key: "config_vars",
  1217  			Value: []interface{}{
  1218  				map[string]interface{}{
  1219  					"foo": "bar",
  1220  				},
  1221  				map[string]interface{}{
  1222  					"bar": "baz",
  1223  				},
  1224  			},
  1225  			Err: false,
  1226  
  1227  			GetKey: "config_vars",
  1228  			GetValue: []interface{}{
  1229  				map[string]interface{}{
  1230  					"foo": "bar",
  1231  				},
  1232  				map[string]interface{}{
  1233  					"bar": "baz",
  1234  				},
  1235  			},
  1236  		},
  1237  
  1238  		// Set a list of maps
  1239  		{
  1240  			Schema: map[string]*Schema{
  1241  				"config_vars": &Schema{
  1242  					Type:     TypeList,
  1243  					Optional: true,
  1244  					Computed: true,
  1245  					Elem: &Schema{
  1246  						Type: TypeMap,
  1247  					},
  1248  				},
  1249  			},
  1250  
  1251  			State: nil,
  1252  
  1253  			Diff: nil,
  1254  
  1255  			Key: "config_vars",
  1256  			Value: []interface{}{
  1257  				map[string]string{
  1258  					"foo": "bar",
  1259  				},
  1260  				map[string]string{
  1261  					"bar": "baz",
  1262  				},
  1263  			},
  1264  			Err: false,
  1265  
  1266  			GetKey: "config_vars",
  1267  			GetValue: []interface{}{
  1268  				map[string]interface{}{
  1269  					"foo": "bar",
  1270  				},
  1271  				map[string]interface{}{
  1272  					"bar": "baz",
  1273  				},
  1274  			},
  1275  		},
  1276  
  1277  		// Set, with list
  1278  		{
  1279  			Schema: map[string]*Schema{
  1280  				"ports": &Schema{
  1281  					Type:     TypeSet,
  1282  					Optional: true,
  1283  					Computed: true,
  1284  					Elem:     &Schema{Type: TypeInt},
  1285  					Set: func(a interface{}) int {
  1286  						return a.(int)
  1287  					},
  1288  				},
  1289  			},
  1290  
  1291  			State: &terraform.InstanceState{
  1292  				Attributes: map[string]string{
  1293  					"ports.#": "3",
  1294  					"ports.0": "100",
  1295  					"ports.1": "80",
  1296  					"ports.2": "80",
  1297  				},
  1298  			},
  1299  
  1300  			Key:   "ports",
  1301  			Value: []interface{}{100, 125, 125},
  1302  
  1303  			GetKey:   "ports",
  1304  			GetValue: []interface{}{100, 125},
  1305  		},
  1306  
  1307  		// Set, with Set
  1308  		{
  1309  			Schema: map[string]*Schema{
  1310  				"ports": &Schema{
  1311  					Type:     TypeSet,
  1312  					Optional: true,
  1313  					Computed: true,
  1314  					Elem:     &Schema{Type: TypeInt},
  1315  					Set: func(a interface{}) int {
  1316  						return a.(int)
  1317  					},
  1318  				},
  1319  			},
  1320  
  1321  			State: &terraform.InstanceState{
  1322  				Attributes: map[string]string{
  1323  					"ports.#": "3",
  1324  					"ports.0": "100",
  1325  					"ports.1": "80",
  1326  					"ports.2": "80",
  1327  				},
  1328  			},
  1329  
  1330  			Key: "ports",
  1331  			Value: &Set{
  1332  				m: map[int]interface{}{
  1333  					1: 1,
  1334  					2: 2,
  1335  				},
  1336  			},
  1337  
  1338  			GetKey:   "ports",
  1339  			GetValue: []interface{}{1, 2},
  1340  		},
  1341  
  1342  		// Set single item
  1343  		{
  1344  			Schema: map[string]*Schema{
  1345  				"ports": &Schema{
  1346  					Type:     TypeSet,
  1347  					Optional: true,
  1348  					Computed: true,
  1349  					Elem:     &Schema{Type: TypeInt},
  1350  					Set: func(a interface{}) int {
  1351  						return a.(int)
  1352  					},
  1353  				},
  1354  			},
  1355  
  1356  			State: &terraform.InstanceState{
  1357  				Attributes: map[string]string{
  1358  					"ports.#": "2",
  1359  					"ports.0": "100",
  1360  					"ports.1": "80",
  1361  				},
  1362  			},
  1363  
  1364  			Key:   "ports.0",
  1365  			Value: 256,
  1366  			Err:   true,
  1367  
  1368  			GetKey:   "ports",
  1369  			GetValue: []interface{}{80, 100},
  1370  		},
  1371  	}
  1372  
  1373  	for i, tc := range cases {
  1374  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
  1375  		if err != nil {
  1376  			t.Fatalf("err: %s", err)
  1377  		}
  1378  
  1379  		err = d.Set(tc.Key, tc.Value)
  1380  		if (err != nil) != tc.Err {
  1381  			t.Fatalf("%d err: %s", i, err)
  1382  		}
  1383  
  1384  		v := d.Get(tc.GetKey)
  1385  		if s, ok := v.(*Set); ok {
  1386  			v = s.List()
  1387  		}
  1388  		if !reflect.DeepEqual(v, tc.GetValue) {
  1389  			t.Fatalf("Get Bad: %d\n\n%#v", i, v)
  1390  		}
  1391  	}
  1392  }
  1393  
  1394  func TestResourceDataState(t *testing.T) {
  1395  	cases := []struct {
  1396  		Schema  map[string]*Schema
  1397  		State   *terraform.InstanceState
  1398  		Diff    *terraform.InstanceDiff
  1399  		Set     map[string]interface{}
  1400  		Result  *terraform.InstanceState
  1401  		Partial []string
  1402  	}{
  1403  		// Basic primitive in diff
  1404  		{
  1405  			Schema: map[string]*Schema{
  1406  				"availability_zone": &Schema{
  1407  					Type:     TypeString,
  1408  					Optional: true,
  1409  					Computed: true,
  1410  					ForceNew: true,
  1411  				},
  1412  			},
  1413  
  1414  			State: nil,
  1415  
  1416  			Diff: &terraform.InstanceDiff{
  1417  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1418  					"availability_zone": &terraform.ResourceAttrDiff{
  1419  						Old:         "",
  1420  						New:         "foo",
  1421  						RequiresNew: true,
  1422  					},
  1423  				},
  1424  			},
  1425  
  1426  			Result: &terraform.InstanceState{
  1427  				Attributes: map[string]string{
  1428  					"availability_zone": "foo",
  1429  				},
  1430  			},
  1431  		},
  1432  
  1433  		// Basic primitive set override
  1434  		{
  1435  			Schema: map[string]*Schema{
  1436  				"availability_zone": &Schema{
  1437  					Type:     TypeString,
  1438  					Optional: true,
  1439  					Computed: true,
  1440  					ForceNew: true,
  1441  				},
  1442  			},
  1443  
  1444  			State: nil,
  1445  
  1446  			Diff: &terraform.InstanceDiff{
  1447  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1448  					"availability_zone": &terraform.ResourceAttrDiff{
  1449  						Old:         "",
  1450  						New:         "foo",
  1451  						RequiresNew: true,
  1452  					},
  1453  				},
  1454  			},
  1455  
  1456  			Set: map[string]interface{}{
  1457  				"availability_zone": "bar",
  1458  			},
  1459  
  1460  			Result: &terraform.InstanceState{
  1461  				Attributes: map[string]string{
  1462  					"availability_zone": "bar",
  1463  				},
  1464  			},
  1465  		},
  1466  
  1467  		{
  1468  			Schema: map[string]*Schema{
  1469  				"vpc": &Schema{
  1470  					Type:     TypeBool,
  1471  					Optional: true,
  1472  				},
  1473  			},
  1474  
  1475  			State: nil,
  1476  
  1477  			Diff: nil,
  1478  
  1479  			Set: map[string]interface{}{
  1480  				"vpc": true,
  1481  			},
  1482  
  1483  			Result: &terraform.InstanceState{
  1484  				Attributes: map[string]string{
  1485  					"vpc": "true",
  1486  				},
  1487  			},
  1488  		},
  1489  
  1490  		// Basic primitive with StateFunc set
  1491  		{
  1492  			Schema: map[string]*Schema{
  1493  				"availability_zone": &Schema{
  1494  					Type:      TypeString,
  1495  					Optional:  true,
  1496  					Computed:  true,
  1497  					StateFunc: func(interface{}) string { return "" },
  1498  				},
  1499  			},
  1500  
  1501  			State: nil,
  1502  
  1503  			Diff: &terraform.InstanceDiff{
  1504  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1505  					"availability_zone": &terraform.ResourceAttrDiff{
  1506  						Old:      "",
  1507  						New:      "foo",
  1508  						NewExtra: "foo!",
  1509  					},
  1510  				},
  1511  			},
  1512  
  1513  			Result: &terraform.InstanceState{
  1514  				Attributes: map[string]string{
  1515  					"availability_zone": "foo",
  1516  				},
  1517  			},
  1518  		},
  1519  
  1520  		// List
  1521  		{
  1522  			Schema: map[string]*Schema{
  1523  				"ports": &Schema{
  1524  					Type:     TypeList,
  1525  					Required: true,
  1526  					Elem:     &Schema{Type: TypeInt},
  1527  				},
  1528  			},
  1529  
  1530  			State: &terraform.InstanceState{
  1531  				Attributes: map[string]string{
  1532  					"ports.#": "1",
  1533  					"ports.0": "80",
  1534  				},
  1535  			},
  1536  
  1537  			Diff: &terraform.InstanceDiff{
  1538  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1539  					"ports.#": &terraform.ResourceAttrDiff{
  1540  						Old: "1",
  1541  						New: "2",
  1542  					},
  1543  					"ports.1": &terraform.ResourceAttrDiff{
  1544  						Old: "",
  1545  						New: "100",
  1546  					},
  1547  				},
  1548  			},
  1549  
  1550  			Result: &terraform.InstanceState{
  1551  				Attributes: map[string]string{
  1552  					"ports.#": "2",
  1553  					"ports.0": "80",
  1554  					"ports.1": "100",
  1555  				},
  1556  			},
  1557  		},
  1558  
  1559  		// List of resources
  1560  		{
  1561  			Schema: map[string]*Schema{
  1562  				"ingress": &Schema{
  1563  					Type:     TypeList,
  1564  					Required: true,
  1565  					Elem: &Resource{
  1566  						Schema: map[string]*Schema{
  1567  							"from": &Schema{
  1568  								Type:     TypeInt,
  1569  								Required: true,
  1570  							},
  1571  						},
  1572  					},
  1573  				},
  1574  			},
  1575  
  1576  			State: &terraform.InstanceState{
  1577  				Attributes: map[string]string{
  1578  					"ingress.#":      "1",
  1579  					"ingress.0.from": "80",
  1580  				},
  1581  			},
  1582  
  1583  			Diff: &terraform.InstanceDiff{
  1584  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1585  					"ingress.#": &terraform.ResourceAttrDiff{
  1586  						Old: "1",
  1587  						New: "2",
  1588  					},
  1589  					"ingress.0.from": &terraform.ResourceAttrDiff{
  1590  						Old: "80",
  1591  						New: "150",
  1592  					},
  1593  					"ingress.1.from": &terraform.ResourceAttrDiff{
  1594  						Old: "",
  1595  						New: "100",
  1596  					},
  1597  				},
  1598  			},
  1599  
  1600  			Result: &terraform.InstanceState{
  1601  				Attributes: map[string]string{
  1602  					"ingress.#":      "2",
  1603  					"ingress.0.from": "150",
  1604  					"ingress.1.from": "100",
  1605  				},
  1606  			},
  1607  		},
  1608  
  1609  		// List of maps
  1610  		{
  1611  			Schema: map[string]*Schema{
  1612  				"config_vars": &Schema{
  1613  					Type:     TypeList,
  1614  					Optional: true,
  1615  					Computed: true,
  1616  					Elem: &Schema{
  1617  						Type: TypeMap,
  1618  					},
  1619  				},
  1620  			},
  1621  
  1622  			State: &terraform.InstanceState{
  1623  				Attributes: map[string]string{
  1624  					"config_vars.#":     "2",
  1625  					"config_vars.0.foo": "bar",
  1626  					"config_vars.0.bar": "bar",
  1627  					"config_vars.1.bar": "baz",
  1628  				},
  1629  			},
  1630  
  1631  			Diff: &terraform.InstanceDiff{
  1632  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1633  					"config_vars.0.bar": &terraform.ResourceAttrDiff{
  1634  						NewRemoved: true,
  1635  					},
  1636  				},
  1637  			},
  1638  
  1639  			Set: map[string]interface{}{
  1640  				"config_vars.1": map[string]interface{}{
  1641  					"baz": "bang",
  1642  				},
  1643  			},
  1644  
  1645  			Result: &terraform.InstanceState{
  1646  				Attributes: map[string]string{
  1647  					"config_vars.#":     "2",
  1648  					"config_vars.0.foo": "bar",
  1649  					"config_vars.1.baz": "bang",
  1650  				},
  1651  			},
  1652  		},
  1653  
  1654  		// List of maps with removal in diff
  1655  		{
  1656  			Schema: map[string]*Schema{
  1657  				"config_vars": &Schema{
  1658  					Type:     TypeList,
  1659  					Optional: true,
  1660  					Computed: true,
  1661  					Elem: &Schema{
  1662  						Type: TypeMap,
  1663  					},
  1664  				},
  1665  			},
  1666  
  1667  			State: &terraform.InstanceState{
  1668  				Attributes: map[string]string{
  1669  					"config_vars.#":     "1",
  1670  					"config_vars.0.FOO": "bar",
  1671  				},
  1672  			},
  1673  
  1674  			Diff: &terraform.InstanceDiff{
  1675  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1676  					"config_vars.#": &terraform.ResourceAttrDiff{
  1677  						Old: "1",
  1678  						New: "0",
  1679  					},
  1680  					"config_vars.0.FOO": &terraform.ResourceAttrDiff{
  1681  						Old:        "bar",
  1682  						NewRemoved: true,
  1683  					},
  1684  				},
  1685  			},
  1686  
  1687  			Result: &terraform.InstanceState{
  1688  				Attributes: map[string]string{
  1689  					"config_vars.#": "0",
  1690  				},
  1691  			},
  1692  		},
  1693  
  1694  		// Basic state with other keys
  1695  		{
  1696  			Schema: map[string]*Schema{
  1697  				"availability_zone": &Schema{
  1698  					Type:     TypeString,
  1699  					Optional: true,
  1700  					Computed: true,
  1701  					ForceNew: true,
  1702  				},
  1703  			},
  1704  
  1705  			State: &terraform.InstanceState{
  1706  				ID: "bar",
  1707  				Attributes: map[string]string{
  1708  					"id": "bar",
  1709  				},
  1710  			},
  1711  
  1712  			Diff: &terraform.InstanceDiff{
  1713  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1714  					"availability_zone": &terraform.ResourceAttrDiff{
  1715  						Old:         "",
  1716  						New:         "foo",
  1717  						RequiresNew: true,
  1718  					},
  1719  				},
  1720  			},
  1721  
  1722  			Result: &terraform.InstanceState{
  1723  				ID: "bar",
  1724  				Attributes: map[string]string{
  1725  					"id":                "bar",
  1726  					"availability_zone": "foo",
  1727  				},
  1728  			},
  1729  		},
  1730  
  1731  		// Sets
  1732  		{
  1733  			Schema: map[string]*Schema{
  1734  				"ports": &Schema{
  1735  					Type:     TypeSet,
  1736  					Optional: true,
  1737  					Computed: true,
  1738  					Elem:     &Schema{Type: TypeInt},
  1739  					Set: func(a interface{}) int {
  1740  						return a.(int)
  1741  					},
  1742  				},
  1743  			},
  1744  
  1745  			State: &terraform.InstanceState{
  1746  				Attributes: map[string]string{
  1747  					"ports.#": "3",
  1748  					"ports.0": "100",
  1749  					"ports.1": "80",
  1750  					"ports.2": "80",
  1751  				},
  1752  			},
  1753  
  1754  			Diff: nil,
  1755  
  1756  			Result: &terraform.InstanceState{
  1757  				Attributes: map[string]string{
  1758  					"ports.#": "2",
  1759  					"ports.0": "80",
  1760  					"ports.1": "100",
  1761  				},
  1762  			},
  1763  		},
  1764  
  1765  		{
  1766  			Schema: map[string]*Schema{
  1767  				"ports": &Schema{
  1768  					Type:     TypeSet,
  1769  					Optional: true,
  1770  					Computed: true,
  1771  					Elem:     &Schema{Type: TypeInt},
  1772  					Set: func(a interface{}) int {
  1773  						return a.(int)
  1774  					},
  1775  				},
  1776  			},
  1777  
  1778  			State: nil,
  1779  
  1780  			Diff: nil,
  1781  
  1782  			Set: map[string]interface{}{
  1783  				"ports": []interface{}{100, 80},
  1784  			},
  1785  
  1786  			Result: &terraform.InstanceState{
  1787  				Attributes: map[string]string{
  1788  					"ports.#": "2",
  1789  					"ports.0": "80",
  1790  					"ports.1": "100",
  1791  				},
  1792  			},
  1793  		},
  1794  
  1795  		{
  1796  			Schema: map[string]*Schema{
  1797  				"ports": &Schema{
  1798  					Type:     TypeSet,
  1799  					Optional: true,
  1800  					Computed: true,
  1801  					Elem: &Resource{
  1802  						Schema: map[string]*Schema{
  1803  							"order": &Schema{
  1804  								Type: TypeInt,
  1805  							},
  1806  
  1807  							"a": &Schema{
  1808  								Type: TypeList,
  1809  								Elem: &Schema{Type: TypeInt},
  1810  							},
  1811  
  1812  							"b": &Schema{
  1813  								Type: TypeList,
  1814  								Elem: &Schema{Type: TypeInt},
  1815  							},
  1816  						},
  1817  					},
  1818  					Set: func(a interface{}) int {
  1819  						m := a.(map[string]interface{})
  1820  						return m["order"].(int)
  1821  					},
  1822  				},
  1823  			},
  1824  
  1825  			State: &terraform.InstanceState{
  1826  				Attributes: map[string]string{
  1827  					"ports.#":       "2",
  1828  					"ports.0.order": "10",
  1829  					"ports.0.a.#":   "1",
  1830  					"ports.0.a.0":   "80",
  1831  					"ports.1.order": "20",
  1832  					"ports.1.b.#":   "1",
  1833  					"ports.1.b.0":   "100",
  1834  				},
  1835  			},
  1836  
  1837  			Set: map[string]interface{}{
  1838  				"ports": []interface{}{
  1839  					map[string]interface{}{
  1840  						"order": 20,
  1841  						"b":     []interface{}{100},
  1842  					},
  1843  					map[string]interface{}{
  1844  						"order": 10,
  1845  						"a":     []interface{}{80},
  1846  					},
  1847  				},
  1848  			},
  1849  
  1850  			Result: &terraform.InstanceState{
  1851  				Attributes: map[string]string{
  1852  					"ports.#":       "2",
  1853  					"ports.0.order": "10",
  1854  					"ports.0.a.#":   "1",
  1855  					"ports.0.a.0":   "80",
  1856  					"ports.1.order": "20",
  1857  					"ports.1.b.#":   "1",
  1858  					"ports.1.b.0":   "100",
  1859  				},
  1860  			},
  1861  		},
  1862  
  1863  		/*
  1864  		 * PARTIAL STATES
  1865  		 */
  1866  
  1867  		// Basic primitive
  1868  		{
  1869  			Schema: map[string]*Schema{
  1870  				"availability_zone": &Schema{
  1871  					Type:     TypeString,
  1872  					Optional: true,
  1873  					Computed: true,
  1874  					ForceNew: true,
  1875  				},
  1876  			},
  1877  
  1878  			State: nil,
  1879  
  1880  			Diff: &terraform.InstanceDiff{
  1881  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1882  					"availability_zone": &terraform.ResourceAttrDiff{
  1883  						Old:         "",
  1884  						New:         "foo",
  1885  						RequiresNew: true,
  1886  					},
  1887  				},
  1888  			},
  1889  
  1890  			Partial: []string{},
  1891  
  1892  			Result: &terraform.InstanceState{
  1893  				Attributes: map[string]string{},
  1894  			},
  1895  		},
  1896  
  1897  		// List
  1898  		{
  1899  			Schema: map[string]*Schema{
  1900  				"ports": &Schema{
  1901  					Type:     TypeList,
  1902  					Required: true,
  1903  					Elem:     &Schema{Type: TypeInt},
  1904  				},
  1905  			},
  1906  
  1907  			State: &terraform.InstanceState{
  1908  				Attributes: map[string]string{
  1909  					"ports.#": "1",
  1910  					"ports.0": "80",
  1911  				},
  1912  			},
  1913  
  1914  			Diff: &terraform.InstanceDiff{
  1915  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1916  					"ports.#": &terraform.ResourceAttrDiff{
  1917  						Old: "1",
  1918  						New: "2",
  1919  					},
  1920  					"ports.1": &terraform.ResourceAttrDiff{
  1921  						Old: "",
  1922  						New: "100",
  1923  					},
  1924  				},
  1925  			},
  1926  
  1927  			Partial: []string{},
  1928  
  1929  			Result: &terraform.InstanceState{
  1930  				Attributes: map[string]string{
  1931  					"ports.#": "1",
  1932  					"ports.0": "80",
  1933  				},
  1934  			},
  1935  		},
  1936  
  1937  		{
  1938  			Schema: map[string]*Schema{
  1939  				"ports": &Schema{
  1940  					Type:     TypeList,
  1941  					Optional: true,
  1942  					Computed: true,
  1943  					Elem:     &Schema{Type: TypeInt},
  1944  				},
  1945  			},
  1946  
  1947  			State: nil,
  1948  
  1949  			Diff: &terraform.InstanceDiff{
  1950  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1951  					"ports.#": &terraform.ResourceAttrDiff{
  1952  						Old:         "",
  1953  						NewComputed: true,
  1954  					},
  1955  				},
  1956  			},
  1957  
  1958  			Partial: []string{},
  1959  
  1960  			Set: map[string]interface{}{
  1961  				"ports": []interface{}{},
  1962  			},
  1963  
  1964  			Result: &terraform.InstanceState{
  1965  				Attributes: map[string]string{
  1966  					"ports.#": "0",
  1967  				},
  1968  			},
  1969  		},
  1970  
  1971  		// List of resources
  1972  		{
  1973  			Schema: map[string]*Schema{
  1974  				"ingress": &Schema{
  1975  					Type:     TypeList,
  1976  					Required: true,
  1977  					Elem: &Resource{
  1978  						Schema: map[string]*Schema{
  1979  							"from": &Schema{
  1980  								Type:     TypeInt,
  1981  								Required: true,
  1982  							},
  1983  						},
  1984  					},
  1985  				},
  1986  			},
  1987  
  1988  			State: &terraform.InstanceState{
  1989  				Attributes: map[string]string{
  1990  					"ingress.#":      "1",
  1991  					"ingress.0.from": "80",
  1992  				},
  1993  			},
  1994  
  1995  			Diff: &terraform.InstanceDiff{
  1996  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1997  					"ingress.#": &terraform.ResourceAttrDiff{
  1998  						Old: "1",
  1999  						New: "2",
  2000  					},
  2001  					"ingress.0.from": &terraform.ResourceAttrDiff{
  2002  						Old: "80",
  2003  						New: "150",
  2004  					},
  2005  					"ingress.1.from": &terraform.ResourceAttrDiff{
  2006  						Old: "",
  2007  						New: "100",
  2008  					},
  2009  				},
  2010  			},
  2011  
  2012  			Partial: []string{},
  2013  
  2014  			Result: &terraform.InstanceState{
  2015  				Attributes: map[string]string{
  2016  					"ingress.#":      "1",
  2017  					"ingress.0.from": "80",
  2018  				},
  2019  			},
  2020  		},
  2021  
  2022  		// List of maps
  2023  		{
  2024  			Schema: map[string]*Schema{
  2025  				"config_vars": &Schema{
  2026  					Type:     TypeList,
  2027  					Optional: true,
  2028  					Computed: true,
  2029  					Elem: &Schema{
  2030  						Type: TypeMap,
  2031  					},
  2032  				},
  2033  			},
  2034  
  2035  			State: &terraform.InstanceState{
  2036  				Attributes: map[string]string{
  2037  					"config_vars.#":     "2",
  2038  					"config_vars.0.foo": "bar",
  2039  					"config_vars.0.bar": "bar",
  2040  					"config_vars.1.bar": "baz",
  2041  				},
  2042  			},
  2043  
  2044  			Diff: &terraform.InstanceDiff{
  2045  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2046  					"config_vars.0.bar": &terraform.ResourceAttrDiff{
  2047  						NewRemoved: true,
  2048  					},
  2049  				},
  2050  			},
  2051  
  2052  			Set: map[string]interface{}{
  2053  				"config_vars.1": map[string]interface{}{
  2054  					"baz": "bang",
  2055  				},
  2056  			},
  2057  
  2058  			Partial: []string{},
  2059  
  2060  			Result: &terraform.InstanceState{
  2061  				Attributes: map[string]string{
  2062  					"config_vars.#":     "2",
  2063  					"config_vars.0.foo": "bar",
  2064  					"config_vars.0.bar": "bar",
  2065  					"config_vars.1.bar": "baz",
  2066  				},
  2067  			},
  2068  		},
  2069  
  2070  		// Sets
  2071  		{
  2072  			Schema: map[string]*Schema{
  2073  				"ports": &Schema{
  2074  					Type:     TypeSet,
  2075  					Optional: true,
  2076  					Computed: true,
  2077  					Elem:     &Schema{Type: TypeInt},
  2078  					Set: func(a interface{}) int {
  2079  						return a.(int)
  2080  					},
  2081  				},
  2082  			},
  2083  
  2084  			State: &terraform.InstanceState{
  2085  				Attributes: map[string]string{
  2086  					"ports.#": "3",
  2087  					"ports.0": "100",
  2088  					"ports.1": "80",
  2089  					"ports.2": "80",
  2090  				},
  2091  			},
  2092  
  2093  			Diff: &terraform.InstanceDiff{
  2094  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2095  					"ports.1": &terraform.ResourceAttrDiff{
  2096  						New: "120",
  2097  					},
  2098  				},
  2099  			},
  2100  
  2101  			Partial: []string{},
  2102  
  2103  			Result: &terraform.InstanceState{
  2104  				Attributes: map[string]string{
  2105  					"ports.#": "2",
  2106  					"ports.0": "80",
  2107  					"ports.1": "100",
  2108  				},
  2109  			},
  2110  		},
  2111  
  2112  		{
  2113  			Schema: map[string]*Schema{
  2114  				"ports": &Schema{
  2115  					Type:     TypeSet,
  2116  					Optional: true,
  2117  					Computed: true,
  2118  					Elem:     &Schema{Type: TypeInt},
  2119  					Set: func(a interface{}) int {
  2120  						return a.(int)
  2121  					},
  2122  				},
  2123  			},
  2124  
  2125  			State: nil,
  2126  
  2127  			Diff: &terraform.InstanceDiff{
  2128  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2129  					"ports.#": &terraform.ResourceAttrDiff{
  2130  						Old:         "",
  2131  						NewComputed: true,
  2132  					},
  2133  				},
  2134  			},
  2135  
  2136  			Partial: []string{},
  2137  
  2138  			Result: &terraform.InstanceState{
  2139  				Attributes: map[string]string{
  2140  					"ports.#": "0",
  2141  				},
  2142  			},
  2143  		},
  2144  	}
  2145  
  2146  	for i, tc := range cases {
  2147  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
  2148  		if err != nil {
  2149  			t.Fatalf("err: %s", err)
  2150  		}
  2151  
  2152  		for k, v := range tc.Set {
  2153  			if err := d.Set(k, v); err != nil {
  2154  				t.Fatalf("%d err: %s", i, err)
  2155  			}
  2156  		}
  2157  
  2158  		// Set an ID so that the state returned is not nil
  2159  		idSet := false
  2160  		if d.Id() == "" {
  2161  			idSet = true
  2162  			d.SetId("foo")
  2163  		}
  2164  
  2165  		// If we have partial, then enable partial state mode.
  2166  		if tc.Partial != nil {
  2167  			d.Partial(true)
  2168  			for _, k := range tc.Partial {
  2169  				d.SetPartial(k)
  2170  			}
  2171  		}
  2172  
  2173  		actual := d.State()
  2174  
  2175  		// If we set an ID, then undo what we did so the comparison works
  2176  		if actual != nil && idSet {
  2177  			actual.ID = ""
  2178  			delete(actual.Attributes, "id")
  2179  		}
  2180  
  2181  		if !reflect.DeepEqual(actual, tc.Result) {
  2182  			t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result)
  2183  		}
  2184  	}
  2185  }
  2186  
  2187  func TestResourceDataSetConnInfo(t *testing.T) {
  2188  	d := &ResourceData{}
  2189  	d.SetId("foo")
  2190  	d.SetConnInfo(map[string]string{
  2191  		"foo": "bar",
  2192  	})
  2193  
  2194  	expected := map[string]string{
  2195  		"foo": "bar",
  2196  	}
  2197  
  2198  	actual := d.State()
  2199  	if !reflect.DeepEqual(actual.Ephemeral.ConnInfo, expected) {
  2200  		t.Fatalf("bad: %#v", actual)
  2201  	}
  2202  }
  2203  
  2204  func TestResourceDataSetId(t *testing.T) {
  2205  	d := &ResourceData{}
  2206  	d.SetId("foo")
  2207  
  2208  	actual := d.State()
  2209  	if actual.ID != "foo" {
  2210  		t.Fatalf("bad: %#v", actual)
  2211  	}
  2212  }
  2213  
  2214  func TestResourceDataSetId_clear(t *testing.T) {
  2215  	d := &ResourceData{
  2216  		state: &terraform.InstanceState{ID: "bar"},
  2217  	}
  2218  	d.SetId("")
  2219  
  2220  	actual := d.State()
  2221  	if actual != nil {
  2222  		t.Fatalf("bad: %#v", actual)
  2223  	}
  2224  }
  2225  
  2226  func TestResourceDataSetId_override(t *testing.T) {
  2227  	d := &ResourceData{
  2228  		state: &terraform.InstanceState{ID: "bar"},
  2229  	}
  2230  	d.SetId("foo")
  2231  
  2232  	actual := d.State()
  2233  	if actual.ID != "foo" {
  2234  		t.Fatalf("bad: %#v", actual)
  2235  	}
  2236  }