github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/configs/hcl2shim/paths_test.go (about)

     1  package hcl2shim
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strconv"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/google/go-cmp/cmp/cmpopts"
    11  
    12  	"github.com/google/go-cmp/cmp"
    13  
    14  	"github.com/zclconf/go-cty/cty"
    15  )
    16  
    17  var (
    18  	ignoreUnexported = cmpopts.IgnoreUnexported(cty.GetAttrStep{}, cty.IndexStep{})
    19  	valueComparer    = cmp.Comparer(cty.Value.RawEquals)
    20  )
    21  
    22  func TestPathFromFlatmap(t *testing.T) {
    23  	tests := []struct {
    24  		Flatmap string
    25  		Type    cty.Type
    26  		Want    cty.Path
    27  		WantErr string
    28  	}{
    29  		{
    30  			Flatmap: "",
    31  			Type:    cty.EmptyObject,
    32  			Want:    nil,
    33  		},
    34  		{
    35  			Flatmap: "attr",
    36  			Type:    cty.EmptyObject,
    37  			Want:    nil,
    38  			WantErr: `attribute "attr" not found`,
    39  		},
    40  		{
    41  			Flatmap: "foo",
    42  			Type: cty.Object(map[string]cty.Type{
    43  				"foo": cty.String,
    44  			}),
    45  			Want: cty.Path{
    46  				cty.GetAttrStep{Name: "foo"},
    47  			},
    48  		},
    49  		{
    50  			Flatmap: "foo.#",
    51  			Type: cty.Object(map[string]cty.Type{
    52  				"foo": cty.List(cty.String),
    53  			}),
    54  			Want: cty.Path{
    55  				cty.GetAttrStep{Name: "foo"},
    56  			},
    57  		},
    58  		{
    59  			Flatmap: "foo.1",
    60  			Type: cty.Object(map[string]cty.Type{
    61  				"foo": cty.List(cty.String),
    62  			}),
    63  			Want: cty.Path{
    64  				cty.GetAttrStep{Name: "foo"},
    65  				cty.IndexStep{Key: cty.NumberIntVal(1)},
    66  			},
    67  		},
    68  		{
    69  			Flatmap: "foo.1",
    70  			Type: cty.Object(map[string]cty.Type{
    71  				"foo": cty.Tuple([]cty.Type{
    72  					cty.String,
    73  					cty.Bool,
    74  				}),
    75  			}),
    76  			Want: cty.Path{
    77  				cty.GetAttrStep{Name: "foo"},
    78  				cty.IndexStep{Key: cty.NumberIntVal(1)},
    79  			},
    80  		},
    81  		{
    82  			// a set index returns the set itself, since this being applied to
    83  			// a diff and the set is changing.
    84  			Flatmap: "foo.24534534",
    85  			Type: cty.Object(map[string]cty.Type{
    86  				"foo": cty.Set(cty.String),
    87  			}),
    88  			Want: cty.Path{
    89  				cty.GetAttrStep{Name: "foo"},
    90  			},
    91  		},
    92  		{
    93  			Flatmap: "foo.%",
    94  			Type: cty.Object(map[string]cty.Type{
    95  				"foo": cty.Map(cty.String),
    96  			}),
    97  			Want: cty.Path{
    98  				cty.GetAttrStep{Name: "foo"},
    99  			},
   100  		},
   101  		{
   102  			Flatmap: "foo.baz",
   103  			Type: cty.Object(map[string]cty.Type{
   104  				"foo": cty.Map(cty.Bool),
   105  			}),
   106  			Want: cty.Path{
   107  				cty.GetAttrStep{Name: "foo"},
   108  				cty.IndexStep{Key: cty.StringVal("baz")},
   109  			},
   110  		},
   111  		{
   112  			Flatmap: "foo.bar.baz",
   113  			Type: cty.Object(map[string]cty.Type{
   114  				"foo": cty.Map(
   115  					cty.Map(cty.Bool),
   116  				),
   117  			}),
   118  			Want: cty.Path{
   119  				cty.GetAttrStep{Name: "foo"},
   120  				cty.IndexStep{Key: cty.StringVal("bar")},
   121  				cty.IndexStep{Key: cty.StringVal("baz")},
   122  			},
   123  		},
   124  		{
   125  			Flatmap: "foo.bar.baz",
   126  			Type: cty.Object(map[string]cty.Type{
   127  				"foo": cty.Map(
   128  					cty.Object(map[string]cty.Type{
   129  						"baz": cty.String,
   130  					}),
   131  				),
   132  			}),
   133  			Want: cty.Path{
   134  				cty.GetAttrStep{Name: "foo"},
   135  				cty.IndexStep{Key: cty.StringVal("bar")},
   136  				cty.GetAttrStep{Name: "baz"},
   137  			},
   138  		},
   139  		{
   140  			Flatmap: "foo.0.bar",
   141  			Type: cty.Object(map[string]cty.Type{
   142  				"foo": cty.List(cty.Object(map[string]cty.Type{
   143  					"bar": cty.String,
   144  					"baz": cty.Bool,
   145  				})),
   146  			}),
   147  			Want: cty.Path{
   148  				cty.GetAttrStep{Name: "foo"},
   149  				cty.IndexStep{Key: cty.NumberIntVal(0)},
   150  				cty.GetAttrStep{Name: "bar"},
   151  			},
   152  		},
   153  		{
   154  			Flatmap: "foo.34534534.baz",
   155  			Type: cty.Object(map[string]cty.Type{
   156  				"foo": cty.Set(cty.Object(map[string]cty.Type{
   157  					"bar": cty.String,
   158  					"baz": cty.Bool,
   159  				})),
   160  			}),
   161  			Want: cty.Path{
   162  				cty.GetAttrStep{Name: "foo"},
   163  			},
   164  		},
   165  		{
   166  			Flatmap: "foo.bar.bang",
   167  			Type: cty.Object(map[string]cty.Type{
   168  				"foo": cty.String,
   169  			}),
   170  			WantErr: `invalid step "bar.bang"`,
   171  		},
   172  		{
   173  			// there should not be any attribute names with dots
   174  			Flatmap: "foo.bar.bang",
   175  			Type: cty.Object(map[string]cty.Type{
   176  				"foo.bar": cty.Map(cty.String),
   177  			}),
   178  			WantErr: `attribute "foo" not found`,
   179  		},
   180  		{
   181  			// We can only handle key names with dots if the map elements are a
   182  			// primitive type.
   183  			Flatmap: "foo.bar.bop",
   184  			Type: cty.Object(map[string]cty.Type{
   185  				"foo": cty.Map(cty.String),
   186  			}),
   187  			Want: cty.Path{
   188  				cty.GetAttrStep{Name: "foo"},
   189  				cty.IndexStep{Key: cty.StringVal("bar.bop")},
   190  			},
   191  		},
   192  		{
   193  			Flatmap: "foo.bar.0.baz",
   194  			Type: cty.Object(map[string]cty.Type{
   195  				"foo": cty.Map(
   196  					cty.List(
   197  						cty.Map(cty.String),
   198  					),
   199  				),
   200  			}),
   201  			Want: cty.Path{
   202  				cty.GetAttrStep{Name: "foo"},
   203  				cty.IndexStep{Key: cty.StringVal("bar")},
   204  				cty.IndexStep{Key: cty.NumberIntVal(0)},
   205  				cty.IndexStep{Key: cty.StringVal("baz")},
   206  			},
   207  		},
   208  	}
   209  
   210  	for _, test := range tests {
   211  		t.Run(fmt.Sprintf("%s as %#v", test.Flatmap, test.Type), func(t *testing.T) {
   212  			got, err := requiresReplacePath(test.Flatmap, test.Type)
   213  
   214  			if test.WantErr != "" {
   215  				if err == nil {
   216  					t.Fatalf("succeeded; want error: %s", test.WantErr)
   217  				}
   218  				if got, want := err.Error(), test.WantErr; !strings.Contains(got, want) {
   219  					t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
   220  				}
   221  				return
   222  			} else {
   223  				if err != nil {
   224  					t.Fatalf("unexpected error: %s", err.Error())
   225  				}
   226  			}
   227  
   228  			if !reflect.DeepEqual(got, test.Want) {
   229  				t.Fatalf("incorrect path\ngot:  %#v\nwant: %#v\n", got, test.Want)
   230  			}
   231  		})
   232  	}
   233  }
   234  
   235  func TestRequiresReplace(t *testing.T) {
   236  	for _, tc := range []struct {
   237  		name     string
   238  		attrs    []string
   239  		expected []cty.Path
   240  		ty       cty.Type
   241  	}{
   242  		{
   243  			name: "basic",
   244  			attrs: []string{
   245  				"foo",
   246  			},
   247  			ty: cty.Object(map[string]cty.Type{
   248  				"foo": cty.String,
   249  			}),
   250  			expected: []cty.Path{
   251  				cty.Path{cty.GetAttrStep{Name: "foo"}},
   252  			},
   253  		},
   254  		{
   255  			name: "two",
   256  			attrs: []string{
   257  				"foo",
   258  				"bar",
   259  			},
   260  			ty: cty.Object(map[string]cty.Type{
   261  				"foo": cty.String,
   262  				"bar": cty.String,
   263  			}),
   264  			expected: []cty.Path{
   265  				cty.Path{cty.GetAttrStep{Name: "foo"}},
   266  				cty.Path{cty.GetAttrStep{Name: "bar"}},
   267  			},
   268  		},
   269  		{
   270  			name: "nested object",
   271  			attrs: []string{
   272  				"foo.bar",
   273  			},
   274  			ty: cty.Object(map[string]cty.Type{
   275  				"foo": cty.Object(map[string]cty.Type{
   276  					"bar": cty.String,
   277  				}),
   278  			}),
   279  			expected: []cty.Path{
   280  				cty.Path{cty.GetAttrStep{Name: "foo"}, cty.GetAttrStep{Name: "bar"}},
   281  			},
   282  		},
   283  		{
   284  			name: "nested objects",
   285  			attrs: []string{
   286  				"foo.bar.baz",
   287  			},
   288  			ty: cty.Object(map[string]cty.Type{
   289  				"foo": cty.Object(map[string]cty.Type{
   290  					"bar": cty.Object(map[string]cty.Type{
   291  						"baz": cty.String,
   292  					}),
   293  				}),
   294  			}),
   295  			expected: []cty.Path{
   296  				cty.Path{cty.GetAttrStep{Name: "foo"}, cty.GetAttrStep{Name: "bar"}, cty.GetAttrStep{Name: "baz"}},
   297  			},
   298  		},
   299  		{
   300  			name: "nested map",
   301  			attrs: []string{
   302  				"foo.%",
   303  				"foo.bar",
   304  			},
   305  			ty: cty.Object(map[string]cty.Type{
   306  				"foo": cty.Map(cty.String),
   307  			}),
   308  			expected: []cty.Path{
   309  				cty.Path{cty.GetAttrStep{Name: "foo"}},
   310  			},
   311  		},
   312  		{
   313  			name: "nested list",
   314  			attrs: []string{
   315  				"foo.#",
   316  				"foo.1",
   317  			},
   318  			ty: cty.Object(map[string]cty.Type{
   319  				"foo": cty.Map(cty.String),
   320  			}),
   321  			expected: []cty.Path{
   322  				cty.Path{cty.GetAttrStep{Name: "foo"}},
   323  			},
   324  		},
   325  		{
   326  			name: "object in map",
   327  			attrs: []string{
   328  				"foo.bar.baz",
   329  			},
   330  			ty: cty.Object(map[string]cty.Type{
   331  				"foo": cty.Map(cty.Object(
   332  					map[string]cty.Type{
   333  						"baz": cty.String,
   334  					},
   335  				)),
   336  			}),
   337  			expected: []cty.Path{
   338  				cty.Path{cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.StringVal("bar")}, cty.GetAttrStep{Name: "baz"}},
   339  			},
   340  		},
   341  		{
   342  			name: "object in list",
   343  			attrs: []string{
   344  				"foo.1.baz",
   345  			},
   346  			ty: cty.Object(map[string]cty.Type{
   347  				"foo": cty.List(cty.Object(
   348  					map[string]cty.Type{
   349  						"baz": cty.String,
   350  					},
   351  				)),
   352  			}),
   353  			expected: []cty.Path{
   354  				cty.Path{cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.NumberIntVal(1)}, cty.GetAttrStep{Name: "baz"}},
   355  			},
   356  		},
   357  	} {
   358  		t.Run(tc.name, func(t *testing.T) {
   359  			rp, err := RequiresReplace(tc.attrs, tc.ty)
   360  			if err != nil {
   361  				t.Fatal(err)
   362  			}
   363  			if !cmp.Equal(tc.expected, rp, ignoreUnexported, valueComparer) {
   364  				t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.expected, rp)
   365  			}
   366  		})
   367  
   368  	}
   369  }
   370  
   371  func TestFlatmapKeyFromPath(t *testing.T) {
   372  	for i, tc := range []struct {
   373  		path cty.Path
   374  		attr string
   375  	}{
   376  		{
   377  			path: cty.Path{
   378  				cty.GetAttrStep{Name: "force_new"},
   379  			},
   380  			attr: "force_new",
   381  		},
   382  		{
   383  			path: cty.Path{
   384  				cty.GetAttrStep{Name: "attr"},
   385  				cty.IndexStep{Key: cty.NumberIntVal(0)},
   386  				cty.GetAttrStep{Name: "force_new"},
   387  			},
   388  			attr: "attr.0.force_new",
   389  		},
   390  		{
   391  			path: cty.Path{
   392  				cty.GetAttrStep{Name: "attr"},
   393  				cty.IndexStep{Key: cty.StringVal("key")},
   394  				cty.GetAttrStep{Name: "obj_attr"},
   395  				cty.IndexStep{Key: cty.NumberIntVal(0)},
   396  				cty.GetAttrStep{Name: "force_new"},
   397  			},
   398  			attr: "attr.key.obj_attr.0.force_new",
   399  		},
   400  	} {
   401  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   402  			attr := FlatmapKeyFromPath(tc.path)
   403  			if attr != tc.attr {
   404  				t.Fatalf("expected:%q got:%q", tc.attr, attr)
   405  			}
   406  		})
   407  	}
   408  }