github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/jsonplan/plan_test.go (about)

     1  package jsonplan
     2  
     3  import (
     4  	"encoding/json"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/google/go-cmp/cmp"
     9  	"github.com/zclconf/go-cty/cty"
    10  
    11  	"github.com/hashicorp/terraform/internal/addrs"
    12  	"github.com/hashicorp/terraform/internal/plans"
    13  )
    14  
    15  func TestOmitUnknowns(t *testing.T) {
    16  	tests := []struct {
    17  		Input cty.Value
    18  		Want  cty.Value
    19  	}{
    20  		{
    21  			cty.StringVal("hello"),
    22  			cty.StringVal("hello"),
    23  		},
    24  		{
    25  			cty.NullVal(cty.String),
    26  			cty.NullVal(cty.String),
    27  		},
    28  		{
    29  			cty.UnknownVal(cty.String),
    30  			cty.NilVal,
    31  		},
    32  		{
    33  			cty.ListValEmpty(cty.String),
    34  			cty.EmptyTupleVal,
    35  		},
    36  		{
    37  			cty.ListVal([]cty.Value{cty.StringVal("hello")}),
    38  			cty.TupleVal([]cty.Value{cty.StringVal("hello")}),
    39  		},
    40  		{
    41  			cty.ListVal([]cty.Value{cty.NullVal(cty.String)}),
    42  			cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}),
    43  		},
    44  		{
    45  			cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}),
    46  			cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}),
    47  		},
    48  		{
    49  			cty.ListVal([]cty.Value{cty.StringVal("hello")}),
    50  			cty.TupleVal([]cty.Value{cty.StringVal("hello")}),
    51  		},
    52  		//
    53  		{
    54  			cty.ListVal([]cty.Value{
    55  				cty.StringVal("hello"),
    56  				cty.UnknownVal(cty.String)}),
    57  			cty.TupleVal([]cty.Value{
    58  				cty.StringVal("hello"),
    59  				cty.NullVal(cty.String),
    60  			}),
    61  		},
    62  		{
    63  			cty.MapVal(map[string]cty.Value{
    64  				"hello": cty.True,
    65  				"world": cty.UnknownVal(cty.Bool),
    66  			}),
    67  			cty.ObjectVal(map[string]cty.Value{
    68  				"hello": cty.True,
    69  			}),
    70  		},
    71  		{
    72  			cty.TupleVal([]cty.Value{
    73  				cty.StringVal("alpha"),
    74  				cty.UnknownVal(cty.String),
    75  				cty.StringVal("charlie"),
    76  			}),
    77  			cty.TupleVal([]cty.Value{
    78  				cty.StringVal("alpha"),
    79  				cty.NullVal(cty.String),
    80  				cty.StringVal("charlie"),
    81  			}),
    82  		},
    83  		{
    84  			cty.SetVal([]cty.Value{
    85  				cty.StringVal("dev"),
    86  				cty.StringVal("foo"),
    87  				cty.StringVal("stg"),
    88  				cty.UnknownVal(cty.String),
    89  			}),
    90  			cty.TupleVal([]cty.Value{
    91  				cty.StringVal("dev"),
    92  				cty.StringVal("foo"),
    93  				cty.StringVal("stg"),
    94  				cty.NullVal(cty.String),
    95  			}),
    96  		},
    97  		{
    98  			cty.SetVal([]cty.Value{
    99  				cty.ObjectVal(map[string]cty.Value{
   100  					"a": cty.UnknownVal(cty.String),
   101  				}),
   102  				cty.ObjectVal(map[string]cty.Value{
   103  					"a": cty.StringVal("known"),
   104  				}),
   105  			}),
   106  			cty.TupleVal([]cty.Value{
   107  				cty.ObjectVal(map[string]cty.Value{
   108  					"a": cty.StringVal("known"),
   109  				}),
   110  				cty.EmptyObjectVal,
   111  			}),
   112  		},
   113  	}
   114  
   115  	for _, test := range tests {
   116  		got := omitUnknowns(test.Input)
   117  		if !reflect.DeepEqual(got, test.Want) {
   118  			t.Errorf(
   119  				"wrong result\ninput: %#v\ngot:   %#v\nwant:  %#v",
   120  				test.Input, got, test.Want,
   121  			)
   122  		}
   123  	}
   124  }
   125  
   126  func TestUnknownAsBool(t *testing.T) {
   127  	tests := []struct {
   128  		Input cty.Value
   129  		Want  cty.Value
   130  	}{
   131  		{
   132  			cty.StringVal("hello"),
   133  			cty.False,
   134  		},
   135  		{
   136  			cty.NullVal(cty.String),
   137  			cty.False,
   138  		},
   139  		{
   140  			cty.UnknownVal(cty.String),
   141  			cty.True,
   142  		},
   143  
   144  		{
   145  			cty.NullVal(cty.DynamicPseudoType),
   146  			cty.False,
   147  		},
   148  		{
   149  			cty.NullVal(cty.Object(map[string]cty.Type{"test": cty.String})),
   150  			cty.False,
   151  		},
   152  		{
   153  			cty.DynamicVal,
   154  			cty.True,
   155  		},
   156  
   157  		{
   158  			cty.ListValEmpty(cty.String),
   159  			cty.EmptyTupleVal,
   160  		},
   161  		{
   162  			cty.ListVal([]cty.Value{cty.StringVal("hello")}),
   163  			cty.TupleVal([]cty.Value{cty.False}),
   164  		},
   165  		{
   166  			cty.ListVal([]cty.Value{cty.NullVal(cty.String)}),
   167  			cty.TupleVal([]cty.Value{cty.False}),
   168  		},
   169  		{
   170  			cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}),
   171  			cty.TupleVal([]cty.Value{cty.True}),
   172  		},
   173  		{
   174  			cty.SetValEmpty(cty.String),
   175  			cty.EmptyTupleVal,
   176  		},
   177  		{
   178  			cty.SetVal([]cty.Value{cty.StringVal("hello")}),
   179  			cty.TupleVal([]cty.Value{cty.False}),
   180  		},
   181  		{
   182  			cty.SetVal([]cty.Value{cty.NullVal(cty.String)}),
   183  			cty.TupleVal([]cty.Value{cty.False}),
   184  		},
   185  		{
   186  			cty.SetVal([]cty.Value{cty.UnknownVal(cty.String)}),
   187  			cty.TupleVal([]cty.Value{cty.True}),
   188  		},
   189  		{
   190  			cty.EmptyTupleVal,
   191  			cty.EmptyTupleVal,
   192  		},
   193  		{
   194  			cty.TupleVal([]cty.Value{cty.StringVal("hello")}),
   195  			cty.TupleVal([]cty.Value{cty.False}),
   196  		},
   197  		{
   198  			cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}),
   199  			cty.TupleVal([]cty.Value{cty.False}),
   200  		},
   201  		{
   202  			cty.TupleVal([]cty.Value{cty.UnknownVal(cty.String)}),
   203  			cty.TupleVal([]cty.Value{cty.True}),
   204  		},
   205  		{
   206  			cty.MapValEmpty(cty.String),
   207  			cty.EmptyObjectVal,
   208  		},
   209  		{
   210  			cty.MapVal(map[string]cty.Value{"greeting": cty.StringVal("hello")}),
   211  			cty.EmptyObjectVal,
   212  		},
   213  		{
   214  			cty.MapVal(map[string]cty.Value{"greeting": cty.NullVal(cty.String)}),
   215  			cty.EmptyObjectVal,
   216  		},
   217  		{
   218  			cty.MapVal(map[string]cty.Value{"greeting": cty.UnknownVal(cty.String)}),
   219  			cty.ObjectVal(map[string]cty.Value{"greeting": cty.True}),
   220  		},
   221  		{
   222  			cty.EmptyObjectVal,
   223  			cty.EmptyObjectVal,
   224  		},
   225  		{
   226  			cty.ObjectVal(map[string]cty.Value{"greeting": cty.StringVal("hello")}),
   227  			cty.EmptyObjectVal,
   228  		},
   229  		{
   230  			cty.ObjectVal(map[string]cty.Value{"greeting": cty.NullVal(cty.String)}),
   231  			cty.EmptyObjectVal,
   232  		},
   233  		{
   234  			cty.ObjectVal(map[string]cty.Value{"greeting": cty.UnknownVal(cty.String)}),
   235  			cty.ObjectVal(map[string]cty.Value{"greeting": cty.True}),
   236  		},
   237  		{
   238  			cty.SetVal([]cty.Value{
   239  				cty.ObjectVal(map[string]cty.Value{
   240  					"a": cty.UnknownVal(cty.String),
   241  				}),
   242  				cty.ObjectVal(map[string]cty.Value{
   243  					"a": cty.StringVal("known"),
   244  				}),
   245  			}),
   246  			cty.TupleVal([]cty.Value{
   247  				cty.EmptyObjectVal,
   248  				cty.ObjectVal(map[string]cty.Value{
   249  					"a": cty.True,
   250  				}),
   251  			}),
   252  		},
   253  		{
   254  			cty.SetVal([]cty.Value{
   255  				cty.MapValEmpty(cty.String),
   256  				cty.MapVal(map[string]cty.Value{
   257  					"a": cty.StringVal("known"),
   258  				}),
   259  				cty.MapVal(map[string]cty.Value{
   260  					"a": cty.UnknownVal(cty.String),
   261  				}),
   262  			}),
   263  			cty.TupleVal([]cty.Value{
   264  				cty.EmptyObjectVal,
   265  				cty.ObjectVal(map[string]cty.Value{
   266  					"a": cty.True,
   267  				}),
   268  				cty.EmptyObjectVal,
   269  			}),
   270  		},
   271  	}
   272  
   273  	for _, test := range tests {
   274  		got := unknownAsBool(test.Input)
   275  		if !reflect.DeepEqual(got, test.Want) {
   276  			t.Errorf(
   277  				"wrong result\ninput: %#v\ngot:   %#v\nwant:  %#v",
   278  				test.Input, got, test.Want,
   279  			)
   280  		}
   281  	}
   282  }
   283  
   284  func TestEncodePaths(t *testing.T) {
   285  	tests := map[string]struct {
   286  		Input cty.PathSet
   287  		Want  json.RawMessage
   288  	}{
   289  		"empty set": {
   290  			cty.NewPathSet(),
   291  			json.RawMessage(nil),
   292  		},
   293  		"index path with string and int steps": {
   294  			cty.NewPathSet(cty.IndexStringPath("boop").IndexInt(0)),
   295  			json.RawMessage(`[["boop",0]]`),
   296  		},
   297  		"get attr path with one step": {
   298  			cty.NewPathSet(cty.GetAttrPath("triggers")),
   299  			json.RawMessage(`[["triggers"]]`),
   300  		},
   301  		"multiple paths of different types": {
   302  			cty.NewPathSet(
   303  				cty.GetAttrPath("alpha").GetAttr("beta").GetAttr("gamma"),
   304  				cty.GetAttrPath("triggers").IndexString("name"),
   305  				cty.IndexIntPath(0).IndexInt(1).IndexInt(2).IndexInt(3),
   306  			),
   307  			json.RawMessage(`[["alpha","beta","gamma"],["triggers","name"],[0,1,2,3]]`),
   308  		},
   309  	}
   310  
   311  	for name, test := range tests {
   312  		t.Run(name, func(t *testing.T) {
   313  			got, err := encodePaths(test.Input)
   314  			if err != nil {
   315  				t.Fatalf("unexpected error: %s", err)
   316  			}
   317  			if !cmp.Equal(got, test.Want) {
   318  				t.Errorf("wrong result:\n %v\n", cmp.Diff(got, test.Want))
   319  			}
   320  		})
   321  	}
   322  }
   323  
   324  func TestOutputs(t *testing.T) {
   325  	root := addrs.RootModuleInstance
   326  
   327  	child, diags := addrs.ParseModuleInstanceStr("module.child")
   328  	if diags.HasErrors() {
   329  		t.Fatalf("unexpected errors: %s", diags.Err())
   330  	}
   331  
   332  	tests := map[string]struct {
   333  		changes  *plans.Changes
   334  		expected map[string]Change
   335  	}{
   336  		"copies all outputs": {
   337  			changes: &plans.Changes{
   338  				Outputs: []*plans.OutputChangeSrc{
   339  					{
   340  						Addr: root.OutputValue("first"),
   341  						ChangeSrc: plans.ChangeSrc{
   342  							Action: plans.Create,
   343  						},
   344  					},
   345  					{
   346  						Addr: root.OutputValue("second"),
   347  						ChangeSrc: plans.ChangeSrc{
   348  							Action: plans.Create,
   349  						},
   350  					},
   351  				},
   352  			},
   353  			expected: map[string]Change{
   354  				"first": {
   355  					Actions:         []string{"create"},
   356  					Before:          json.RawMessage("null"),
   357  					After:           json.RawMessage("null"),
   358  					AfterUnknown:    json.RawMessage("false"),
   359  					BeforeSensitive: json.RawMessage("false"),
   360  					AfterSensitive:  json.RawMessage("false"),
   361  				},
   362  				"second": {
   363  					Actions:         []string{"create"},
   364  					Before:          json.RawMessage("null"),
   365  					After:           json.RawMessage("null"),
   366  					AfterUnknown:    json.RawMessage("false"),
   367  					BeforeSensitive: json.RawMessage("false"),
   368  					AfterSensitive:  json.RawMessage("false"),
   369  				},
   370  			},
   371  		},
   372  		"skips non root modules": {
   373  			changes: &plans.Changes{
   374  				Outputs: []*plans.OutputChangeSrc{
   375  					{
   376  						Addr: root.OutputValue("first"),
   377  						ChangeSrc: plans.ChangeSrc{
   378  							Action: plans.Create,
   379  						},
   380  					},
   381  					{
   382  						Addr: child.OutputValue("second"),
   383  						ChangeSrc: plans.ChangeSrc{
   384  							Action: plans.Create,
   385  						},
   386  					},
   387  				},
   388  			},
   389  			expected: map[string]Change{
   390  				"first": {
   391  					Actions:         []string{"create"},
   392  					Before:          json.RawMessage("null"),
   393  					After:           json.RawMessage("null"),
   394  					AfterUnknown:    json.RawMessage("false"),
   395  					BeforeSensitive: json.RawMessage("false"),
   396  					AfterSensitive:  json.RawMessage("false"),
   397  				},
   398  			},
   399  		},
   400  	}
   401  	for name, test := range tests {
   402  		t.Run(name, func(t *testing.T) {
   403  			changes, err := MarshalOutputChanges(test.changes)
   404  			if err != nil {
   405  				t.Fatalf("unexpected err: %s", err)
   406  			}
   407  
   408  			if !cmp.Equal(changes, test.expected) {
   409  				t.Errorf("wrong result:\n %v\n", cmp.Diff(changes, test.expected))
   410  			}
   411  		})
   412  	}
   413  }
   414  
   415  func deepObjectValue(depth int) cty.Value {
   416  	v := cty.ObjectVal(map[string]cty.Value{
   417  		"a": cty.StringVal("a"),
   418  		"b": cty.NumberIntVal(2),
   419  		"c": cty.True,
   420  		"d": cty.UnknownVal(cty.String),
   421  	})
   422  
   423  	result := v
   424  
   425  	for i := 0; i < depth; i++ {
   426  		result = cty.ObjectVal(map[string]cty.Value{
   427  			"a": result,
   428  			"b": result,
   429  			"c": result,
   430  		})
   431  	}
   432  
   433  	return result
   434  }
   435  
   436  func BenchmarkUnknownAsBool_2(b *testing.B) {
   437  	value := deepObjectValue(2)
   438  	for n := 0; n < b.N; n++ {
   439  		unknownAsBool(value)
   440  	}
   441  }
   442  
   443  func BenchmarkUnknownAsBool_3(b *testing.B) {
   444  	value := deepObjectValue(3)
   445  	for n := 0; n < b.N; n++ {
   446  		unknownAsBool(value)
   447  	}
   448  }
   449  
   450  func BenchmarkUnknownAsBool_5(b *testing.B) {
   451  	value := deepObjectValue(5)
   452  	for n := 0; n < b.N; n++ {
   453  		unknownAsBool(value)
   454  	}
   455  }
   456  
   457  func BenchmarkUnknownAsBool_7(b *testing.B) {
   458  	value := deepObjectValue(7)
   459  	for n := 0; n < b.N; n++ {
   460  		unknownAsBool(value)
   461  	}
   462  }
   463  
   464  func BenchmarkUnknownAsBool_9(b *testing.B) {
   465  	value := deepObjectValue(9)
   466  	for n := 0; n < b.N; n++ {
   467  		unknownAsBool(value)
   468  	}
   469  }