github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/lang/funcs/collection_test.go (about)

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