github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/legacy/helper/schema/field_reader_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  
    11  func TestAddrToSchema(t *testing.T) {
    12  	cases := map[string]struct {
    13  		Addr   []string
    14  		Schema map[string]*Schema
    15  		Result []ValueType
    16  	}{
    17  		"full object": {
    18  			[]string{},
    19  			map[string]*Schema{
    20  				"list": &Schema{
    21  					Type: TypeList,
    22  					Elem: &Schema{Type: TypeInt},
    23  				},
    24  			},
    25  			[]ValueType{typeObject},
    26  		},
    27  
    28  		"list": {
    29  			[]string{"list"},
    30  			map[string]*Schema{
    31  				"list": &Schema{
    32  					Type: TypeList,
    33  					Elem: &Schema{Type: TypeInt},
    34  				},
    35  			},
    36  			[]ValueType{TypeList},
    37  		},
    38  
    39  		"list.#": {
    40  			[]string{"list", "#"},
    41  			map[string]*Schema{
    42  				"list": &Schema{
    43  					Type: TypeList,
    44  					Elem: &Schema{Type: TypeInt},
    45  				},
    46  			},
    47  			[]ValueType{TypeList, TypeInt},
    48  		},
    49  
    50  		"list.0": {
    51  			[]string{"list", "0"},
    52  			map[string]*Schema{
    53  				"list": &Schema{
    54  					Type: TypeList,
    55  					Elem: &Schema{Type: TypeInt},
    56  				},
    57  			},
    58  			[]ValueType{TypeList, TypeInt},
    59  		},
    60  
    61  		"list.0 with resource": {
    62  			[]string{"list", "0"},
    63  			map[string]*Schema{
    64  				"list": &Schema{
    65  					Type: TypeList,
    66  					Elem: &Resource{
    67  						Schema: map[string]*Schema{
    68  							"field": &Schema{Type: TypeString},
    69  						},
    70  					},
    71  				},
    72  			},
    73  			[]ValueType{TypeList, typeObject},
    74  		},
    75  
    76  		"list.0.field": {
    77  			[]string{"list", "0", "field"},
    78  			map[string]*Schema{
    79  				"list": &Schema{
    80  					Type: TypeList,
    81  					Elem: &Resource{
    82  						Schema: map[string]*Schema{
    83  							"field": &Schema{Type: TypeString},
    84  						},
    85  					},
    86  				},
    87  			},
    88  			[]ValueType{TypeList, typeObject, TypeString},
    89  		},
    90  
    91  		"set": {
    92  			[]string{"set"},
    93  			map[string]*Schema{
    94  				"set": &Schema{
    95  					Type: TypeSet,
    96  					Elem: &Schema{Type: TypeInt},
    97  					Set: func(a interface{}) int {
    98  						return a.(int)
    99  					},
   100  				},
   101  			},
   102  			[]ValueType{TypeSet},
   103  		},
   104  
   105  		"set.#": {
   106  			[]string{"set", "#"},
   107  			map[string]*Schema{
   108  				"set": &Schema{
   109  					Type: TypeSet,
   110  					Elem: &Schema{Type: TypeInt},
   111  					Set: func(a interface{}) int {
   112  						return a.(int)
   113  					},
   114  				},
   115  			},
   116  			[]ValueType{TypeSet, TypeInt},
   117  		},
   118  
   119  		"set.0": {
   120  			[]string{"set", "0"},
   121  			map[string]*Schema{
   122  				"set": &Schema{
   123  					Type: TypeSet,
   124  					Elem: &Schema{Type: TypeInt},
   125  					Set: func(a interface{}) int {
   126  						return a.(int)
   127  					},
   128  				},
   129  			},
   130  			[]ValueType{TypeSet, TypeInt},
   131  		},
   132  
   133  		"set.0 with resource": {
   134  			[]string{"set", "0"},
   135  			map[string]*Schema{
   136  				"set": &Schema{
   137  					Type: TypeSet,
   138  					Elem: &Resource{
   139  						Schema: map[string]*Schema{
   140  							"field": &Schema{Type: TypeString},
   141  						},
   142  					},
   143  				},
   144  			},
   145  			[]ValueType{TypeSet, typeObject},
   146  		},
   147  
   148  		"mapElem": {
   149  			[]string{"map", "foo"},
   150  			map[string]*Schema{
   151  				"map": &Schema{Type: TypeMap},
   152  			},
   153  			[]ValueType{TypeMap, TypeString},
   154  		},
   155  
   156  		"setDeep": {
   157  			[]string{"set", "50", "index"},
   158  			map[string]*Schema{
   159  				"set": &Schema{
   160  					Type: TypeSet,
   161  					Elem: &Resource{
   162  						Schema: map[string]*Schema{
   163  							"index": &Schema{Type: TypeInt},
   164  							"value": &Schema{Type: TypeString},
   165  						},
   166  					},
   167  					Set: func(a interface{}) int {
   168  						return a.(map[string]interface{})["index"].(int)
   169  					},
   170  				},
   171  			},
   172  			[]ValueType{TypeSet, typeObject, TypeInt},
   173  		},
   174  	}
   175  
   176  	for name, tc := range cases {
   177  		result := addrToSchema(tc.Addr, tc.Schema)
   178  		types := make([]ValueType, len(result))
   179  		for i, v := range result {
   180  			types[i] = v.Type
   181  		}
   182  
   183  		if !reflect.DeepEqual(types, tc.Result) {
   184  			t.Fatalf("%s: %#v", name, types)
   185  		}
   186  	}
   187  }
   188  
   189  // testFieldReader is a helper that should be used to verify that
   190  // a FieldReader behaves properly in all the common cases.
   191  func testFieldReader(t *testing.T, f func(map[string]*Schema) FieldReader) {
   192  	schema := map[string]*Schema{
   193  		// Primitives
   194  		"bool":   &Schema{Type: TypeBool},
   195  		"float":  &Schema{Type: TypeFloat},
   196  		"int":    &Schema{Type: TypeInt},
   197  		"string": &Schema{Type: TypeString},
   198  
   199  		// Lists
   200  		"list": &Schema{
   201  			Type: TypeList,
   202  			Elem: &Schema{Type: TypeString},
   203  		},
   204  		"listInt": &Schema{
   205  			Type: TypeList,
   206  			Elem: &Schema{Type: TypeInt},
   207  		},
   208  		"listMap": &Schema{
   209  			Type: TypeList,
   210  			Elem: &Schema{
   211  				Type: TypeMap,
   212  			},
   213  		},
   214  
   215  		// Maps
   216  		"map": &Schema{Type: TypeMap},
   217  		"mapInt": &Schema{
   218  			Type: TypeMap,
   219  			Elem: TypeInt,
   220  		},
   221  
   222  		// This is used to verify that the type of a Map can be specified using the
   223  		// same syntax as for lists (as a nested *Schema passed to Elem)
   224  		"mapIntNestedSchema": &Schema{
   225  			Type: TypeMap,
   226  			Elem: &Schema{Type: TypeInt},
   227  		},
   228  		"mapFloat": &Schema{
   229  			Type: TypeMap,
   230  			Elem: TypeFloat,
   231  		},
   232  		"mapBool": &Schema{
   233  			Type: TypeMap,
   234  			Elem: TypeBool,
   235  		},
   236  
   237  		// Sets
   238  		"set": &Schema{
   239  			Type: TypeSet,
   240  			Elem: &Schema{Type: TypeInt},
   241  			Set: func(a interface{}) int {
   242  				return a.(int)
   243  			},
   244  		},
   245  		"setDeep": &Schema{
   246  			Type: TypeSet,
   247  			Elem: &Resource{
   248  				Schema: map[string]*Schema{
   249  					"index": &Schema{Type: TypeInt},
   250  					"value": &Schema{Type: TypeString},
   251  				},
   252  			},
   253  			Set: func(a interface{}) int {
   254  				return a.(map[string]interface{})["index"].(int)
   255  			},
   256  		},
   257  		"setEmpty": &Schema{
   258  			Type: TypeSet,
   259  			Elem: &Schema{Type: TypeInt},
   260  			Set: func(a interface{}) int {
   261  				return a.(int)
   262  			},
   263  		},
   264  	}
   265  
   266  	cases := map[string]struct {
   267  		Addr   []string
   268  		Result FieldReadResult
   269  		Err    bool
   270  	}{
   271  		"noexist": {
   272  			[]string{"boolNOPE"},
   273  			FieldReadResult{
   274  				Value:    nil,
   275  				Exists:   false,
   276  				Computed: false,
   277  			},
   278  			false,
   279  		},
   280  
   281  		"bool": {
   282  			[]string{"bool"},
   283  			FieldReadResult{
   284  				Value:    true,
   285  				Exists:   true,
   286  				Computed: false,
   287  			},
   288  			false,
   289  		},
   290  
   291  		"float": {
   292  			[]string{"float"},
   293  			FieldReadResult{
   294  				Value:    3.1415,
   295  				Exists:   true,
   296  				Computed: false,
   297  			},
   298  			false,
   299  		},
   300  
   301  		"int": {
   302  			[]string{"int"},
   303  			FieldReadResult{
   304  				Value:    42,
   305  				Exists:   true,
   306  				Computed: false,
   307  			},
   308  			false,
   309  		},
   310  
   311  		"string": {
   312  			[]string{"string"},
   313  			FieldReadResult{
   314  				Value:    "string",
   315  				Exists:   true,
   316  				Computed: false,
   317  			},
   318  			false,
   319  		},
   320  
   321  		"list": {
   322  			[]string{"list"},
   323  			FieldReadResult{
   324  				Value: []interface{}{
   325  					"foo",
   326  					"bar",
   327  				},
   328  				Exists:   true,
   329  				Computed: false,
   330  			},
   331  			false,
   332  		},
   333  
   334  		"listInt": {
   335  			[]string{"listInt"},
   336  			FieldReadResult{
   337  				Value: []interface{}{
   338  					21,
   339  					42,
   340  				},
   341  				Exists:   true,
   342  				Computed: false,
   343  			},
   344  			false,
   345  		},
   346  
   347  		"map": {
   348  			[]string{"map"},
   349  			FieldReadResult{
   350  				Value: map[string]interface{}{
   351  					"foo": "bar",
   352  					"bar": "baz",
   353  				},
   354  				Exists:   true,
   355  				Computed: false,
   356  			},
   357  			false,
   358  		},
   359  
   360  		"mapInt": {
   361  			[]string{"mapInt"},
   362  			FieldReadResult{
   363  				Value: map[string]interface{}{
   364  					"one": 1,
   365  					"two": 2,
   366  				},
   367  				Exists:   true,
   368  				Computed: false,
   369  			},
   370  			false,
   371  		},
   372  
   373  		"mapIntNestedSchema": {
   374  			[]string{"mapIntNestedSchema"},
   375  			FieldReadResult{
   376  				Value: map[string]interface{}{
   377  					"one": 1,
   378  					"two": 2,
   379  				},
   380  				Exists:   true,
   381  				Computed: false,
   382  			},
   383  			false,
   384  		},
   385  
   386  		"mapFloat": {
   387  			[]string{"mapFloat"},
   388  			FieldReadResult{
   389  				Value: map[string]interface{}{
   390  					"oneDotTwo": 1.2,
   391  				},
   392  				Exists:   true,
   393  				Computed: false,
   394  			},
   395  			false,
   396  		},
   397  
   398  		"mapBool": {
   399  			[]string{"mapBool"},
   400  			FieldReadResult{
   401  				Value: map[string]interface{}{
   402  					"True":  true,
   403  					"False": false,
   404  				},
   405  				Exists:   true,
   406  				Computed: false,
   407  			},
   408  			false,
   409  		},
   410  
   411  		"mapelem": {
   412  			[]string{"map", "foo"},
   413  			FieldReadResult{
   414  				Value:    "bar",
   415  				Exists:   true,
   416  				Computed: false,
   417  			},
   418  			false,
   419  		},
   420  
   421  		"set": {
   422  			[]string{"set"},
   423  			FieldReadResult{
   424  				Value:    []interface{}{10, 50},
   425  				Exists:   true,
   426  				Computed: false,
   427  			},
   428  			false,
   429  		},
   430  
   431  		"setDeep": {
   432  			[]string{"setDeep"},
   433  			FieldReadResult{
   434  				Value: []interface{}{
   435  					map[string]interface{}{
   436  						"index": 10,
   437  						"value": "foo",
   438  					},
   439  					map[string]interface{}{
   440  						"index": 50,
   441  						"value": "bar",
   442  					},
   443  				},
   444  				Exists:   true,
   445  				Computed: false,
   446  			},
   447  			false,
   448  		},
   449  
   450  		"setEmpty": {
   451  			[]string{"setEmpty"},
   452  			FieldReadResult{
   453  				Value:  []interface{}{},
   454  				Exists: false,
   455  			},
   456  			false,
   457  		},
   458  	}
   459  
   460  	for name, tc := range cases {
   461  		r := f(schema)
   462  		out, err := r.ReadField(tc.Addr)
   463  		if err != nil != tc.Err {
   464  			t.Fatalf("%s: err: %s", name, err)
   465  		}
   466  		if s, ok := out.Value.(*Set); ok {
   467  			// If it is a set, convert to a list so its more easily checked.
   468  			out.Value = s.List()
   469  		}
   470  		if !reflect.DeepEqual(tc.Result, out) {
   471  			t.Fatalf("%s: bad: %#v", name, out)
   472  		}
   473  	}
   474  }