github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/legacy/helper/schema/field_reader_diff_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package schema
     5  
     6  import (
     7  	"reflect"
     8  	"testing"
     9  
    10  	"github.com/terramate-io/tf/legacy/terraform"
    11  )
    12  
    13  func TestDiffFieldReader_impl(t *testing.T) {
    14  	var _ FieldReader = new(DiffFieldReader)
    15  }
    16  
    17  func TestDiffFieldReader_NestedSetUpdate(t *testing.T) {
    18  	hashFn := func(a interface{}) int {
    19  		m := a.(map[string]interface{})
    20  		return m["val"].(int)
    21  	}
    22  
    23  	schema := map[string]*Schema{
    24  		"list_of_sets_1": &Schema{
    25  			Type: TypeList,
    26  			Elem: &Resource{
    27  				Schema: map[string]*Schema{
    28  					"nested_set": &Schema{
    29  						Type: TypeSet,
    30  						Elem: &Resource{
    31  							Schema: map[string]*Schema{
    32  								"val": &Schema{
    33  									Type: TypeInt,
    34  								},
    35  							},
    36  						},
    37  						Set: hashFn,
    38  					},
    39  				},
    40  			},
    41  		},
    42  		"list_of_sets_2": &Schema{
    43  			Type: TypeList,
    44  			Elem: &Resource{
    45  				Schema: map[string]*Schema{
    46  					"nested_set": &Schema{
    47  						Type: TypeSet,
    48  						Elem: &Resource{
    49  							Schema: map[string]*Schema{
    50  								"val": &Schema{
    51  									Type: TypeInt,
    52  								},
    53  							},
    54  						},
    55  						Set: hashFn,
    56  					},
    57  				},
    58  			},
    59  		},
    60  	}
    61  
    62  	r := &DiffFieldReader{
    63  		Schema: schema,
    64  		Diff: &terraform.InstanceDiff{
    65  			Attributes: map[string]*terraform.ResourceAttrDiff{
    66  				"list_of_sets_1.0.nested_set.1.val": &terraform.ResourceAttrDiff{
    67  					Old:        "1",
    68  					New:        "0",
    69  					NewRemoved: true,
    70  				},
    71  				"list_of_sets_1.0.nested_set.2.val": &terraform.ResourceAttrDiff{
    72  					New: "2",
    73  				},
    74  			},
    75  		},
    76  	}
    77  
    78  	r.Source = &MultiLevelFieldReader{
    79  		Readers: map[string]FieldReader{
    80  			"diff": r,
    81  			"set":  &MapFieldReader{Schema: schema},
    82  			"state": &MapFieldReader{
    83  				Map: &BasicMapReader{
    84  					"list_of_sets_1.#":                  "1",
    85  					"list_of_sets_1.0.nested_set.#":     "1",
    86  					"list_of_sets_1.0.nested_set.1.val": "1",
    87  					"list_of_sets_2.#":                  "1",
    88  					"list_of_sets_2.0.nested_set.#":     "1",
    89  					"list_of_sets_2.0.nested_set.1.val": "1",
    90  				},
    91  				Schema: schema,
    92  			},
    93  		},
    94  		Levels: []string{"state", "config"},
    95  	}
    96  
    97  	out, err := r.ReadField([]string{"list_of_sets_2"})
    98  	if err != nil {
    99  		t.Fatalf("err: %v", err)
   100  	}
   101  
   102  	s := &Set{F: hashFn}
   103  	s.Add(map[string]interface{}{"val": 1})
   104  	expected := s.List()
   105  
   106  	l := out.Value.([]interface{})
   107  	i := l[0].(map[string]interface{})
   108  	actual := i["nested_set"].(*Set).List()
   109  
   110  	if !reflect.DeepEqual(expected, actual) {
   111  		t.Fatalf("bad: NestedSetUpdate\n\nexpected: %#v\n\ngot: %#v\n\n", expected, actual)
   112  	}
   113  }
   114  
   115  // https://github.com/terramate-io/tf/issues/914
   116  func TestDiffFieldReader_MapHandling(t *testing.T) {
   117  	schema := map[string]*Schema{
   118  		"tags": &Schema{
   119  			Type: TypeMap,
   120  		},
   121  	}
   122  	r := &DiffFieldReader{
   123  		Schema: schema,
   124  		Diff: &terraform.InstanceDiff{
   125  			Attributes: map[string]*terraform.ResourceAttrDiff{
   126  				"tags.%": &terraform.ResourceAttrDiff{
   127  					Old: "1",
   128  					New: "2",
   129  				},
   130  				"tags.baz": &terraform.ResourceAttrDiff{
   131  					Old: "",
   132  					New: "qux",
   133  				},
   134  			},
   135  		},
   136  		Source: &MapFieldReader{
   137  			Schema: schema,
   138  			Map: BasicMapReader(map[string]string{
   139  				"tags.%":   "1",
   140  				"tags.foo": "bar",
   141  			}),
   142  		},
   143  	}
   144  
   145  	result, err := r.ReadField([]string{"tags"})
   146  	if err != nil {
   147  		t.Fatalf("ReadField failed: %#v", err)
   148  	}
   149  
   150  	expected := map[string]interface{}{
   151  		"foo": "bar",
   152  		"baz": "qux",
   153  	}
   154  
   155  	if !reflect.DeepEqual(expected, result.Value) {
   156  		t.Fatalf("bad: DiffHandling\n\nexpected: %#v\n\ngot: %#v\n\n", expected, result.Value)
   157  	}
   158  }
   159  
   160  func TestDiffFieldReader_extra(t *testing.T) {
   161  	schema := map[string]*Schema{
   162  		"stringComputed": &Schema{Type: TypeString},
   163  
   164  		"listMap": &Schema{
   165  			Type: TypeList,
   166  			Elem: &Schema{
   167  				Type: TypeMap,
   168  			},
   169  		},
   170  
   171  		"mapRemove": &Schema{Type: TypeMap},
   172  
   173  		"setChange": &Schema{
   174  			Type:     TypeSet,
   175  			Optional: true,
   176  			Elem: &Resource{
   177  				Schema: map[string]*Schema{
   178  					"index": &Schema{
   179  						Type:     TypeInt,
   180  						Required: true,
   181  					},
   182  
   183  					"value": &Schema{
   184  						Type:     TypeString,
   185  						Required: true,
   186  					},
   187  				},
   188  			},
   189  			Set: func(a interface{}) int {
   190  				m := a.(map[string]interface{})
   191  				return m["index"].(int)
   192  			},
   193  		},
   194  
   195  		"setEmpty": &Schema{
   196  			Type:     TypeSet,
   197  			Optional: true,
   198  			Elem: &Resource{
   199  				Schema: map[string]*Schema{
   200  					"index": &Schema{
   201  						Type:     TypeInt,
   202  						Required: true,
   203  					},
   204  
   205  					"value": &Schema{
   206  						Type:     TypeString,
   207  						Required: true,
   208  					},
   209  				},
   210  			},
   211  			Set: func(a interface{}) int {
   212  				m := a.(map[string]interface{})
   213  				return m["index"].(int)
   214  			},
   215  		},
   216  	}
   217  
   218  	r := &DiffFieldReader{
   219  		Schema: schema,
   220  		Diff: &terraform.InstanceDiff{
   221  			Attributes: map[string]*terraform.ResourceAttrDiff{
   222  				"stringComputed": &terraform.ResourceAttrDiff{
   223  					Old:         "foo",
   224  					New:         "bar",
   225  					NewComputed: true,
   226  				},
   227  
   228  				"listMap.0.bar": &terraform.ResourceAttrDiff{
   229  					NewRemoved: true,
   230  				},
   231  
   232  				"mapRemove.bar": &terraform.ResourceAttrDiff{
   233  					NewRemoved: true,
   234  				},
   235  
   236  				"setChange.10.value": &terraform.ResourceAttrDiff{
   237  					Old: "50",
   238  					New: "80",
   239  				},
   240  
   241  				"setEmpty.#": &terraform.ResourceAttrDiff{
   242  					Old: "2",
   243  					New: "0",
   244  				},
   245  			},
   246  		},
   247  
   248  		Source: &MapFieldReader{
   249  			Schema: schema,
   250  			Map: BasicMapReader(map[string]string{
   251  				"listMap.#":     "2",
   252  				"listMap.0.foo": "bar",
   253  				"listMap.0.bar": "baz",
   254  				"listMap.1.baz": "baz",
   255  
   256  				"mapRemove.foo": "bar",
   257  				"mapRemove.bar": "bar",
   258  
   259  				"setChange.#":        "1",
   260  				"setChange.10.index": "10",
   261  				"setChange.10.value": "50",
   262  
   263  				"setEmpty.#":        "2",
   264  				"setEmpty.10.index": "10",
   265  				"setEmpty.10.value": "50",
   266  				"setEmpty.20.index": "20",
   267  				"setEmpty.20.value": "50",
   268  			}),
   269  		},
   270  	}
   271  
   272  	cases := map[string]struct {
   273  		Addr   []string
   274  		Result FieldReadResult
   275  		Err    bool
   276  	}{
   277  		"stringComputed": {
   278  			[]string{"stringComputed"},
   279  			FieldReadResult{
   280  				Value:    "",
   281  				Exists:   true,
   282  				Computed: true,
   283  			},
   284  			false,
   285  		},
   286  
   287  		"listMapRemoval": {
   288  			[]string{"listMap"},
   289  			FieldReadResult{
   290  				Value: []interface{}{
   291  					map[string]interface{}{
   292  						"foo": "bar",
   293  					},
   294  					map[string]interface{}{
   295  						"baz": "baz",
   296  					},
   297  				},
   298  				Exists: true,
   299  			},
   300  			false,
   301  		},
   302  
   303  		"mapRemove": {
   304  			[]string{"mapRemove"},
   305  			FieldReadResult{
   306  				Value: map[string]interface{}{
   307  					"foo": "bar",
   308  				},
   309  				Exists:   true,
   310  				Computed: false,
   311  			},
   312  			false,
   313  		},
   314  
   315  		"setChange": {
   316  			[]string{"setChange"},
   317  			FieldReadResult{
   318  				Value: []interface{}{
   319  					map[string]interface{}{
   320  						"index": 10,
   321  						"value": "80",
   322  					},
   323  				},
   324  				Exists: true,
   325  			},
   326  			false,
   327  		},
   328  
   329  		"setEmpty": {
   330  			[]string{"setEmpty"},
   331  			FieldReadResult{
   332  				Value:  []interface{}{},
   333  				Exists: true,
   334  			},
   335  			false,
   336  		},
   337  	}
   338  
   339  	for name, tc := range cases {
   340  		out, err := r.ReadField(tc.Addr)
   341  		if err != nil != tc.Err {
   342  			t.Fatalf("%s: err: %s", name, err)
   343  		}
   344  		if s, ok := out.Value.(*Set); ok {
   345  			// If it is a set, convert to a list so its more easily checked.
   346  			out.Value = s.List()
   347  		}
   348  		if !reflect.DeepEqual(tc.Result, out) {
   349  			t.Fatalf("%s: bad: %#v", name, out)
   350  		}
   351  	}
   352  }
   353  
   354  func TestDiffFieldReader(t *testing.T) {
   355  	testFieldReader(t, func(s map[string]*Schema) FieldReader {
   356  		return &DiffFieldReader{
   357  			Schema: s,
   358  			Diff: &terraform.InstanceDiff{
   359  				Attributes: map[string]*terraform.ResourceAttrDiff{
   360  					"bool": &terraform.ResourceAttrDiff{
   361  						Old: "",
   362  						New: "true",
   363  					},
   364  
   365  					"int": &terraform.ResourceAttrDiff{
   366  						Old: "",
   367  						New: "42",
   368  					},
   369  
   370  					"float": &terraform.ResourceAttrDiff{
   371  						Old: "",
   372  						New: "3.1415",
   373  					},
   374  
   375  					"string": &terraform.ResourceAttrDiff{
   376  						Old: "",
   377  						New: "string",
   378  					},
   379  
   380  					"stringComputed": &terraform.ResourceAttrDiff{
   381  						Old:         "foo",
   382  						New:         "bar",
   383  						NewComputed: true,
   384  					},
   385  
   386  					"list.#": &terraform.ResourceAttrDiff{
   387  						Old: "0",
   388  						New: "2",
   389  					},
   390  
   391  					"list.0": &terraform.ResourceAttrDiff{
   392  						Old: "",
   393  						New: "foo",
   394  					},
   395  
   396  					"list.1": &terraform.ResourceAttrDiff{
   397  						Old: "",
   398  						New: "bar",
   399  					},
   400  
   401  					"listInt.#": &terraform.ResourceAttrDiff{
   402  						Old: "0",
   403  						New: "2",
   404  					},
   405  
   406  					"listInt.0": &terraform.ResourceAttrDiff{
   407  						Old: "",
   408  						New: "21",
   409  					},
   410  
   411  					"listInt.1": &terraform.ResourceAttrDiff{
   412  						Old: "",
   413  						New: "42",
   414  					},
   415  
   416  					"map.foo": &terraform.ResourceAttrDiff{
   417  						Old: "",
   418  						New: "bar",
   419  					},
   420  
   421  					"map.bar": &terraform.ResourceAttrDiff{
   422  						Old: "",
   423  						New: "baz",
   424  					},
   425  
   426  					"mapInt.%": &terraform.ResourceAttrDiff{
   427  						Old: "",
   428  						New: "2",
   429  					},
   430  					"mapInt.one": &terraform.ResourceAttrDiff{
   431  						Old: "",
   432  						New: "1",
   433  					},
   434  					"mapInt.two": &terraform.ResourceAttrDiff{
   435  						Old: "",
   436  						New: "2",
   437  					},
   438  
   439  					"mapIntNestedSchema.%": &terraform.ResourceAttrDiff{
   440  						Old: "",
   441  						New: "2",
   442  					},
   443  					"mapIntNestedSchema.one": &terraform.ResourceAttrDiff{
   444  						Old: "",
   445  						New: "1",
   446  					},
   447  					"mapIntNestedSchema.two": &terraform.ResourceAttrDiff{
   448  						Old: "",
   449  						New: "2",
   450  					},
   451  
   452  					"mapFloat.%": &terraform.ResourceAttrDiff{
   453  						Old: "",
   454  						New: "1",
   455  					},
   456  					"mapFloat.oneDotTwo": &terraform.ResourceAttrDiff{
   457  						Old: "",
   458  						New: "1.2",
   459  					},
   460  
   461  					"mapBool.%": &terraform.ResourceAttrDiff{
   462  						Old: "",
   463  						New: "2",
   464  					},
   465  					"mapBool.True": &terraform.ResourceAttrDiff{
   466  						Old: "",
   467  						New: "true",
   468  					},
   469  					"mapBool.False": &terraform.ResourceAttrDiff{
   470  						Old: "",
   471  						New: "false",
   472  					},
   473  
   474  					"set.#": &terraform.ResourceAttrDiff{
   475  						Old: "0",
   476  						New: "2",
   477  					},
   478  
   479  					"set.10": &terraform.ResourceAttrDiff{
   480  						Old: "",
   481  						New: "10",
   482  					},
   483  
   484  					"set.50": &terraform.ResourceAttrDiff{
   485  						Old: "",
   486  						New: "50",
   487  					},
   488  
   489  					"setDeep.#": &terraform.ResourceAttrDiff{
   490  						Old: "0",
   491  						New: "2",
   492  					},
   493  
   494  					"setDeep.10.index": &terraform.ResourceAttrDiff{
   495  						Old: "",
   496  						New: "10",
   497  					},
   498  
   499  					"setDeep.10.value": &terraform.ResourceAttrDiff{
   500  						Old: "",
   501  						New: "foo",
   502  					},
   503  
   504  					"setDeep.50.index": &terraform.ResourceAttrDiff{
   505  						Old: "",
   506  						New: "50",
   507  					},
   508  
   509  					"setDeep.50.value": &terraform.ResourceAttrDiff{
   510  						Old: "",
   511  						New: "bar",
   512  					},
   513  				},
   514  			},
   515  
   516  			Source: &MapFieldReader{
   517  				Schema: s,
   518  				Map: BasicMapReader(map[string]string{
   519  					"listMap.#":     "2",
   520  					"listMap.0.foo": "bar",
   521  					"listMap.0.bar": "baz",
   522  					"listMap.1.baz": "baz",
   523  				}),
   524  			},
   525  		}
   526  	})
   527  }