github.com/hugorut/terraform@v1.1.3/src/lang/funcs/collection_test.go (about)

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