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

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