github.com/hashicorp/terraform-plugin-sdk@v1.17.2/helper/schema/field_writer_map_test.go (about)

     1  package schema
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  )
     7  
     8  func TestMapFieldWriter_impl(t *testing.T) {
     9  	var _ FieldWriter = new(MapFieldWriter)
    10  }
    11  
    12  func TestMapFieldWriter(t *testing.T) {
    13  	schema := map[string]*Schema{
    14  		"bool":   {Type: TypeBool},
    15  		"int":    {Type: TypeInt},
    16  		"string": {Type: TypeString},
    17  		"list": {
    18  			Type: TypeList,
    19  			Elem: &Schema{Type: TypeString},
    20  		},
    21  		"listInt": {
    22  			Type: TypeList,
    23  			Elem: &Schema{Type: TypeInt},
    24  		},
    25  		"listResource": {
    26  			Type:     TypeList,
    27  			Optional: true,
    28  			Computed: true,
    29  			Elem: &Resource{
    30  				Schema: map[string]*Schema{
    31  					"value": {
    32  						Type:     TypeInt,
    33  						Optional: true,
    34  					},
    35  				},
    36  			},
    37  		},
    38  		"map": {Type: TypeMap},
    39  		"set": {
    40  			Type: TypeSet,
    41  			Elem: &Schema{Type: TypeInt},
    42  			Set: func(a interface{}) int {
    43  				return a.(int)
    44  			},
    45  		},
    46  		"setDeep": {
    47  			Type: TypeSet,
    48  			Elem: &Resource{
    49  				Schema: map[string]*Schema{
    50  					"index": {Type: TypeInt},
    51  					"value": {Type: TypeString},
    52  				},
    53  			},
    54  			Set: func(a interface{}) int {
    55  				return a.(map[string]interface{})["index"].(int)
    56  			},
    57  		},
    58  	}
    59  
    60  	cases := map[string]struct {
    61  		Addr  []string
    62  		Value interface{}
    63  		Err   bool
    64  		Out   map[string]string
    65  	}{
    66  		"noexist": {
    67  			[]string{"noexist"},
    68  			42,
    69  			true,
    70  			map[string]string{},
    71  		},
    72  
    73  		"bool": {
    74  			[]string{"bool"},
    75  			false,
    76  			false,
    77  			map[string]string{
    78  				"bool": "false",
    79  			},
    80  		},
    81  
    82  		"int": {
    83  			[]string{"int"},
    84  			42,
    85  			false,
    86  			map[string]string{
    87  				"int": "42",
    88  			},
    89  		},
    90  
    91  		"bigint": {
    92  			[]string{"int"},
    93  			7227701560655103598,
    94  			false,
    95  			map[string]string{
    96  				"int": "7227701560655103598",
    97  			},
    98  		},
    99  
   100  		"string": {
   101  			[]string{"string"},
   102  			"42",
   103  			false,
   104  			map[string]string{
   105  				"string": "42",
   106  			},
   107  		},
   108  
   109  		"string nil": {
   110  			[]string{"string"},
   111  			nil,
   112  			false,
   113  			map[string]string{
   114  				"string": "",
   115  			},
   116  		},
   117  
   118  		"list of resources": {
   119  			[]string{"listResource"},
   120  			[]interface{}{
   121  				map[string]interface{}{
   122  					"value": 80,
   123  				},
   124  			},
   125  			false,
   126  			map[string]string{
   127  				"listResource.#":       "1",
   128  				"listResource.0.value": "80",
   129  			},
   130  		},
   131  
   132  		"list of resources empty": {
   133  			[]string{"listResource"},
   134  			[]interface{}{},
   135  			false,
   136  			map[string]string{
   137  				"listResource.#": "0",
   138  			},
   139  		},
   140  
   141  		"list of resources nil": {
   142  			[]string{"listResource"},
   143  			nil,
   144  			false,
   145  			map[string]string{
   146  				"listResource.#": "0",
   147  			},
   148  		},
   149  
   150  		"list of strings": {
   151  			[]string{"list"},
   152  			[]interface{}{"foo", "bar"},
   153  			false,
   154  			map[string]string{
   155  				"list.#": "2",
   156  				"list.0": "foo",
   157  				"list.1": "bar",
   158  			},
   159  		},
   160  
   161  		"list element": {
   162  			[]string{"list", "0"},
   163  			"string",
   164  			true,
   165  			map[string]string{},
   166  		},
   167  
   168  		"map": {
   169  			[]string{"map"},
   170  			map[string]interface{}{"foo": "bar"},
   171  			false,
   172  			map[string]string{
   173  				"map.%":   "1",
   174  				"map.foo": "bar",
   175  			},
   176  		},
   177  
   178  		"map delete": {
   179  			[]string{"map"},
   180  			nil,
   181  			false,
   182  			map[string]string{
   183  				"map": "",
   184  			},
   185  		},
   186  
   187  		"map element": {
   188  			[]string{"map", "foo"},
   189  			"bar",
   190  			true,
   191  			map[string]string{},
   192  		},
   193  
   194  		"set": {
   195  			[]string{"set"},
   196  			[]interface{}{1, 2, 5},
   197  			false,
   198  			map[string]string{
   199  				"set.#": "3",
   200  				"set.1": "1",
   201  				"set.2": "2",
   202  				"set.5": "5",
   203  			},
   204  		},
   205  
   206  		"set nil": {
   207  			[]string{"set"},
   208  			nil,
   209  			false,
   210  			map[string]string{
   211  				"set.#": "0",
   212  			},
   213  		},
   214  
   215  		"set typed nil": {
   216  			[]string{"set"},
   217  			func() *Set { return nil }(),
   218  			false,
   219  			map[string]string{
   220  				"set.#": "0",
   221  			},
   222  		},
   223  
   224  		"set resource": {
   225  			[]string{"setDeep"},
   226  			[]interface{}{
   227  				map[string]interface{}{
   228  					"index": 10,
   229  					"value": "foo",
   230  				},
   231  				map[string]interface{}{
   232  					"index": 50,
   233  					"value": "bar",
   234  				},
   235  			},
   236  			false,
   237  			map[string]string{
   238  				"setDeep.#":        "2",
   239  				"setDeep.10.index": "10",
   240  				"setDeep.10.value": "foo",
   241  				"setDeep.50.index": "50",
   242  				"setDeep.50.value": "bar",
   243  			},
   244  		},
   245  
   246  		"set element": {
   247  			[]string{"set", "5"},
   248  			5,
   249  			true,
   250  			map[string]string{},
   251  		},
   252  
   253  		"full object": {
   254  			nil,
   255  			map[string]interface{}{
   256  				"string": "foo",
   257  				"list":   []interface{}{"foo", "bar"},
   258  			},
   259  			false,
   260  			map[string]string{
   261  				"string": "foo",
   262  				"list.#": "2",
   263  				"list.0": "foo",
   264  				"list.1": "bar",
   265  			},
   266  		},
   267  	}
   268  
   269  	for name, tc := range cases {
   270  		w := &MapFieldWriter{Schema: schema}
   271  		err := w.WriteField(tc.Addr, tc.Value)
   272  		if err != nil != tc.Err {
   273  			t.Fatalf("%s: err: %s", name, err)
   274  		}
   275  
   276  		actual := w.Map()
   277  		if !reflect.DeepEqual(actual, tc.Out) {
   278  			t.Fatalf("%s: bad: %#v", name, actual)
   279  		}
   280  	}
   281  }
   282  
   283  func TestMapFieldWriterCleanSet(t *testing.T) {
   284  	schema := map[string]*Schema{
   285  		"setDeep": {
   286  			Type: TypeSet,
   287  			Elem: &Resource{
   288  				Schema: map[string]*Schema{
   289  					"index": {Type: TypeInt},
   290  					"value": {Type: TypeString},
   291  				},
   292  			},
   293  			Set: func(a interface{}) int {
   294  				return a.(map[string]interface{})["index"].(int)
   295  			},
   296  		},
   297  	}
   298  
   299  	values := []struct {
   300  		Addr  []string
   301  		Value interface{}
   302  		Out   map[string]string
   303  	}{
   304  		{
   305  			[]string{"setDeep"},
   306  			[]interface{}{
   307  				map[string]interface{}{
   308  					"index": 10,
   309  					"value": "foo",
   310  				},
   311  				map[string]interface{}{
   312  					"index": 50,
   313  					"value": "bar",
   314  				},
   315  			},
   316  			map[string]string{
   317  				"setDeep.#":        "2",
   318  				"setDeep.10.index": "10",
   319  				"setDeep.10.value": "foo",
   320  				"setDeep.50.index": "50",
   321  				"setDeep.50.value": "bar",
   322  			},
   323  		},
   324  		{
   325  			[]string{"setDeep"},
   326  			[]interface{}{
   327  				map[string]interface{}{
   328  					"index": 20,
   329  					"value": "baz",
   330  				},
   331  				map[string]interface{}{
   332  					"index": 60,
   333  					"value": "qux",
   334  				},
   335  			},
   336  			map[string]string{
   337  				"setDeep.#":        "2",
   338  				"setDeep.20.index": "20",
   339  				"setDeep.20.value": "baz",
   340  				"setDeep.60.index": "60",
   341  				"setDeep.60.value": "qux",
   342  			},
   343  		},
   344  		{
   345  			[]string{"setDeep"},
   346  			[]interface{}{
   347  				map[string]interface{}{
   348  					"index": 30,
   349  					"value": "one",
   350  				},
   351  				map[string]interface{}{
   352  					"index": 70,
   353  					"value": "two",
   354  				},
   355  			},
   356  			map[string]string{
   357  				"setDeep.#":        "2",
   358  				"setDeep.30.index": "30",
   359  				"setDeep.30.value": "one",
   360  				"setDeep.70.index": "70",
   361  				"setDeep.70.value": "two",
   362  			},
   363  		},
   364  	}
   365  
   366  	w := &MapFieldWriter{Schema: schema}
   367  
   368  	for n, tc := range values {
   369  		err := w.WriteField(tc.Addr, tc.Value)
   370  		if err != nil {
   371  			t.Fatalf("%d: err: %s", n, err)
   372  		}
   373  
   374  		actual := w.Map()
   375  		if !reflect.DeepEqual(actual, tc.Out) {
   376  			t.Fatalf("%d: bad: %#v", n, actual)
   377  		}
   378  	}
   379  }
   380  
   381  func TestMapFieldWriterCleanList(t *testing.T) {
   382  	schema := map[string]*Schema{
   383  		"listDeep": {
   384  			Type: TypeList,
   385  			Elem: &Resource{
   386  				Schema: map[string]*Schema{
   387  					"thing1": {Type: TypeString},
   388  					"thing2": {Type: TypeString},
   389  				},
   390  			},
   391  		},
   392  	}
   393  
   394  	values := []struct {
   395  		Addr  []string
   396  		Value interface{}
   397  		Out   map[string]string
   398  	}{
   399  		{
   400  			// Base list
   401  			[]string{"listDeep"},
   402  			[]interface{}{
   403  				map[string]interface{}{
   404  					"thing1": "a",
   405  					"thing2": "b",
   406  				},
   407  				map[string]interface{}{
   408  					"thing1": "c",
   409  					"thing2": "d",
   410  				},
   411  				map[string]interface{}{
   412  					"thing1": "e",
   413  					"thing2": "f",
   414  				},
   415  				map[string]interface{}{
   416  					"thing1": "g",
   417  					"thing2": "h",
   418  				},
   419  			},
   420  			map[string]string{
   421  				"listDeep.#":        "4",
   422  				"listDeep.0.thing1": "a",
   423  				"listDeep.0.thing2": "b",
   424  				"listDeep.1.thing1": "c",
   425  				"listDeep.1.thing2": "d",
   426  				"listDeep.2.thing1": "e",
   427  				"listDeep.2.thing2": "f",
   428  				"listDeep.3.thing1": "g",
   429  				"listDeep.3.thing2": "h",
   430  			},
   431  		},
   432  		{
   433  			// Remove an element
   434  			[]string{"listDeep"},
   435  			[]interface{}{
   436  				map[string]interface{}{
   437  					"thing1": "a",
   438  					"thing2": "b",
   439  				},
   440  				map[string]interface{}{
   441  					"thing1": "c",
   442  					"thing2": "d",
   443  				},
   444  				map[string]interface{}{
   445  					"thing1": "e",
   446  					"thing2": "f",
   447  				},
   448  			},
   449  			map[string]string{
   450  				"listDeep.#":        "3",
   451  				"listDeep.0.thing1": "a",
   452  				"listDeep.0.thing2": "b",
   453  				"listDeep.1.thing1": "c",
   454  				"listDeep.1.thing2": "d",
   455  				"listDeep.2.thing1": "e",
   456  				"listDeep.2.thing2": "f",
   457  			},
   458  		},
   459  		{
   460  			// Rewrite with missing keys. This should normally not be necessary, as
   461  			// hopefully the writers are writing zero values as necessary, but for
   462  			// brevity we want to make sure that what exists in the writer is exactly
   463  			// what the last write looked like coming from the provider.
   464  			[]string{"listDeep"},
   465  			[]interface{}{
   466  				map[string]interface{}{
   467  					"thing1": "a",
   468  				},
   469  				map[string]interface{}{
   470  					"thing1": "c",
   471  				},
   472  				map[string]interface{}{
   473  					"thing1": "e",
   474  				},
   475  			},
   476  			map[string]string{
   477  				"listDeep.#":        "3",
   478  				"listDeep.0.thing1": "a",
   479  				"listDeep.1.thing1": "c",
   480  				"listDeep.2.thing1": "e",
   481  			},
   482  		},
   483  	}
   484  
   485  	w := &MapFieldWriter{Schema: schema}
   486  
   487  	for n, tc := range values {
   488  		err := w.WriteField(tc.Addr, tc.Value)
   489  		if err != nil {
   490  			t.Fatalf("%d: err: %s", n, err)
   491  		}
   492  
   493  		actual := w.Map()
   494  		if !reflect.DeepEqual(actual, tc.Out) {
   495  			t.Fatalf("%d: bad: %#v", n, actual)
   496  		}
   497  	}
   498  }
   499  
   500  func TestMapFieldWriterCleanMap(t *testing.T) {
   501  	schema := map[string]*Schema{
   502  		"map": {
   503  			Type: TypeMap,
   504  		},
   505  	}
   506  
   507  	values := []struct {
   508  		Value interface{}
   509  		Out   map[string]string
   510  	}{
   511  		{
   512  			// Base map
   513  			map[string]interface{}{
   514  				"thing1": "a",
   515  				"thing2": "b",
   516  				"thing3": "c",
   517  				"thing4": "d",
   518  			},
   519  			map[string]string{
   520  				"map.%":      "4",
   521  				"map.thing1": "a",
   522  				"map.thing2": "b",
   523  				"map.thing3": "c",
   524  				"map.thing4": "d",
   525  			},
   526  		},
   527  		{
   528  			// Base map
   529  			map[string]interface{}{
   530  				"thing1": "a",
   531  				"thing2": "b",
   532  				"thing4": "d",
   533  			},
   534  			map[string]string{
   535  				"map.%":      "3",
   536  				"map.thing1": "a",
   537  				"map.thing2": "b",
   538  				"map.thing4": "d",
   539  			},
   540  		},
   541  	}
   542  
   543  	w := &MapFieldWriter{Schema: schema}
   544  
   545  	for n, tc := range values {
   546  		err := w.WriteField([]string{"map"}, tc.Value)
   547  		if err != nil {
   548  			t.Fatalf("%d: err: %s", n, err)
   549  		}
   550  
   551  		actual := w.Map()
   552  		if !reflect.DeepEqual(actual, tc.Out) {
   553  			t.Fatalf("%d: bad: %#v", n, actual)
   554  		}
   555  	}
   556  }