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

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