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