github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/lang/funcs/collection_test.go (about)

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