github.com/opentofu/opentofu@v1.7.1/internal/lang/funcs/collection_test.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package funcs
     7  
     8  import (
     9  	"fmt"
    10  	"math"
    11  	"testing"
    12  
    13  	"github.com/opentofu/opentofu/internal/lang/marks"
    14  	"github.com/zclconf/go-cty/cty"
    15  )
    16  
    17  func TestLength(t *testing.T) {
    18  	tests := []struct {
    19  		Value cty.Value
    20  		Want  cty.Value
    21  	}{
    22  		{
    23  			cty.ListValEmpty(cty.Number),
    24  			cty.NumberIntVal(0),
    25  		},
    26  		{
    27  			cty.ListVal([]cty.Value{cty.True}),
    28  			cty.NumberIntVal(1),
    29  		},
    30  		{
    31  			cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}),
    32  			cty.NumberIntVal(1),
    33  		},
    34  		{
    35  			cty.SetValEmpty(cty.Number),
    36  			cty.NumberIntVal(0),
    37  		},
    38  		{
    39  			cty.SetVal([]cty.Value{cty.True}),
    40  			cty.NumberIntVal(1),
    41  		},
    42  		{
    43  			cty.MapValEmpty(cty.Bool),
    44  			cty.NumberIntVal(0),
    45  		},
    46  		{
    47  			cty.MapVal(map[string]cty.Value{"hello": cty.True}),
    48  			cty.NumberIntVal(1),
    49  		},
    50  		{
    51  			cty.EmptyTupleVal,
    52  			cty.NumberIntVal(0),
    53  		},
    54  		{
    55  			cty.UnknownVal(cty.EmptyTuple),
    56  			cty.NumberIntVal(0),
    57  		},
    58  		{
    59  			cty.TupleVal([]cty.Value{cty.True}),
    60  			cty.NumberIntVal(1),
    61  		},
    62  		{
    63  			cty.EmptyObjectVal,
    64  			cty.NumberIntVal(0),
    65  		},
    66  		{
    67  			cty.UnknownVal(cty.EmptyObject),
    68  			cty.NumberIntVal(0),
    69  		},
    70  		{
    71  			cty.ObjectVal(map[string]cty.Value{"true": cty.True}),
    72  			cty.NumberIntVal(1),
    73  		},
    74  		{
    75  			cty.UnknownVal(cty.List(cty.Bool)),
    76  			cty.UnknownVal(cty.Number).Refine().
    77  				NotNull().
    78  				NumberRangeLowerBound(cty.Zero, true).
    79  				NumberRangeUpperBound(cty.NumberIntVal(math.MaxInt), true).
    80  				NewValue(),
    81  		},
    82  		{
    83  			cty.DynamicVal,
    84  			cty.UnknownVal(cty.Number).RefineNotNull(),
    85  		},
    86  		{
    87  			cty.StringVal("hello"),
    88  			cty.NumberIntVal(5),
    89  		},
    90  		{
    91  			cty.StringVal(""),
    92  			cty.NumberIntVal(0),
    93  		},
    94  		{
    95  			cty.StringVal("1"),
    96  			cty.NumberIntVal(1),
    97  		},
    98  		{
    99  			cty.StringVal("Живой Журнал"),
   100  			cty.NumberIntVal(12),
   101  		},
   102  		{
   103  			// note that the dieresis here is intentionally a combining
   104  			// ligature.
   105  			cty.StringVal("noël"),
   106  			cty.NumberIntVal(4),
   107  		},
   108  		{
   109  			// The Es in this string has three combining acute accents.
   110  			// This tests something that NFC-normalization cannot collapse
   111  			// into a single precombined codepoint, since otherwise we might
   112  			// be cheating and relying on the single-codepoint forms.
   113  			cty.StringVal("wé́́é́́é́́!"),
   114  			cty.NumberIntVal(5),
   115  		},
   116  		{
   117  			// Go's normalization forms don't handle this ligature, so we
   118  			// will produce the wrong result but this is now a compatibility
   119  			// constraint and so we'll test it.
   120  			cty.StringVal("baffle"),
   121  			cty.NumberIntVal(4),
   122  		},
   123  		{
   124  			cty.StringVal("😸😾"),
   125  			cty.NumberIntVal(2),
   126  		},
   127  		{
   128  			cty.UnknownVal(cty.String),
   129  			cty.UnknownVal(cty.Number).Refine().
   130  				NotNull().
   131  				NumberRangeLowerBound(cty.Zero, true).
   132  				NewValue(),
   133  		},
   134  		{ // Marked collections return a marked length
   135  			cty.ListVal([]cty.Value{
   136  				cty.StringVal("hello"),
   137  				cty.StringVal("world"),
   138  			}).Mark("secret"),
   139  			cty.NumberIntVal(2).Mark("secret"),
   140  		},
   141  		{ // Marks on values in unmarked collections do not propagate
   142  			cty.ListVal([]cty.Value{
   143  				cty.StringVal("hello").Mark("a"),
   144  				cty.StringVal("world").Mark("b"),
   145  			}),
   146  			cty.NumberIntVal(2),
   147  		},
   148  		{ // Marked strings return a marked length
   149  			cty.StringVal("hello world").Mark("secret"),
   150  			cty.NumberIntVal(11).Mark("secret"),
   151  		},
   152  		{ // Marked tuples return a marked length
   153  			cty.TupleVal([]cty.Value{
   154  				cty.StringVal("hello"),
   155  				cty.StringVal("world"),
   156  			}).Mark("secret"),
   157  			cty.NumberIntVal(2).Mark("secret"),
   158  		},
   159  		{ // Marks on values in unmarked tuples do not propagate
   160  			cty.TupleVal([]cty.Value{
   161  				cty.StringVal("hello").Mark("a"),
   162  				cty.StringVal("world").Mark("b"),
   163  			}),
   164  			cty.NumberIntVal(2),
   165  		},
   166  		{ // Marked objects return a marked length
   167  			cty.ObjectVal(map[string]cty.Value{
   168  				"a": cty.StringVal("hello"),
   169  				"b": cty.StringVal("world"),
   170  				"c": cty.StringVal("nice to meet you"),
   171  			}).Mark("secret"),
   172  			cty.NumberIntVal(3).Mark("secret"),
   173  		},
   174  		{ // Marks on object attribute values do not propagate
   175  			cty.ObjectVal(map[string]cty.Value{
   176  				"a": cty.StringVal("hello").Mark("a"),
   177  				"b": cty.StringVal("world").Mark("b"),
   178  				"c": cty.StringVal("nice to meet you").Mark("c"),
   179  			}),
   180  			cty.NumberIntVal(3),
   181  		},
   182  	}
   183  
   184  	for _, test := range tests {
   185  		t.Run(fmt.Sprintf("Length(%#v)", test.Value), func(t *testing.T) {
   186  			got, err := Length(test.Value)
   187  
   188  			if err != nil {
   189  				t.Fatalf("unexpected error: %s", err)
   190  			}
   191  
   192  			if !got.RawEquals(test.Want) {
   193  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   194  			}
   195  		})
   196  	}
   197  }
   198  
   199  func TestAllTrue(t *testing.T) {
   200  	tests := []struct {
   201  		Collection cty.Value
   202  		Want       cty.Value
   203  		Err        bool
   204  	}{
   205  		{
   206  			cty.ListValEmpty(cty.Bool),
   207  			cty.True,
   208  			false,
   209  		},
   210  		{
   211  			cty.ListVal([]cty.Value{cty.True}),
   212  			cty.True,
   213  			false,
   214  		},
   215  		{
   216  			cty.ListVal([]cty.Value{cty.False}),
   217  			cty.False,
   218  			false,
   219  		},
   220  		{
   221  			cty.ListVal([]cty.Value{cty.True, cty.False}),
   222  			cty.False,
   223  			false,
   224  		},
   225  		{
   226  			cty.ListVal([]cty.Value{cty.False, cty.True}),
   227  			cty.False,
   228  			false,
   229  		},
   230  		{
   231  			cty.ListVal([]cty.Value{cty.True, cty.NullVal(cty.Bool)}),
   232  			cty.False,
   233  			false,
   234  		},
   235  		{
   236  			cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}),
   237  			cty.UnknownVal(cty.Bool).RefineNotNull(),
   238  			false,
   239  		},
   240  		{
   241  			cty.ListVal([]cty.Value{
   242  				cty.UnknownVal(cty.Bool),
   243  				cty.UnknownVal(cty.Bool),
   244  			}),
   245  			cty.UnknownVal(cty.Bool).RefineNotNull(),
   246  			false,
   247  		},
   248  		{
   249  			cty.UnknownVal(cty.List(cty.Bool)),
   250  			cty.UnknownVal(cty.Bool).RefineNotNull(),
   251  			false,
   252  		},
   253  		{
   254  			cty.NullVal(cty.List(cty.Bool)),
   255  			cty.NilVal,
   256  			true,
   257  		},
   258  	}
   259  
   260  	for _, test := range tests {
   261  		t.Run(fmt.Sprintf("alltrue(%#v)", test.Collection), func(t *testing.T) {
   262  			got, err := AllTrue(test.Collection)
   263  
   264  			if test.Err {
   265  				if err == nil {
   266  					t.Fatal("succeeded; want error")
   267  				}
   268  				return
   269  			} else if err != nil {
   270  				t.Fatalf("unexpected error: %s", err)
   271  			}
   272  
   273  			if !got.RawEquals(test.Want) {
   274  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   275  			}
   276  		})
   277  	}
   278  }
   279  
   280  func TestAnyTrue(t *testing.T) {
   281  	tests := []struct {
   282  		Collection cty.Value
   283  		Want       cty.Value
   284  		Err        bool
   285  	}{
   286  		{
   287  			cty.ListValEmpty(cty.Bool),
   288  			cty.False,
   289  			false,
   290  		},
   291  		{
   292  			cty.ListVal([]cty.Value{cty.True}),
   293  			cty.True,
   294  			false,
   295  		},
   296  		{
   297  			cty.ListVal([]cty.Value{cty.False}),
   298  			cty.False,
   299  			false,
   300  		},
   301  		{
   302  			cty.ListVal([]cty.Value{cty.True, cty.False}),
   303  			cty.True,
   304  			false,
   305  		},
   306  		{
   307  			cty.ListVal([]cty.Value{cty.False, cty.True}),
   308  			cty.True,
   309  			false,
   310  		},
   311  		{
   312  			cty.ListVal([]cty.Value{cty.NullVal(cty.Bool), cty.True}),
   313  			cty.True,
   314  			false,
   315  		},
   316  		{
   317  			cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}),
   318  			cty.UnknownVal(cty.Bool).RefineNotNull(),
   319  			false,
   320  		},
   321  		{
   322  			cty.ListVal([]cty.Value{
   323  				cty.UnknownVal(cty.Bool),
   324  				cty.False,
   325  			}),
   326  			cty.UnknownVal(cty.Bool).RefineNotNull(),
   327  			false,
   328  		},
   329  		{
   330  			cty.ListVal([]cty.Value{
   331  				cty.UnknownVal(cty.Bool),
   332  				cty.True,
   333  			}),
   334  			cty.True,
   335  			false,
   336  		},
   337  		{
   338  			cty.UnknownVal(cty.List(cty.Bool)),
   339  			cty.UnknownVal(cty.Bool).RefineNotNull(),
   340  			false,
   341  		},
   342  		{
   343  			cty.NullVal(cty.List(cty.Bool)),
   344  			cty.NilVal,
   345  			true,
   346  		},
   347  	}
   348  
   349  	for _, test := range tests {
   350  		t.Run(fmt.Sprintf("anytrue(%#v)", test.Collection), func(t *testing.T) {
   351  			got, err := AnyTrue(test.Collection)
   352  
   353  			if test.Err {
   354  				if err == nil {
   355  					t.Fatal("succeeded; want error")
   356  				}
   357  				return
   358  			} else if err != nil {
   359  				t.Fatalf("unexpected error: %s", err)
   360  			}
   361  
   362  			if !got.RawEquals(test.Want) {
   363  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   364  			}
   365  		})
   366  	}
   367  }
   368  
   369  func TestCoalesce(t *testing.T) {
   370  	tests := []struct {
   371  		Values []cty.Value
   372  		Want   cty.Value
   373  		Err    bool
   374  	}{
   375  		{
   376  			[]cty.Value{cty.StringVal("first"), cty.StringVal("second"), cty.StringVal("third")},
   377  			cty.StringVal("first"),
   378  			false,
   379  		},
   380  		{
   381  			[]cty.Value{cty.StringVal(""), cty.StringVal("second"), cty.StringVal("third")},
   382  			cty.StringVal("second"),
   383  			false,
   384  		},
   385  		{
   386  			[]cty.Value{cty.StringVal(""), cty.StringVal("")},
   387  			cty.NilVal,
   388  			true,
   389  		},
   390  		{
   391  			[]cty.Value{cty.True},
   392  			cty.True,
   393  			false,
   394  		},
   395  		{
   396  			[]cty.Value{cty.NullVal(cty.Bool), cty.True},
   397  			cty.True,
   398  			false,
   399  		},
   400  		{
   401  			[]cty.Value{cty.NullVal(cty.Bool), cty.False},
   402  			cty.False,
   403  			false,
   404  		},
   405  		{
   406  			[]cty.Value{cty.NullVal(cty.Bool), cty.False, cty.StringVal("hello")},
   407  			cty.StringVal("false"),
   408  			false,
   409  		},
   410  		{
   411  			[]cty.Value{cty.True, cty.UnknownVal(cty.Bool)},
   412  			cty.True,
   413  			false,
   414  		},
   415  		{
   416  			[]cty.Value{cty.UnknownVal(cty.Bool), cty.True},
   417  			cty.UnknownVal(cty.Bool).RefineNotNull(),
   418  			false,
   419  		},
   420  		{
   421  			[]cty.Value{cty.UnknownVal(cty.Bool), cty.StringVal("hello")},
   422  			cty.UnknownVal(cty.String).RefineNotNull(),
   423  			false,
   424  		},
   425  		{
   426  			[]cty.Value{cty.DynamicVal, cty.True},
   427  			cty.UnknownVal(cty.Bool).RefineNotNull(),
   428  			false,
   429  		},
   430  		{
   431  			[]cty.Value{cty.DynamicVal},
   432  			cty.DynamicVal,
   433  			false,
   434  		},
   435  	}
   436  
   437  	for _, test := range tests {
   438  		t.Run(fmt.Sprintf("Coalesce(%#v...)", test.Values), func(t *testing.T) {
   439  			got, err := Coalesce(test.Values...)
   440  
   441  			if test.Err {
   442  				if err == nil {
   443  					t.Fatal("succeeded; want error")
   444  				}
   445  				return
   446  			} else if err != nil {
   447  				t.Fatalf("unexpected error: %s", err)
   448  			}
   449  
   450  			if !got.RawEquals(test.Want) {
   451  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   452  			}
   453  		})
   454  	}
   455  }
   456  
   457  func TestIndex(t *testing.T) {
   458  	tests := []struct {
   459  		List  cty.Value
   460  		Value cty.Value
   461  		Want  cty.Value
   462  		Err   bool
   463  	}{
   464  		{
   465  			cty.ListVal([]cty.Value{
   466  				cty.StringVal("a"),
   467  				cty.StringVal("b"),
   468  				cty.StringVal("c"),
   469  			}),
   470  			cty.StringVal("a"),
   471  			cty.NumberIntVal(0),
   472  			false,
   473  		},
   474  		{
   475  			cty.ListVal([]cty.Value{
   476  				cty.StringVal("a"),
   477  				cty.StringVal("b"),
   478  				cty.UnknownVal(cty.String),
   479  			}),
   480  			cty.StringVal("a"),
   481  			cty.NumberIntVal(0),
   482  			false,
   483  		},
   484  		{
   485  			cty.ListVal([]cty.Value{
   486  				cty.StringVal("a"),
   487  				cty.StringVal("b"),
   488  				cty.StringVal("c"),
   489  			}),
   490  			cty.StringVal("b"),
   491  			cty.NumberIntVal(1),
   492  			false,
   493  		},
   494  		{
   495  			cty.ListVal([]cty.Value{
   496  				cty.StringVal("a"),
   497  				cty.StringVal("b"),
   498  				cty.StringVal("c"),
   499  			}),
   500  			cty.StringVal("z"),
   501  			cty.NilVal,
   502  			true,
   503  		},
   504  		{
   505  			cty.ListVal([]cty.Value{
   506  				cty.StringVal("1"),
   507  				cty.StringVal("2"),
   508  				cty.StringVal("3"),
   509  			}),
   510  			cty.NumberIntVal(1),
   511  			cty.NumberIntVal(0),
   512  			true,
   513  		},
   514  		{
   515  			cty.ListVal([]cty.Value{
   516  				cty.NumberIntVal(1),
   517  				cty.NumberIntVal(2),
   518  				cty.NumberIntVal(3),
   519  			}),
   520  			cty.NumberIntVal(2),
   521  			cty.NumberIntVal(1),
   522  			false,
   523  		},
   524  		{
   525  			cty.ListVal([]cty.Value{
   526  				cty.NumberIntVal(1),
   527  				cty.NumberIntVal(2),
   528  				cty.NumberIntVal(3),
   529  			}),
   530  			cty.NumberIntVal(4),
   531  			cty.NilVal,
   532  			true,
   533  		},
   534  		{
   535  			cty.ListVal([]cty.Value{
   536  				cty.NumberIntVal(1),
   537  				cty.NumberIntVal(2),
   538  				cty.NumberIntVal(3),
   539  			}),
   540  			cty.StringVal("1"),
   541  			cty.NumberIntVal(0),
   542  			true,
   543  		},
   544  		{
   545  			cty.TupleVal([]cty.Value{
   546  				cty.NumberIntVal(1),
   547  				cty.NumberIntVal(2),
   548  				cty.NumberIntVal(3),
   549  			}),
   550  			cty.NumberIntVal(1),
   551  			cty.NumberIntVal(0),
   552  			false,
   553  		},
   554  	}
   555  
   556  	for _, test := range tests {
   557  		t.Run(fmt.Sprintf("index(%#v, %#v)", test.List, test.Value), func(t *testing.T) {
   558  			got, err := Index(test.List, test.Value)
   559  
   560  			if test.Err {
   561  				if err == nil {
   562  					t.Fatal("succeeded; want error")
   563  				}
   564  				return
   565  			} else if err != nil {
   566  				t.Fatalf("unexpected error: %s", err)
   567  			}
   568  
   569  			if !got.RawEquals(test.Want) {
   570  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   571  			}
   572  		})
   573  	}
   574  }
   575  
   576  func TestLookup(t *testing.T) {
   577  	simpleMap := cty.MapVal(map[string]cty.Value{
   578  		"foo": cty.StringVal("bar"),
   579  	})
   580  	intsMap := cty.MapVal(map[string]cty.Value{
   581  		"foo": cty.NumberIntVal(42),
   582  	})
   583  	mapOfLists := cty.MapVal(map[string]cty.Value{
   584  		"foo": cty.ListVal([]cty.Value{
   585  			cty.StringVal("bar"),
   586  			cty.StringVal("baz"),
   587  		}),
   588  	})
   589  	mapOfMaps := cty.MapVal(map[string]cty.Value{
   590  		"foo": cty.MapVal(map[string]cty.Value{
   591  			"a": cty.StringVal("bar"),
   592  		}),
   593  		"baz": cty.MapVal(map[string]cty.Value{
   594  			"b": cty.StringVal("bat"),
   595  		}),
   596  	})
   597  	mapOfTuples := cty.MapVal(map[string]cty.Value{
   598  		"foo": cty.TupleVal([]cty.Value{cty.StringVal("bar")}),
   599  		"baz": cty.TupleVal([]cty.Value{cty.StringVal("bat")}),
   600  	})
   601  	objectOfMaps := cty.ObjectVal(map[string]cty.Value{
   602  		"foo": cty.MapVal(map[string]cty.Value{
   603  			"a": cty.StringVal("bar"),
   604  		}),
   605  		"baz": cty.MapVal(map[string]cty.Value{
   606  			"b": cty.StringVal("bat"),
   607  		}),
   608  	})
   609  	mapWithUnknowns := cty.MapVal(map[string]cty.Value{
   610  		"foo": cty.StringVal("bar"),
   611  		"baz": cty.UnknownVal(cty.String),
   612  	})
   613  	mapWithObjects := cty.ObjectVal(map[string]cty.Value{
   614  		"foo": cty.StringVal("bar"),
   615  		"baz": cty.NumberIntVal(42),
   616  	})
   617  
   618  	tests := []struct {
   619  		Values []cty.Value
   620  		Want   cty.Value
   621  		Err    bool
   622  	}{
   623  		{
   624  			[]cty.Value{
   625  				simpleMap,
   626  				cty.StringVal("foo"),
   627  			},
   628  			cty.StringVal("bar"),
   629  			false,
   630  		},
   631  		{
   632  			[]cty.Value{
   633  				mapWithObjects,
   634  				cty.StringVal("foo"),
   635  			},
   636  			cty.StringVal("bar"),
   637  			false,
   638  		},
   639  		{
   640  			[]cty.Value{
   641  				intsMap,
   642  				cty.StringVal("foo"),
   643  			},
   644  			cty.NumberIntVal(42),
   645  			false,
   646  		},
   647  		{
   648  			[]cty.Value{
   649  				mapOfMaps,
   650  				cty.StringVal("foo"),
   651  			},
   652  			cty.MapVal(map[string]cty.Value{
   653  				"a": cty.StringVal("bar"),
   654  			}),
   655  			false,
   656  		},
   657  		{
   658  			[]cty.Value{
   659  				objectOfMaps,
   660  				cty.StringVal("foo"),
   661  			},
   662  			cty.MapVal(map[string]cty.Value{
   663  				"a": cty.StringVal("bar"),
   664  			}),
   665  			false,
   666  		},
   667  		{
   668  			[]cty.Value{
   669  				mapOfTuples,
   670  				cty.StringVal("foo"),
   671  			},
   672  			cty.TupleVal([]cty.Value{cty.StringVal("bar")}),
   673  			false,
   674  		},
   675  		{ // Invalid key
   676  			[]cty.Value{
   677  				simpleMap,
   678  				cty.StringVal("bar"),
   679  			},
   680  			cty.NilVal,
   681  			true,
   682  		},
   683  		{ // Invalid key
   684  			[]cty.Value{
   685  				mapWithObjects,
   686  				cty.StringVal("bar"),
   687  			},
   688  			cty.NilVal,
   689  			true,
   690  		},
   691  		{ // Supplied default with valid key
   692  			[]cty.Value{
   693  				simpleMap,
   694  				cty.StringVal("foo"),
   695  				cty.StringVal(""),
   696  			},
   697  			cty.StringVal("bar"),
   698  			false,
   699  		},
   700  		{ // Supplied default with valid (int) key
   701  			[]cty.Value{
   702  				simpleMap,
   703  				cty.StringVal("foo"),
   704  				cty.NumberIntVal(-1),
   705  			},
   706  			cty.StringVal("bar"),
   707  			false,
   708  		},
   709  		{ // Supplied default with valid (int) key
   710  			[]cty.Value{
   711  				simpleMap,
   712  				cty.StringVal("foobar"),
   713  				cty.NumberIntVal(-1),
   714  			},
   715  			cty.StringVal("-1"),
   716  			false,
   717  		},
   718  		{ // Supplied default with valid key
   719  			[]cty.Value{
   720  				mapWithObjects,
   721  				cty.StringVal("foobar"),
   722  				cty.StringVal(""),
   723  			},
   724  			cty.StringVal(""),
   725  			false,
   726  		},
   727  		{ // Supplied default with invalid key
   728  			[]cty.Value{
   729  				simpleMap,
   730  				cty.StringVal("baz"),
   731  				cty.StringVal(""),
   732  			},
   733  			cty.StringVal(""),
   734  			false,
   735  		},
   736  		{ // Supplied default with type mismatch: expects a map return
   737  			[]cty.Value{
   738  				mapOfMaps,
   739  				cty.StringVal("foo"),
   740  				cty.StringVal(""),
   741  			},
   742  			cty.NilVal,
   743  			true,
   744  		},
   745  		{ // Supplied non-empty default with invalid key
   746  			[]cty.Value{
   747  				simpleMap,
   748  				cty.StringVal("bar"),
   749  				cty.StringVal("xyz"),
   750  			},
   751  			cty.StringVal("xyz"),
   752  			false,
   753  		},
   754  		{ // too many args
   755  			[]cty.Value{
   756  				simpleMap,
   757  				cty.StringVal("foo"),
   758  				cty.StringVal("bar"),
   759  				cty.StringVal("baz"),
   760  			},
   761  			cty.NilVal,
   762  			true,
   763  		},
   764  		{ // cannot search a map of lists
   765  			[]cty.Value{
   766  				mapOfLists,
   767  				cty.StringVal("baz"),
   768  			},
   769  			cty.NilVal,
   770  			true,
   771  		},
   772  		{
   773  			[]cty.Value{
   774  				mapWithUnknowns,
   775  				cty.StringVal("baz"),
   776  			},
   777  			cty.UnknownVal(cty.String),
   778  			false,
   779  		},
   780  		{
   781  			[]cty.Value{
   782  				mapWithUnknowns,
   783  				cty.StringVal("foo"),
   784  			},
   785  			cty.StringVal("bar"),
   786  			false,
   787  		},
   788  		{
   789  			[]cty.Value{
   790  				simpleMap,
   791  				cty.UnknownVal(cty.String),
   792  			},
   793  			cty.UnknownVal(cty.String),
   794  			false,
   795  		},
   796  		{
   797  			[]cty.Value{
   798  				cty.ObjectVal(map[string]cty.Value{
   799  					"foo": cty.StringVal("a"),
   800  					"bar": cty.StringVal("b"),
   801  				}),
   802  				cty.UnknownVal(cty.String),
   803  			},
   804  			cty.DynamicVal, // if the key is unknown then we don't know which object attribute and thus can't know the type
   805  			false,
   806  		},
   807  		{ // successful marked collection lookup returns marked value
   808  			[]cty.Value{
   809  				cty.MapVal(map[string]cty.Value{
   810  					"boop": cty.StringVal("beep"),
   811  				}).Mark("a"),
   812  				cty.StringVal("boop"),
   813  				cty.StringVal("nope"),
   814  			},
   815  			cty.StringVal("beep").Mark("a"),
   816  			false,
   817  		},
   818  		{ // apply collection marks to unknown return vaue
   819  			[]cty.Value{
   820  				cty.MapVal(map[string]cty.Value{
   821  					"boop": cty.StringVal("beep"),
   822  					"frob": cty.UnknownVal(cty.String),
   823  				}).Mark("a"),
   824  				cty.StringVal("frob"),
   825  				cty.StringVal("nope"),
   826  			},
   827  			cty.UnknownVal(cty.String).Mark("a"),
   828  			false,
   829  		},
   830  		{ // propagate collection marks to default when returning
   831  			[]cty.Value{
   832  				cty.MapVal(map[string]cty.Value{
   833  					"boop": cty.StringVal("beep"),
   834  				}).Mark("a"),
   835  				cty.StringVal("frob"),
   836  				cty.StringVal("nope").Mark("b"),
   837  			},
   838  			cty.StringVal("nope").WithMarks(cty.NewValueMarks("a", "b")),
   839  			false,
   840  		},
   841  		{ // on unmarked collection, return only marks from found value
   842  			[]cty.Value{
   843  				cty.MapVal(map[string]cty.Value{
   844  					"boop": cty.StringVal("beep").Mark("a"),
   845  					"frob": cty.StringVal("honk").Mark("b"),
   846  				}),
   847  				cty.StringVal("frob"),
   848  				cty.StringVal("nope").Mark("c"),
   849  			},
   850  			cty.StringVal("honk").Mark("b"),
   851  			false,
   852  		},
   853  		{ // on unmarked collection, return default exactly on missing
   854  			[]cty.Value{
   855  				cty.MapVal(map[string]cty.Value{
   856  					"boop": cty.StringVal("beep").Mark("a"),
   857  					"frob": cty.StringVal("honk").Mark("b"),
   858  				}),
   859  				cty.StringVal("squish"),
   860  				cty.StringVal("nope").Mark("c"),
   861  			},
   862  			cty.StringVal("nope").Mark("c"),
   863  			false,
   864  		},
   865  		{ // retain marks on default if converted
   866  			[]cty.Value{
   867  				cty.MapVal(map[string]cty.Value{
   868  					"boop": cty.StringVal("beep").Mark("a"),
   869  					"frob": cty.StringVal("honk").Mark("b"),
   870  				}),
   871  				cty.StringVal("squish"),
   872  				cty.NumberIntVal(5).Mark("c"),
   873  			},
   874  			cty.StringVal("5").Mark("c"),
   875  			false,
   876  		},
   877  		{ // propagate marks from key
   878  			[]cty.Value{
   879  				cty.MapVal(map[string]cty.Value{
   880  					"boop": cty.StringVal("beep"),
   881  					"frob": cty.StringVal("honk"),
   882  				}),
   883  				cty.StringVal("boop").Mark("a"),
   884  				cty.StringVal("nope"),
   885  			},
   886  			cty.StringVal("beep").Mark("a"),
   887  			false,
   888  		},
   889  	}
   890  
   891  	for _, test := range tests {
   892  		t.Run(fmt.Sprintf("lookup(%#v)", test.Values), func(t *testing.T) {
   893  			got, err := Lookup(test.Values...)
   894  
   895  			if test.Err {
   896  				if err == nil {
   897  					t.Fatal("succeeded; want error")
   898  				}
   899  				return
   900  			} else if err != nil {
   901  				t.Fatalf("unexpected error: %s", err)
   902  			}
   903  
   904  			if !got.RawEquals(test.Want) {
   905  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   906  			}
   907  		})
   908  	}
   909  }
   910  
   911  func TestLookup_error(t *testing.T) {
   912  	simpleMap := cty.MapVal(map[string]cty.Value{
   913  		"foo": cty.StringVal("bar"),
   914  	})
   915  
   916  	tests := map[string]struct {
   917  		Values  []cty.Value
   918  		WantErr string
   919  	}{
   920  		"failed to find non-sensitive key": {
   921  			[]cty.Value{
   922  				simpleMap,
   923  				cty.StringVal("boop"),
   924  			},
   925  			`lookup failed to find key "boop"`,
   926  		},
   927  		"failed to find sensitive key": {
   928  			[]cty.Value{
   929  				simpleMap,
   930  				cty.StringVal("boop").Mark(marks.Sensitive),
   931  			},
   932  			"lookup failed to find key (sensitive value)",
   933  		},
   934  	}
   935  
   936  	for name, test := range tests {
   937  		t.Run(name, func(t *testing.T) {
   938  			_, err := Lookup(test.Values...)
   939  
   940  			if err == nil {
   941  				t.Fatal("succeeded; want error")
   942  			}
   943  
   944  			if err.Error() != test.WantErr {
   945  				t.Errorf("wrong error\ngot:  %#v\nwant: %#v", err, test.WantErr)
   946  			}
   947  		})
   948  	}
   949  }
   950  
   951  func TestMatchkeys(t *testing.T) {
   952  	tests := []struct {
   953  		Keys      cty.Value
   954  		Values    cty.Value
   955  		Searchset cty.Value
   956  		Want      cty.Value
   957  		Err       bool
   958  	}{
   959  		{ // normal usage
   960  			cty.ListVal([]cty.Value{
   961  				cty.StringVal("a"),
   962  				cty.StringVal("b"),
   963  				cty.StringVal("c"),
   964  			}),
   965  			cty.ListVal([]cty.Value{
   966  				cty.StringVal("ref1"),
   967  				cty.StringVal("ref2"),
   968  				cty.StringVal("ref3"),
   969  			}),
   970  			cty.ListVal([]cty.Value{
   971  				cty.StringVal("ref1"),
   972  			}),
   973  			cty.ListVal([]cty.Value{
   974  				cty.StringVal("a"),
   975  			}),
   976  			false,
   977  		},
   978  		{ // normal usage 2, check the order
   979  			cty.ListVal([]cty.Value{
   980  				cty.StringVal("a"),
   981  				cty.StringVal("b"),
   982  				cty.StringVal("c"),
   983  			}),
   984  			cty.ListVal([]cty.Value{
   985  				cty.StringVal("ref1"),
   986  				cty.StringVal("ref2"),
   987  				cty.StringVal("ref3"),
   988  			}),
   989  			cty.ListVal([]cty.Value{
   990  				cty.StringVal("ref2"),
   991  				cty.StringVal("ref1"),
   992  			}),
   993  			cty.ListVal([]cty.Value{
   994  				cty.StringVal("a"),
   995  				cty.StringVal("b"),
   996  			}),
   997  			false,
   998  		},
   999  		{ // no matches
  1000  			cty.ListVal([]cty.Value{
  1001  				cty.StringVal("a"),
  1002  				cty.StringVal("b"),
  1003  				cty.StringVal("c"),
  1004  			}),
  1005  			cty.ListVal([]cty.Value{
  1006  				cty.StringVal("ref1"),
  1007  				cty.StringVal("ref2"),
  1008  				cty.StringVal("ref3"),
  1009  			}),
  1010  			cty.ListVal([]cty.Value{
  1011  				cty.StringVal("ref4"),
  1012  			}),
  1013  			cty.ListValEmpty(cty.String),
  1014  			false,
  1015  		},
  1016  		{ // no matches 2
  1017  			cty.ListVal([]cty.Value{
  1018  				cty.StringVal("a"),
  1019  				cty.StringVal("b"),
  1020  				cty.StringVal("c"),
  1021  			}),
  1022  			cty.ListVal([]cty.Value{
  1023  				cty.StringVal("ref1"),
  1024  				cty.StringVal("ref2"),
  1025  				cty.StringVal("ref3"),
  1026  			}),
  1027  			cty.ListValEmpty(cty.String),
  1028  			cty.ListValEmpty(cty.String),
  1029  			false,
  1030  		},
  1031  		{ // zero case
  1032  			cty.ListValEmpty(cty.String),
  1033  			cty.ListValEmpty(cty.String),
  1034  			cty.ListVal([]cty.Value{cty.StringVal("nope")}),
  1035  			cty.ListValEmpty(cty.String),
  1036  			false,
  1037  		},
  1038  		{ // complex values
  1039  			cty.ListVal([]cty.Value{
  1040  				cty.ListVal([]cty.Value{
  1041  					cty.StringVal("a"),
  1042  					cty.StringVal("a"),
  1043  				}),
  1044  			}),
  1045  			cty.ListVal([]cty.Value{
  1046  				cty.StringVal("a"),
  1047  			}),
  1048  			cty.ListVal([]cty.Value{
  1049  				cty.StringVal("a"),
  1050  			}),
  1051  			cty.ListVal([]cty.Value{
  1052  				cty.ListVal([]cty.Value{
  1053  					cty.StringVal("a"),
  1054  					cty.StringVal("a"),
  1055  				}),
  1056  			}),
  1057  			false,
  1058  		},
  1059  		{ // unknowns
  1060  			cty.ListVal([]cty.Value{
  1061  				cty.StringVal("a"),
  1062  				cty.StringVal("b"),
  1063  				cty.UnknownVal(cty.String),
  1064  			}),
  1065  			cty.ListVal([]cty.Value{
  1066  				cty.StringVal("ref1"),
  1067  				cty.StringVal("ref2"),
  1068  				cty.UnknownVal(cty.String),
  1069  			}),
  1070  			cty.ListVal([]cty.Value{
  1071  				cty.StringVal("ref1"),
  1072  			}),
  1073  			cty.UnknownVal(cty.List(cty.String)).RefineNotNull(),
  1074  			false,
  1075  		},
  1076  		{ // different types that can be unified
  1077  			cty.ListVal([]cty.Value{
  1078  				cty.StringVal("a"),
  1079  			}),
  1080  			cty.ListVal([]cty.Value{
  1081  				cty.NumberIntVal(1),
  1082  			}),
  1083  			cty.ListVal([]cty.Value{
  1084  				cty.StringVal("a"),
  1085  			}),
  1086  			cty.ListValEmpty(cty.String),
  1087  			false,
  1088  		},
  1089  		{ // complex values: values is a different type from keys and searchset
  1090  			cty.ListVal([]cty.Value{
  1091  				cty.MapVal(map[string]cty.Value{
  1092  					"foo": cty.StringVal("bar"),
  1093  				}),
  1094  				cty.MapVal(map[string]cty.Value{
  1095  					"foo": cty.StringVal("baz"),
  1096  				}),
  1097  				cty.MapVal(map[string]cty.Value{
  1098  					"foo": cty.StringVal("beep"),
  1099  				}),
  1100  			}),
  1101  			cty.ListVal([]cty.Value{
  1102  				cty.StringVal("a"),
  1103  				cty.StringVal("b"),
  1104  				cty.StringVal("c"),
  1105  			}),
  1106  			cty.ListVal([]cty.Value{
  1107  				cty.StringVal("a"),
  1108  				cty.StringVal("c"),
  1109  			}),
  1110  			cty.ListVal([]cty.Value{
  1111  				cty.MapVal(map[string]cty.Value{
  1112  					"foo": cty.StringVal("bar"),
  1113  				}),
  1114  				cty.MapVal(map[string]cty.Value{
  1115  					"foo": cty.StringVal("beep"),
  1116  				}),
  1117  			}),
  1118  			false,
  1119  		},
  1120  		// errors
  1121  		{ // different types
  1122  			cty.ListVal([]cty.Value{
  1123  				cty.StringVal("a"),
  1124  			}),
  1125  			cty.ListVal([]cty.Value{
  1126  				cty.ListVal([]cty.Value{
  1127  					cty.StringVal("a"),
  1128  				}),
  1129  				cty.ListVal([]cty.Value{
  1130  					cty.StringVal("a"),
  1131  				}),
  1132  			}),
  1133  			cty.ListVal([]cty.Value{
  1134  				cty.StringVal("a"),
  1135  			}),
  1136  			cty.NilVal,
  1137  			true,
  1138  		},
  1139  		{ // lists of different length
  1140  			cty.ListVal([]cty.Value{
  1141  				cty.StringVal("a"),
  1142  			}),
  1143  			cty.ListVal([]cty.Value{
  1144  				cty.StringVal("a"),
  1145  				cty.StringVal("b"),
  1146  			}),
  1147  			cty.ListVal([]cty.Value{
  1148  				cty.StringVal("a"),
  1149  			}),
  1150  			cty.NilVal,
  1151  			true,
  1152  		},
  1153  	}
  1154  
  1155  	for _, test := range tests {
  1156  		t.Run(fmt.Sprintf("matchkeys(%#v, %#v, %#v)", test.Keys, test.Values, test.Searchset), func(t *testing.T) {
  1157  			got, err := Matchkeys(test.Keys, test.Values, test.Searchset)
  1158  
  1159  			if test.Err {
  1160  				if err == nil {
  1161  					t.Fatal("succeeded; want error")
  1162  				}
  1163  				return
  1164  			} else if err != nil {
  1165  				t.Fatalf("unexpected error: %s", err)
  1166  			}
  1167  
  1168  			if !got.RawEquals(test.Want) {
  1169  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
  1170  			}
  1171  		})
  1172  	}
  1173  }
  1174  
  1175  func TestOne(t *testing.T) {
  1176  	tests := []struct {
  1177  		List cty.Value
  1178  		Want cty.Value
  1179  		Err  string
  1180  	}{
  1181  		{
  1182  			cty.ListVal([]cty.Value{
  1183  				cty.NumberIntVal(1),
  1184  			}),
  1185  			cty.NumberIntVal(1),
  1186  			"",
  1187  		},
  1188  		{
  1189  			cty.ListValEmpty(cty.Number),
  1190  			cty.NullVal(cty.Number),
  1191  			"",
  1192  		},
  1193  		{
  1194  			cty.ListVal([]cty.Value{
  1195  				cty.NumberIntVal(1),
  1196  				cty.NumberIntVal(2),
  1197  				cty.NumberIntVal(3),
  1198  			}),
  1199  			cty.NilVal,
  1200  			"must be a list, set, or tuple value with either zero or one elements",
  1201  		},
  1202  		{
  1203  			cty.ListVal([]cty.Value{
  1204  				cty.UnknownVal(cty.Number),
  1205  			}),
  1206  			cty.UnknownVal(cty.Number),
  1207  			"",
  1208  		},
  1209  		{
  1210  			cty.ListVal([]cty.Value{
  1211  				cty.UnknownVal(cty.Number),
  1212  				cty.UnknownVal(cty.Number),
  1213  			}),
  1214  			cty.NilVal,
  1215  			"must be a list, set, or tuple value with either zero or one elements",
  1216  		},
  1217  		{
  1218  			cty.UnknownVal(cty.List(cty.String)),
  1219  			cty.UnknownVal(cty.String),
  1220  			"",
  1221  		},
  1222  		{
  1223  			cty.NullVal(cty.List(cty.String)),
  1224  			cty.NilVal,
  1225  			"argument must not be null",
  1226  		},
  1227  		{
  1228  			cty.ListVal([]cty.Value{
  1229  				cty.NumberIntVal(1),
  1230  			}).Mark("boop"),
  1231  			cty.NumberIntVal(1).Mark("boop"),
  1232  			"",
  1233  		},
  1234  		{
  1235  			cty.ListValEmpty(cty.Bool).Mark("boop"),
  1236  			cty.NullVal(cty.Bool).Mark("boop"),
  1237  			"",
  1238  		},
  1239  		{
  1240  			cty.ListVal([]cty.Value{
  1241  				cty.NumberIntVal(1).Mark("boop"),
  1242  			}),
  1243  			cty.NumberIntVal(1).Mark("boop"),
  1244  			"",
  1245  		},
  1246  
  1247  		{
  1248  			cty.SetVal([]cty.Value{
  1249  				cty.NumberIntVal(1),
  1250  			}),
  1251  			cty.NumberIntVal(1),
  1252  			"",
  1253  		},
  1254  		{
  1255  			cty.SetValEmpty(cty.Number),
  1256  			cty.NullVal(cty.Number),
  1257  			"",
  1258  		},
  1259  		{
  1260  			cty.SetVal([]cty.Value{
  1261  				cty.NumberIntVal(1),
  1262  				cty.NumberIntVal(2),
  1263  				cty.NumberIntVal(3),
  1264  			}),
  1265  			cty.NilVal,
  1266  			"must be a list, set, or tuple value with either zero or one elements",
  1267  		},
  1268  		{
  1269  			cty.SetVal([]cty.Value{
  1270  				cty.UnknownVal(cty.Number),
  1271  			}),
  1272  			cty.UnknownVal(cty.Number),
  1273  			"",
  1274  		},
  1275  		{
  1276  			cty.SetVal([]cty.Value{
  1277  				cty.UnknownVal(cty.Number),
  1278  				cty.UnknownVal(cty.Number),
  1279  			}),
  1280  			// The above would be valid if those two unknown values were
  1281  			// equal known values, so this returns unknown rather than failing.
  1282  			cty.UnknownVal(cty.Number),
  1283  			"",
  1284  		},
  1285  		{
  1286  			cty.UnknownVal(cty.Set(cty.String)),
  1287  			cty.UnknownVal(cty.String),
  1288  			"",
  1289  		},
  1290  		{
  1291  			cty.NullVal(cty.Set(cty.String)),
  1292  			cty.NilVal,
  1293  			"argument must not be null",
  1294  		},
  1295  		{
  1296  			cty.SetVal([]cty.Value{
  1297  				cty.NumberIntVal(1),
  1298  			}).Mark("boop"),
  1299  			cty.NumberIntVal(1).Mark("boop"),
  1300  			"",
  1301  		},
  1302  		{
  1303  			cty.SetValEmpty(cty.Bool).Mark("boop"),
  1304  			cty.NullVal(cty.Bool).Mark("boop"),
  1305  			"",
  1306  		},
  1307  		{
  1308  			cty.SetVal([]cty.Value{
  1309  				cty.NumberIntVal(1).Mark("boop"),
  1310  			}),
  1311  			cty.NumberIntVal(1).Mark("boop"),
  1312  			"",
  1313  		},
  1314  
  1315  		{
  1316  			cty.TupleVal([]cty.Value{
  1317  				cty.NumberIntVal(1),
  1318  			}),
  1319  			cty.NumberIntVal(1),
  1320  			"",
  1321  		},
  1322  		{
  1323  			cty.EmptyTupleVal,
  1324  			cty.NullVal(cty.DynamicPseudoType),
  1325  			"",
  1326  		},
  1327  		{
  1328  			cty.TupleVal([]cty.Value{
  1329  				cty.NumberIntVal(1),
  1330  				cty.NumberIntVal(2),
  1331  				cty.NumberIntVal(3),
  1332  			}),
  1333  			cty.NilVal,
  1334  			"must be a list, set, or tuple value with either zero or one elements",
  1335  		},
  1336  		{
  1337  			cty.TupleVal([]cty.Value{
  1338  				cty.UnknownVal(cty.Number),
  1339  			}),
  1340  			cty.UnknownVal(cty.Number),
  1341  			"",
  1342  		},
  1343  		{
  1344  			cty.TupleVal([]cty.Value{
  1345  				cty.UnknownVal(cty.Number),
  1346  				cty.UnknownVal(cty.Number),
  1347  			}),
  1348  			cty.NilVal,
  1349  			"must be a list, set, or tuple value with either zero or one elements",
  1350  		},
  1351  		{
  1352  			cty.UnknownVal(cty.EmptyTuple),
  1353  			// Could actually return null here, but don't for consistency with unknown lists
  1354  			cty.UnknownVal(cty.DynamicPseudoType),
  1355  			"",
  1356  		},
  1357  		{
  1358  			cty.UnknownVal(cty.Tuple([]cty.Type{cty.Bool})),
  1359  			cty.UnknownVal(cty.Bool),
  1360  			"",
  1361  		},
  1362  		{
  1363  			cty.UnknownVal(cty.Tuple([]cty.Type{cty.Bool, cty.Number})),
  1364  			cty.NilVal,
  1365  			"must be a list, set, or tuple value with either zero or one elements",
  1366  		},
  1367  		{
  1368  			cty.NullVal(cty.EmptyTuple),
  1369  			cty.NilVal,
  1370  			"argument must not be null",
  1371  		},
  1372  		{
  1373  			cty.NullVal(cty.Tuple([]cty.Type{cty.Bool})),
  1374  			cty.NilVal,
  1375  			"argument must not be null",
  1376  		},
  1377  		{
  1378  			cty.NullVal(cty.Tuple([]cty.Type{cty.Bool, cty.Number})),
  1379  			cty.NilVal,
  1380  			"argument must not be null",
  1381  		},
  1382  		{
  1383  			cty.TupleVal([]cty.Value{
  1384  				cty.NumberIntVal(1),
  1385  			}).Mark("boop"),
  1386  			cty.NumberIntVal(1).Mark("boop"),
  1387  			"",
  1388  		},
  1389  		{
  1390  			cty.EmptyTupleVal.Mark("boop"),
  1391  			cty.NullVal(cty.DynamicPseudoType).Mark("boop"),
  1392  			"",
  1393  		},
  1394  		{
  1395  			cty.TupleVal([]cty.Value{
  1396  				cty.NumberIntVal(1).Mark("boop"),
  1397  			}),
  1398  			cty.NumberIntVal(1).Mark("boop"),
  1399  			"",
  1400  		},
  1401  
  1402  		{
  1403  			cty.DynamicVal,
  1404  			cty.DynamicVal,
  1405  			"",
  1406  		},
  1407  		{
  1408  			cty.NullVal(cty.DynamicPseudoType),
  1409  			cty.NilVal,
  1410  			"argument must not be null",
  1411  		},
  1412  		{
  1413  			cty.MapValEmpty(cty.String),
  1414  			cty.NilVal,
  1415  			"must be a list, set, or tuple value with either zero or one elements",
  1416  		},
  1417  		{
  1418  			cty.EmptyObjectVal,
  1419  			cty.NilVal,
  1420  			"must be a list, set, or tuple value with either zero or one elements",
  1421  		},
  1422  		{
  1423  			cty.True,
  1424  			cty.NilVal,
  1425  			"must be a list, set, or tuple value with either zero or one elements",
  1426  		},
  1427  		{
  1428  			cty.UnknownVal(cty.Bool),
  1429  			cty.NilVal,
  1430  			"must be a list, set, or tuple value with either zero or one elements",
  1431  		},
  1432  	}
  1433  
  1434  	for _, test := range tests {
  1435  		t.Run(fmt.Sprintf("one(%#v)", test.List), func(t *testing.T) {
  1436  			got, err := One(test.List)
  1437  
  1438  			if test.Err != "" {
  1439  				if err == nil {
  1440  					t.Fatal("succeeded; want error")
  1441  				} else if got, want := err.Error(), test.Err; got != want {
  1442  					t.Fatalf("wrong error\n got: %s\nwant: %s", got, want)
  1443  				}
  1444  				return
  1445  			} else if err != nil {
  1446  				t.Fatalf("unexpected error: %s", err)
  1447  			}
  1448  
  1449  			if !test.Want.RawEquals(got) {
  1450  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
  1451  			}
  1452  		})
  1453  	}
  1454  }
  1455  
  1456  func TestSum(t *testing.T) {
  1457  	tests := []struct {
  1458  		List cty.Value
  1459  		Want cty.Value
  1460  		Err  string
  1461  	}{
  1462  		{
  1463  			cty.ListVal([]cty.Value{
  1464  				cty.NumberIntVal(1),
  1465  				cty.NumberIntVal(2),
  1466  				cty.NumberIntVal(3),
  1467  			}),
  1468  			cty.NumberIntVal(6),
  1469  			"",
  1470  		},
  1471  		{
  1472  			cty.ListVal([]cty.Value{
  1473  				cty.NumberIntVal(1476),
  1474  				cty.NumberIntVal(2093),
  1475  				cty.NumberIntVal(2092495),
  1476  				cty.NumberIntVal(64589234),
  1477  				cty.NumberIntVal(234),
  1478  			}),
  1479  			cty.NumberIntVal(66685532),
  1480  			"",
  1481  		},
  1482  		{
  1483  			cty.ListVal([]cty.Value{
  1484  				cty.StringVal("a"),
  1485  				cty.StringVal("b"),
  1486  				cty.StringVal("c"),
  1487  			}),
  1488  			cty.UnknownVal(cty.String),
  1489  			"argument must be list, set, or tuple of number values",
  1490  		},
  1491  		{
  1492  			cty.ListVal([]cty.Value{
  1493  				cty.NumberIntVal(10),
  1494  				cty.NumberIntVal(-19),
  1495  				cty.NumberIntVal(5),
  1496  			}),
  1497  			cty.NumberIntVal(-4),
  1498  			"",
  1499  		},
  1500  		{
  1501  			cty.ListVal([]cty.Value{
  1502  				cty.NumberFloatVal(10.2),
  1503  				cty.NumberFloatVal(19.4),
  1504  				cty.NumberFloatVal(5.7),
  1505  			}),
  1506  			cty.NumberFloatVal(35.3),
  1507  			"",
  1508  		},
  1509  		{
  1510  			cty.ListVal([]cty.Value{
  1511  				cty.NumberFloatVal(-10.2),
  1512  				cty.NumberFloatVal(-19.4),
  1513  				cty.NumberFloatVal(-5.7),
  1514  			}),
  1515  			cty.NumberFloatVal(-35.3),
  1516  			"",
  1517  		},
  1518  		{
  1519  			cty.ListVal([]cty.Value{cty.NullVal(cty.Number)}),
  1520  			cty.NilVal,
  1521  			"argument must be list, set, or tuple of number values",
  1522  		},
  1523  		{
  1524  			cty.ListVal([]cty.Value{
  1525  				cty.NumberIntVal(5),
  1526  				cty.NullVal(cty.Number),
  1527  			}),
  1528  			cty.NilVal,
  1529  			"argument must be list, set, or tuple of number values",
  1530  		},
  1531  		{
  1532  			cty.SetVal([]cty.Value{
  1533  				cty.StringVal("a"),
  1534  				cty.StringVal("b"),
  1535  				cty.StringVal("c"),
  1536  			}),
  1537  			cty.UnknownVal(cty.String).RefineNotNull(),
  1538  			"argument must be list, set, or tuple of number values",
  1539  		},
  1540  		{
  1541  			cty.SetVal([]cty.Value{
  1542  				cty.NumberIntVal(10),
  1543  				cty.NumberIntVal(-19),
  1544  				cty.NumberIntVal(5),
  1545  			}),
  1546  			cty.NumberIntVal(-4),
  1547  			"",
  1548  		},
  1549  		{
  1550  			cty.SetVal([]cty.Value{
  1551  				cty.NumberIntVal(10),
  1552  				cty.NumberIntVal(25),
  1553  				cty.NumberIntVal(30),
  1554  			}),
  1555  			cty.NumberIntVal(65),
  1556  			"",
  1557  		},
  1558  		{
  1559  			cty.SetVal([]cty.Value{
  1560  				cty.NumberFloatVal(2340.8),
  1561  				cty.NumberFloatVal(10.2),
  1562  				cty.NumberFloatVal(3),
  1563  			}),
  1564  			cty.NumberFloatVal(2354),
  1565  			"",
  1566  		},
  1567  		{
  1568  			cty.SetVal([]cty.Value{
  1569  				cty.NumberFloatVal(2),
  1570  			}),
  1571  			cty.NumberFloatVal(2),
  1572  			"",
  1573  		},
  1574  		{
  1575  			cty.SetVal([]cty.Value{
  1576  				cty.NumberFloatVal(-2),
  1577  				cty.NumberFloatVal(-50),
  1578  				cty.NumberFloatVal(-20),
  1579  				cty.NumberFloatVal(-123),
  1580  				cty.NumberFloatVal(-4),
  1581  			}),
  1582  			cty.NumberFloatVal(-199),
  1583  			"",
  1584  		},
  1585  		{
  1586  			cty.TupleVal([]cty.Value{
  1587  				cty.NumberIntVal(12),
  1588  				cty.StringVal("a"),
  1589  				cty.NumberIntVal(38),
  1590  			}),
  1591  			cty.UnknownVal(cty.String).RefineNotNull(),
  1592  			"argument must be list, set, or tuple of number values",
  1593  		},
  1594  		{
  1595  			cty.NumberIntVal(12),
  1596  			cty.NilVal,
  1597  			"cannot sum noniterable",
  1598  		},
  1599  		{
  1600  			cty.ListValEmpty(cty.Number),
  1601  			cty.NilVal,
  1602  			"cannot sum an empty list",
  1603  		},
  1604  		{
  1605  			cty.MapVal(map[string]cty.Value{"hello": cty.True}),
  1606  			cty.NilVal,
  1607  			"argument must be list, set, or tuple. Received map of bool",
  1608  		},
  1609  		{
  1610  			cty.UnknownVal(cty.Number),
  1611  			cty.UnknownVal(cty.Number).RefineNotNull(),
  1612  			"",
  1613  		},
  1614  		{
  1615  			cty.UnknownVal(cty.List(cty.Number)),
  1616  			cty.UnknownVal(cty.Number).RefineNotNull(),
  1617  			"",
  1618  		},
  1619  		{ // known list containing unknown values
  1620  			cty.ListVal([]cty.Value{cty.UnknownVal(cty.Number)}),
  1621  			cty.UnknownVal(cty.Number).RefineNotNull(),
  1622  			"",
  1623  		},
  1624  		{ // numbers too large to represent as float64
  1625  			cty.ListVal([]cty.Value{
  1626  				cty.MustParseNumberVal("1e+500"),
  1627  				cty.MustParseNumberVal("1e+500"),
  1628  			}),
  1629  			cty.MustParseNumberVal("2e+500"),
  1630  			"",
  1631  		},
  1632  		{ // edge case we have a special error handler for
  1633  			cty.ListVal([]cty.Value{
  1634  				cty.NumberFloatVal(math.Inf(1)),
  1635  				cty.NumberFloatVal(math.Inf(-1)),
  1636  			}),
  1637  			cty.NilVal,
  1638  			"can't compute sum of opposing infinities",
  1639  		},
  1640  		{
  1641  			cty.ListVal([]cty.Value{
  1642  				cty.StringVal("1"),
  1643  				cty.StringVal("2"),
  1644  				cty.StringVal("3"),
  1645  			}),
  1646  			cty.NumberIntVal(6),
  1647  			"",
  1648  		},
  1649  	}
  1650  
  1651  	for _, test := range tests {
  1652  		t.Run(fmt.Sprintf("sum(%#v)", test.List), func(t *testing.T) {
  1653  			got, err := Sum(test.List)
  1654  
  1655  			if test.Err != "" {
  1656  				if err == nil {
  1657  					t.Fatal("succeeded; want error")
  1658  				} else if got, want := err.Error(), test.Err; got != want {
  1659  					t.Fatalf("wrong error\n got: %s\nwant: %s", got, want)
  1660  				}
  1661  				return
  1662  			} else if err != nil {
  1663  				t.Fatalf("unexpected error: %s", err)
  1664  			}
  1665  
  1666  			if !got.RawEquals(test.Want) {
  1667  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
  1668  			}
  1669  		})
  1670  	}
  1671  }
  1672  
  1673  func TestTranspose(t *testing.T) {
  1674  	tests := []struct {
  1675  		Values cty.Value
  1676  		Want   cty.Value
  1677  		Err    bool
  1678  	}{
  1679  		{
  1680  			cty.MapVal(map[string]cty.Value{
  1681  				"key1": cty.ListVal([]cty.Value{
  1682  					cty.StringVal("a"),
  1683  					cty.StringVal("b"),
  1684  				}),
  1685  				"key2": cty.ListVal([]cty.Value{
  1686  					cty.StringVal("a"),
  1687  					cty.StringVal("b"),
  1688  					cty.StringVal("c"),
  1689  				}),
  1690  				"key3": cty.ListVal([]cty.Value{
  1691  					cty.StringVal("c"),
  1692  				}),
  1693  				"key4": cty.ListValEmpty(cty.String),
  1694  			}),
  1695  			cty.MapVal(map[string]cty.Value{
  1696  				"a": cty.ListVal([]cty.Value{
  1697  					cty.StringVal("key1"),
  1698  					cty.StringVal("key2"),
  1699  				}),
  1700  				"b": cty.ListVal([]cty.Value{
  1701  					cty.StringVal("key1"),
  1702  					cty.StringVal("key2"),
  1703  				}),
  1704  				"c": cty.ListVal([]cty.Value{
  1705  					cty.StringVal("key2"),
  1706  					cty.StringVal("key3"),
  1707  				}),
  1708  			}),
  1709  			false,
  1710  		},
  1711  		{ // map - unknown value
  1712  			cty.MapVal(map[string]cty.Value{
  1713  				"key1": cty.UnknownVal(cty.List(cty.String)),
  1714  			}),
  1715  			cty.UnknownVal(cty.Map(cty.List(cty.String))).RefineNotNull(),
  1716  			false,
  1717  		},
  1718  		{ // bad map - empty value
  1719  			cty.MapVal(map[string]cty.Value{
  1720  				"key1": cty.ListValEmpty(cty.String),
  1721  			}),
  1722  			cty.MapValEmpty(cty.List(cty.String)),
  1723  			false,
  1724  		},
  1725  		{ // bad map - value not a list
  1726  			cty.MapVal(map[string]cty.Value{
  1727  				"key1": cty.StringVal("a"),
  1728  			}),
  1729  			cty.NilVal,
  1730  			true,
  1731  		},
  1732  		{ // marks (deep or shallow) on any elements will propegate to the entire return value
  1733  			cty.MapVal(map[string]cty.Value{
  1734  				"key1": cty.ListVal([]cty.Value{
  1735  					cty.StringVal("a").Mark("beep"), // mark on the inner list element
  1736  					cty.StringVal("b"),
  1737  				}),
  1738  				"key2": cty.ListVal([]cty.Value{
  1739  					cty.StringVal("a"),
  1740  					cty.StringVal("b"),
  1741  					cty.StringVal("c"),
  1742  				}).Mark("boop"), // mark on the map element
  1743  				"key3": cty.ListVal([]cty.Value{
  1744  					cty.StringVal("c"),
  1745  				}),
  1746  				"key4": cty.ListValEmpty(cty.String),
  1747  			}),
  1748  			cty.MapVal(map[string]cty.Value{
  1749  				"a": cty.ListVal([]cty.Value{
  1750  					cty.StringVal("key1"),
  1751  					cty.StringVal("key2"),
  1752  				}),
  1753  				"b": cty.ListVal([]cty.Value{
  1754  					cty.StringVal("key1"),
  1755  					cty.StringVal("key2"),
  1756  				}),
  1757  				"c": cty.ListVal([]cty.Value{
  1758  					cty.StringVal("key2"),
  1759  					cty.StringVal("key3")}),
  1760  			}).WithMarks(cty.NewValueMarks("beep", "boop")),
  1761  			false,
  1762  		},
  1763  		{ // Marks on the input value will be applied to the return value
  1764  			cty.MapVal(map[string]cty.Value{
  1765  				"key1": cty.ListVal([]cty.Value{
  1766  					cty.StringVal("a"),
  1767  					cty.StringVal("b"),
  1768  				}),
  1769  				"key2": cty.ListVal([]cty.Value{
  1770  					cty.StringVal("a"),
  1771  					cty.StringVal("b"),
  1772  					cty.StringVal("c"),
  1773  				}),
  1774  				"key3": cty.ListVal([]cty.Value{
  1775  					cty.StringVal("c"),
  1776  				}),
  1777  			}).Mark("beep"), // mark on the entire input value
  1778  			cty.MapVal(map[string]cty.Value{
  1779  				"a": cty.ListVal([]cty.Value{
  1780  					cty.StringVal("key1"),
  1781  					cty.StringVal("key2"),
  1782  				}),
  1783  				"b": cty.ListVal([]cty.Value{
  1784  					cty.StringVal("key1"),
  1785  					cty.StringVal("key2"),
  1786  				}),
  1787  				"c": cty.ListVal([]cty.Value{
  1788  					cty.StringVal("key2"),
  1789  					cty.StringVal("key3"),
  1790  				}),
  1791  			}).Mark("beep"),
  1792  			false,
  1793  		},
  1794  		{ // Marks on the entire input value AND inner elements (deep or shallow) ALL apply to the return
  1795  			cty.MapVal(map[string]cty.Value{
  1796  				"key1": cty.ListVal([]cty.Value{
  1797  					cty.StringVal("a"),
  1798  					cty.StringVal("b"),
  1799  				}).Mark("beep"), // mark on the map element
  1800  				"key2": cty.ListVal([]cty.Value{
  1801  					cty.StringVal("a"),
  1802  					cty.StringVal("b"),
  1803  					cty.StringVal("c"),
  1804  				}),
  1805  				"key3": cty.ListVal([]cty.Value{
  1806  					cty.StringVal("c").Mark("boop"), // mark on the inner list element
  1807  				}),
  1808  			}).Mark("bloop"), // mark on the entire input value
  1809  			cty.MapVal(map[string]cty.Value{
  1810  				"a": cty.ListVal([]cty.Value{
  1811  					cty.StringVal("key1"),
  1812  					cty.StringVal("key2"),
  1813  				}),
  1814  				"b": cty.ListVal([]cty.Value{
  1815  					cty.StringVal("key1"),
  1816  					cty.StringVal("key2"),
  1817  				}),
  1818  				"c": cty.ListVal([]cty.Value{
  1819  					cty.StringVal("key2"),
  1820  					cty.StringVal("key3"),
  1821  				}),
  1822  			}).WithMarks(cty.NewValueMarks("beep", "boop", "bloop")),
  1823  			false,
  1824  		},
  1825  	}
  1826  
  1827  	for _, test := range tests {
  1828  		t.Run(fmt.Sprintf("transpose(%#v)", test.Values), func(t *testing.T) {
  1829  			got, err := Transpose(test.Values)
  1830  
  1831  			if test.Err {
  1832  				if err == nil {
  1833  					t.Fatal("succeeded; want error")
  1834  				}
  1835  				return
  1836  			} else if err != nil {
  1837  				t.Fatalf("unexpected error: %s", err)
  1838  			}
  1839  
  1840  			if !got.RawEquals(test.Want) {
  1841  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
  1842  			}
  1843  		})
  1844  	}
  1845  }