1package stdlib
2
3import (
4	"fmt"
5	"testing"
6
7	"github.com/zclconf/go-cty/cty"
8)
9
10func TestHasIndex(t *testing.T) {
11	tests := []struct {
12		Collection cty.Value
13		Key        cty.Value
14		Want       cty.Value
15	}{
16		{
17			cty.ListValEmpty(cty.Number),
18			cty.NumberIntVal(2),
19			cty.False,
20		},
21		{
22			cty.ListVal([]cty.Value{cty.True}),
23			cty.NumberIntVal(0),
24			cty.True,
25		},
26		{
27			cty.ListVal([]cty.Value{cty.True}),
28			cty.StringVal("hello"),
29			cty.False,
30		},
31		{
32			cty.MapValEmpty(cty.Bool),
33			cty.StringVal("hello"),
34			cty.False,
35		},
36		{
37			cty.MapVal(map[string]cty.Value{"hello": cty.True}),
38			cty.StringVal("hello"),
39			cty.True,
40		},
41		{
42			cty.EmptyTupleVal,
43			cty.StringVal("hello"),
44			cty.False,
45		},
46		{
47			cty.EmptyTupleVal,
48			cty.NumberIntVal(0),
49			cty.False,
50		},
51		{
52			cty.TupleVal([]cty.Value{cty.True}),
53			cty.NumberIntVal(0),
54			cty.True,
55		},
56		{
57			cty.ListValEmpty(cty.Number),
58			cty.UnknownVal(cty.Number),
59			cty.UnknownVal(cty.Bool),
60		},
61		{
62			cty.UnknownVal(cty.List(cty.Bool)),
63			cty.UnknownVal(cty.Number),
64			cty.UnknownVal(cty.Bool),
65		},
66		{
67			cty.ListValEmpty(cty.Number),
68			cty.DynamicVal,
69			cty.UnknownVal(cty.Bool),
70		},
71		{
72			cty.DynamicVal,
73			cty.DynamicVal,
74			cty.UnknownVal(cty.Bool),
75		},
76	}
77
78	for _, test := range tests {
79		t.Run(fmt.Sprintf("HasIndex(%#v,%#v)", test.Collection, test.Key), func(t *testing.T) {
80			got, err := HasIndex(test.Collection, test.Key)
81
82			if err != nil {
83				t.Fatalf("unexpected error: %s", err)
84			}
85
86			if !got.RawEquals(test.Want) {
87				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
88			}
89		})
90	}
91}
92
93func TestContains(t *testing.T) {
94	listOfStrings := cty.ListVal([]cty.Value{
95		cty.StringVal("the"),
96		cty.StringVal("quick"),
97		cty.StringVal("brown"),
98		cty.StringVal("fox"),
99	})
100	listOfInts := cty.ListVal([]cty.Value{
101		cty.NumberIntVal(1),
102		cty.NumberIntVal(2),
103		cty.NumberIntVal(3),
104		cty.NumberIntVal(4),
105	})
106	listWithUnknown := cty.ListVal([]cty.Value{
107		cty.StringVal("the"),
108		cty.StringVal("quick"),
109		cty.StringVal("brown"),
110		cty.UnknownVal(cty.String),
111	})
112
113	tests := []struct {
114		List  cty.Value
115		Value cty.Value
116		Want  cty.Value
117		Err   bool
118	}{
119		{
120			listOfStrings,
121			cty.StringVal("the"),
122			cty.BoolVal(true),
123			false,
124		},
125		{
126			listWithUnknown,
127			cty.StringVal("the"),
128			cty.BoolVal(true),
129			false,
130		},
131		{
132			listWithUnknown,
133			cty.StringVal("orange"),
134			cty.UnknownVal(cty.Bool),
135			false,
136		},
137		{
138			listOfStrings,
139			cty.StringVal("penguin"),
140			cty.BoolVal(false),
141			false,
142		},
143		{
144			listOfInts,
145			cty.NumberIntVal(1),
146			cty.BoolVal(true),
147			false,
148		},
149		{
150			listOfInts,
151			cty.NumberIntVal(42),
152			cty.BoolVal(false),
153			false,
154		},
155		{ // And now we mix and match
156			listOfInts,
157			cty.StringVal("1"),
158			cty.BoolVal(false),
159			false,
160		},
161		{ // Check a list with an unknown value
162			cty.ListVal([]cty.Value{
163				cty.UnknownVal(cty.String),
164				cty.StringVal("quick"),
165				cty.StringVal("brown"),
166				cty.StringVal("fox"),
167			}),
168			cty.StringVal("quick"),
169			cty.BoolVal(true),
170			false,
171		},
172		{
173			cty.ListVal([]cty.Value{
174				cty.UnknownVal(cty.String),
175				cty.StringVal("brown"),
176				cty.StringVal("fox"),
177			}),
178			cty.StringVal("quick"),
179			cty.UnknownVal(cty.Bool),
180			false,
181		},
182		{ // set val
183			cty.SetVal([]cty.Value{
184				cty.StringVal("quick"),
185				cty.StringVal("brown"),
186				cty.StringVal("fox"),
187			}),
188			cty.StringVal("quick"),
189			cty.BoolVal(true),
190			false,
191		},
192		{
193			cty.SetVal([]cty.Value{
194				cty.UnknownVal(cty.String),
195				cty.StringVal("brown"),
196				cty.StringVal("fox"),
197			}),
198			cty.StringVal("quick"),
199			cty.UnknownVal(cty.Bool),
200			false,
201		},
202		{ // nested unknown
203			cty.ListVal([]cty.Value{
204				cty.ObjectVal(map[string]cty.Value{
205					"a": cty.UnknownVal(cty.String),
206				}),
207			}),
208			cty.ObjectVal(map[string]cty.Value{
209				"a": cty.StringVal("b"),
210			}),
211			cty.UnknownVal(cty.Bool),
212			false,
213		},
214		{ // tuple val
215			cty.TupleVal([]cty.Value{
216				cty.StringVal("quick"),
217				cty.StringVal("brown"),
218				cty.NumberIntVal(3),
219			}),
220			cty.NumberIntVal(3),
221			cty.BoolVal(true),
222			false,
223		},
224	}
225
226	for _, test := range tests {
227		t.Run(fmt.Sprintf("contains(%#v, %#v)", test.List, test.Value), func(t *testing.T) {
228			got, err := Contains(test.List, test.Value)
229
230			if test.Err {
231				if err == nil {
232					t.Fatal("succeeded; want error")
233				}
234				return
235			} else if err != nil {
236				t.Fatalf("unexpected error: %s", err)
237			}
238
239			if !got.RawEquals(test.Want) {
240				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
241			}
242		})
243	}
244}
245
246func TestMerge(t *testing.T) {
247	tests := []struct {
248		Values []cty.Value
249		Want   cty.Value
250		Err    bool
251	}{
252		{
253			[]cty.Value{
254				cty.MapVal(map[string]cty.Value{
255					"a": cty.StringVal("b"),
256				}),
257				cty.MapVal(map[string]cty.Value{
258					"c": cty.StringVal("d"),
259				}),
260			},
261			cty.MapVal(map[string]cty.Value{
262				"a": cty.StringVal("b"),
263				"c": cty.StringVal("d"),
264			}),
265			false,
266		},
267		{ // handle unknowns
268			[]cty.Value{
269				cty.MapVal(map[string]cty.Value{
270					"a": cty.UnknownVal(cty.String),
271				}),
272				cty.MapVal(map[string]cty.Value{
273					"c": cty.StringVal("d"),
274				}),
275			},
276			cty.MapVal(map[string]cty.Value{
277				"a": cty.UnknownVal(cty.String),
278				"c": cty.StringVal("d"),
279			}),
280			false,
281		},
282		{ // handle null map
283			[]cty.Value{
284				cty.NullVal(cty.Map(cty.String)),
285				cty.MapVal(map[string]cty.Value{
286					"c": cty.StringVal("d"),
287				}),
288			},
289			cty.MapVal(map[string]cty.Value{
290				"c": cty.StringVal("d"),
291			}),
292			false,
293		},
294		{ // all inputs are null
295			[]cty.Value{
296				cty.NullVal(cty.Map(cty.String)),
297				cty.NullVal(cty.Object(map[string]cty.Type{
298					"a": cty.List(cty.String),
299				})),
300			},
301			cty.EmptyObjectVal,
302			false,
303		},
304		{ // single null input
305			[]cty.Value{
306				cty.MapValEmpty(cty.String),
307			},
308			cty.MapValEmpty(cty.String),
309			false,
310		},
311		{ // handle null object
312			[]cty.Value{
313				cty.MapVal(map[string]cty.Value{
314					"c": cty.StringVal("d"),
315				}),
316				cty.NullVal(cty.Object(map[string]cty.Type{
317					"a": cty.List(cty.String),
318				})),
319			},
320			cty.ObjectVal(map[string]cty.Value{
321				"c": cty.StringVal("d"),
322			}),
323			false,
324		},
325		{ // handle unknowns
326			[]cty.Value{
327				cty.UnknownVal(cty.Map(cty.String)),
328				cty.MapVal(map[string]cty.Value{
329					"c": cty.StringVal("d"),
330				}),
331			},
332			cty.UnknownVal(cty.Map(cty.String)),
333			false,
334		},
335		{ // handle dynamic unknown
336			[]cty.Value{
337				cty.UnknownVal(cty.DynamicPseudoType),
338				cty.MapVal(map[string]cty.Value{
339					"c": cty.StringVal("d"),
340				}),
341			},
342			cty.DynamicVal,
343			false,
344		},
345		{ // merge with conflicts is ok, last in wins
346			[]cty.Value{
347				cty.MapVal(map[string]cty.Value{
348					"a": cty.StringVal("b"),
349					"c": cty.StringVal("d"),
350				}),
351				cty.MapVal(map[string]cty.Value{
352					"a": cty.StringVal("x"),
353				}),
354			},
355			cty.MapVal(map[string]cty.Value{
356				"a": cty.StringVal("x"),
357				"c": cty.StringVal("d"),
358			}),
359			false,
360		},
361		{ // only accept maps
362			[]cty.Value{
363				cty.MapVal(map[string]cty.Value{
364					"a": cty.StringVal("b"),
365					"c": cty.StringVal("d"),
366				}),
367				cty.ListVal([]cty.Value{
368					cty.StringVal("a"),
369					cty.StringVal("x"),
370				}),
371			},
372			cty.NilVal,
373			true,
374		},
375
376		{ // argument error, for a null type
377			[]cty.Value{
378				cty.MapVal(map[string]cty.Value{
379					"a": cty.StringVal("b"),
380				}),
381				cty.NullVal(cty.String),
382			},
383			cty.NilVal,
384			true,
385		},
386		{ // merge maps of maps
387			[]cty.Value{
388				cty.MapVal(map[string]cty.Value{
389					"a": cty.MapVal(map[string]cty.Value{
390						"b": cty.StringVal("c"),
391					}),
392				}),
393				cty.MapVal(map[string]cty.Value{
394					"d": cty.MapVal(map[string]cty.Value{
395						"e": cty.StringVal("f"),
396					}),
397				}),
398			},
399			cty.MapVal(map[string]cty.Value{
400				"a": cty.MapVal(map[string]cty.Value{
401					"b": cty.StringVal("c"),
402				}),
403				"d": cty.MapVal(map[string]cty.Value{
404					"e": cty.StringVal("f"),
405				}),
406			}),
407			false,
408		},
409		{ // map of lists
410			[]cty.Value{
411				cty.MapVal(map[string]cty.Value{
412					"a": cty.ListVal([]cty.Value{
413						cty.StringVal("b"),
414						cty.StringVal("c"),
415					}),
416				}),
417				cty.MapVal(map[string]cty.Value{
418					"d": cty.ListVal([]cty.Value{
419						cty.StringVal("e"),
420						cty.StringVal("f"),
421					}),
422				}),
423			},
424			cty.MapVal(map[string]cty.Value{
425				"a": cty.ListVal([]cty.Value{
426					cty.StringVal("b"),
427					cty.StringVal("c"),
428				}),
429				"d": cty.ListVal([]cty.Value{
430					cty.StringVal("e"),
431					cty.StringVal("f"),
432				}),
433			}),
434			false,
435		},
436		{ // merge map of various kinds
437			[]cty.Value{
438				cty.MapVal(map[string]cty.Value{
439					"a": cty.ListVal([]cty.Value{
440						cty.StringVal("b"),
441						cty.StringVal("c"),
442					}),
443				}),
444				cty.MapVal(map[string]cty.Value{
445					"d": cty.MapVal(map[string]cty.Value{
446						"e": cty.StringVal("f"),
447					}),
448				}),
449			},
450			cty.ObjectVal(map[string]cty.Value{
451				"a": cty.ListVal([]cty.Value{
452					cty.StringVal("b"),
453					cty.StringVal("c"),
454				}),
455				"d": cty.MapVal(map[string]cty.Value{
456					"e": cty.StringVal("f"),
457				}),
458			}),
459			false,
460		},
461		{ // merge objects of various shapes
462			[]cty.Value{
463				cty.ObjectVal(map[string]cty.Value{
464					"a": cty.ListVal([]cty.Value{
465						cty.StringVal("b"),
466					}),
467				}),
468				cty.ObjectVal(map[string]cty.Value{
469					"d": cty.DynamicVal,
470				}),
471			},
472			cty.ObjectVal(map[string]cty.Value{
473				"a": cty.ListVal([]cty.Value{
474					cty.StringVal("b"),
475				}),
476				"d": cty.DynamicVal,
477			}),
478			false,
479		},
480		{ // merge maps and objects
481			[]cty.Value{
482				cty.MapVal(map[string]cty.Value{
483					"a": cty.ListVal([]cty.Value{
484						cty.StringVal("b"),
485					}),
486				}),
487				cty.ObjectVal(map[string]cty.Value{
488					"d": cty.NumberIntVal(2),
489				}),
490			},
491			cty.ObjectVal(map[string]cty.Value{
492				"a": cty.ListVal([]cty.Value{
493					cty.StringVal("b"),
494				}),
495				"d": cty.NumberIntVal(2),
496			}),
497			false,
498		},
499		{ // attr a type and value is overridden
500			[]cty.Value{
501				cty.ObjectVal(map[string]cty.Value{
502					"a": cty.ListVal([]cty.Value{
503						cty.StringVal("b"),
504					}),
505					"b": cty.StringVal("b"),
506				}),
507				cty.ObjectVal(map[string]cty.Value{
508					"a": cty.ObjectVal(map[string]cty.Value{
509						"e": cty.StringVal("f"),
510					}),
511				}),
512			},
513			cty.ObjectVal(map[string]cty.Value{
514				"a": cty.ObjectVal(map[string]cty.Value{
515					"e": cty.StringVal("f"),
516				}),
517				"b": cty.StringVal("b"),
518			}),
519			false,
520		},
521		{ // argument error: non map type
522			[]cty.Value{
523				cty.MapVal(map[string]cty.Value{
524					"a": cty.ListVal([]cty.Value{
525						cty.StringVal("b"),
526						cty.StringVal("c"),
527					}),
528				}),
529				cty.ListVal([]cty.Value{
530					cty.StringVal("d"),
531					cty.StringVal("e"),
532				}),
533			},
534			cty.NilVal,
535			true,
536		},
537		{ // Empty maps are allowed in merge
538			[]cty.Value{
539				cty.MapValEmpty(cty.String),
540				cty.MapValEmpty(cty.String),
541			},
542			cty.MapValEmpty(cty.String),
543			false,
544		},
545	}
546
547	for _, test := range tests {
548		t.Run(fmt.Sprintf("merge(%#v)", test.Values), func(t *testing.T) {
549			got, err := Merge(test.Values...)
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
567func TestIndex(t *testing.T) {
568	tests := []struct {
569		Collection cty.Value
570		Key        cty.Value
571		Want       cty.Value
572	}{
573		{
574			cty.ListVal([]cty.Value{cty.True}),
575			cty.NumberIntVal(0),
576			cty.True,
577		},
578		{
579			cty.MapVal(map[string]cty.Value{"hello": cty.True}),
580			cty.StringVal("hello"),
581			cty.True,
582		},
583		{
584			cty.TupleVal([]cty.Value{cty.True, cty.StringVal("hello")}),
585			cty.NumberIntVal(0),
586			cty.True,
587		},
588		{
589			cty.TupleVal([]cty.Value{cty.True, cty.StringVal("hello")}),
590			cty.NumberIntVal(1),
591			cty.StringVal("hello"),
592		},
593		{
594			cty.ListValEmpty(cty.Number),
595			cty.UnknownVal(cty.Number),
596			cty.UnknownVal(cty.Number),
597		},
598		{
599			cty.UnknownVal(cty.List(cty.Bool)),
600			cty.UnknownVal(cty.Number),
601			cty.UnknownVal(cty.Bool),
602		},
603		{
604			cty.ListValEmpty(cty.Number),
605			cty.DynamicVal,
606			cty.UnknownVal(cty.Number),
607		},
608		{
609			cty.MapValEmpty(cty.Number),
610			cty.DynamicVal,
611			cty.UnknownVal(cty.Number),
612		},
613		{
614			cty.DynamicVal,
615			cty.StringVal("hello"),
616			cty.DynamicVal,
617		},
618		{
619			cty.DynamicVal,
620			cty.DynamicVal,
621			cty.DynamicVal,
622		},
623	}
624
625	for _, test := range tests {
626		t.Run(fmt.Sprintf("Index(%#v,%#v)", test.Collection, test.Key), func(t *testing.T) {
627			got, err := Index(test.Collection, test.Key)
628
629			if err != nil {
630				t.Fatalf("unexpected error: %s", err)
631			}
632
633			if !got.RawEquals(test.Want) {
634				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
635			}
636		})
637	}
638}
639
640func TestLength(t *testing.T) {
641	tests := []struct {
642		Collection cty.Value
643		Want       cty.Value
644	}{
645		{
646			cty.ListValEmpty(cty.Number),
647			cty.NumberIntVal(0),
648		},
649		{
650			cty.ListVal([]cty.Value{cty.True}),
651			cty.NumberIntVal(1),
652		},
653		{
654			cty.SetValEmpty(cty.Number),
655			cty.NumberIntVal(0),
656		},
657		{
658			cty.SetVal([]cty.Value{cty.True}),
659			cty.NumberIntVal(1),
660		},
661		{
662			cty.SetVal([]cty.Value{cty.True, cty.False}),
663			cty.NumberIntVal(2),
664		},
665		{
666			cty.SetVal([]cty.Value{cty.True, cty.UnknownVal(cty.Bool)}),
667			cty.UnknownVal(cty.Number), // Don't know if the unknown in the input represents cty.True or cty.False
668		},
669		{
670			cty.SetVal([]cty.Value{cty.UnknownVal(cty.Bool)}),
671			cty.NumberIntVal(1), // Will be one regardless of what value the unknown in the input is representing
672		},
673		{
674			cty.MapValEmpty(cty.Bool),
675			cty.NumberIntVal(0),
676		},
677		{
678			cty.MapVal(map[string]cty.Value{"hello": cty.True}),
679			cty.NumberIntVal(1),
680		},
681		{
682			cty.EmptyTupleVal,
683			cty.NumberIntVal(0),
684		},
685		{
686			cty.TupleVal([]cty.Value{cty.True}),
687			cty.NumberIntVal(1),
688		},
689		{
690			cty.UnknownVal(cty.List(cty.Bool)),
691			cty.UnknownVal(cty.Number),
692		},
693		{
694			cty.DynamicVal,
695			cty.UnknownVal(cty.Number),
696		},
697	}
698
699	for _, test := range tests {
700		t.Run(fmt.Sprintf("Length(%#v)", test.Collection), func(t *testing.T) {
701			got, err := Length(test.Collection)
702
703			if err != nil {
704				t.Fatalf("unexpected error: %s", err)
705			}
706
707			if !got.RawEquals(test.Want) {
708				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
709			}
710		})
711	}
712}
713
714func TestLookup(t *testing.T) {
715	tests := []struct {
716		Collection cty.Value
717		Key        cty.Value
718		Default    cty.Value
719		Want       cty.Value
720	}{
721		{
722			cty.MapValEmpty(cty.String),
723			cty.StringVal("baz"),
724			cty.StringVal("foo"),
725			cty.StringVal("foo"),
726		},
727		{
728			cty.MapVal(map[string]cty.Value{
729				"foo": cty.StringVal("bar"),
730			}),
731			cty.StringVal("foo"),
732			cty.StringVal("nope"),
733			cty.StringVal("bar"),
734		},
735	}
736
737	for _, test := range tests {
738		t.Run(fmt.Sprintf("Lookup(%#v,%#v,%#v)", test.Collection, test.Key, test.Default), func(t *testing.T) {
739			got, err := Lookup(test.Collection, test.Key, test.Default)
740
741			if err != nil {
742				t.Fatalf("unexpected error: %s", err)
743			}
744
745			if !got.RawEquals(test.Want) {
746				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
747			}
748		})
749	}
750}
751
752func TestElement(t *testing.T) {
753	listOfStrings := cty.ListVal([]cty.Value{
754		cty.StringVal("the"),
755		cty.StringVal("quick"),
756		cty.StringVal("brown"),
757		cty.StringVal("fox"),
758	})
759	listOfInts := cty.ListVal([]cty.Value{
760		cty.NumberIntVal(1),
761		cty.NumberIntVal(2),
762		cty.NumberIntVal(3),
763		cty.NumberIntVal(4),
764	})
765	listWithUnknown := cty.ListVal([]cty.Value{
766		cty.StringVal("the"),
767		cty.StringVal("quick"),
768		cty.StringVal("brown"),
769		cty.UnknownVal(cty.String),
770	})
771
772	tests := []struct {
773		List  cty.Value
774		Index cty.Value
775		Want  cty.Value
776		Err   bool
777	}{
778		{
779			listOfStrings,
780			cty.NumberIntVal(2),
781			cty.StringVal("brown"),
782			false,
783		},
784		{ // index greater than length(list)
785			listOfStrings,
786			cty.NumberIntVal(5),
787			cty.StringVal("quick"),
788			false,
789		},
790		{ // list of lists
791			cty.ListVal([]cty.Value{listOfStrings, listOfStrings}),
792			cty.NumberIntVal(0),
793			listOfStrings,
794			false,
795		},
796		{
797			listOfStrings,
798			cty.UnknownVal(cty.Number),
799			cty.UnknownVal(cty.String),
800			false,
801		},
802		{
803			listOfInts,
804			cty.NumberIntVal(2),
805			cty.NumberIntVal(3),
806			false,
807		},
808		{
809			listWithUnknown,
810			cty.NumberIntVal(2),
811			cty.StringVal("brown"),
812			false,
813		},
814		{
815			listWithUnknown,
816			cty.NumberIntVal(3),
817			cty.UnknownVal(cty.String),
818			false,
819		},
820		{
821			listOfStrings,
822			cty.NumberIntVal(-1),
823			cty.DynamicVal,
824			true, // index cannot be a negative number
825		},
826		{
827			listOfStrings,
828			cty.StringVal("brown"), // definitely not an index
829			cty.DynamicVal,
830			true,
831		},
832	}
833
834	for _, test := range tests {
835		t.Run(fmt.Sprintf("Element(%#v,%#v)", test.List, test.Index), func(t *testing.T) {
836			got, err := Element(test.List, test.Index)
837
838			if test.Err {
839				if err == nil {
840					t.Fatal("succeeded; want error")
841				}
842				return
843			} else if err != nil {
844				t.Fatalf("unexpected error: %s", err)
845			}
846
847			if !got.RawEquals(test.Want) {
848				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
849			}
850		})
851	}
852}
853
854func TestCoalesceList(t *testing.T) {
855	tests := map[string]struct {
856		Values []cty.Value
857		Want   cty.Value
858		Err    bool
859	}{
860		"returns first list if non-empty": {
861			[]cty.Value{
862				cty.ListVal([]cty.Value{
863					cty.StringVal("a"),
864					cty.StringVal("b"),
865				}),
866				cty.ListVal([]cty.Value{
867					cty.StringVal("c"),
868					cty.StringVal("d"),
869				}),
870			},
871			cty.ListVal([]cty.Value{
872				cty.StringVal("a"),
873				cty.StringVal("b"),
874			}),
875			false,
876		},
877		"returns second list if first is empty": {
878			[]cty.Value{
879				cty.ListValEmpty(cty.String),
880				cty.ListVal([]cty.Value{
881					cty.StringVal("c"),
882					cty.StringVal("d"),
883				}),
884			},
885			cty.ListVal([]cty.Value{
886				cty.StringVal("c"),
887				cty.StringVal("d"),
888			}),
889			false,
890		},
891		"return type is dynamic, not unified": {
892			[]cty.Value{
893				cty.ListValEmpty(cty.String),
894				cty.ListVal([]cty.Value{
895					cty.NumberIntVal(3),
896					cty.NumberIntVal(4),
897				}),
898			},
899			cty.ListVal([]cty.Value{
900				cty.NumberIntVal(3),
901				cty.NumberIntVal(4),
902			}),
903			false,
904		},
905		"works with tuples": {
906			[]cty.Value{
907				cty.EmptyTupleVal,
908				cty.TupleVal([]cty.Value{
909					cty.StringVal("c"),
910					cty.StringVal("d"),
911				}),
912			},
913			cty.TupleVal([]cty.Value{
914				cty.StringVal("c"),
915				cty.StringVal("d"),
916			}),
917			false,
918		},
919		"unknown arguments": {
920			[]cty.Value{
921				cty.UnknownVal(cty.List(cty.String)),
922				cty.ListVal([]cty.Value{
923					cty.StringVal("c"),
924					cty.StringVal("d"),
925				}),
926			},
927			cty.DynamicVal,
928			false,
929		},
930		"null arguments": {
931			[]cty.Value{
932				cty.NullVal(cty.List(cty.String)),
933				cty.ListVal([]cty.Value{
934					cty.StringVal("c"),
935					cty.StringVal("d"),
936				}),
937			},
938			cty.ListVal([]cty.Value{
939				cty.StringVal("c"),
940				cty.StringVal("d"),
941			}),
942			false,
943		},
944		"all null arguments": {
945			[]cty.Value{
946				cty.NullVal(cty.List(cty.String)),
947				cty.NullVal(cty.List(cty.String)),
948			},
949			cty.NilVal,
950			true,
951		},
952		"invalid arguments": {
953			[]cty.Value{
954				cty.MapVal(map[string]cty.Value{"a": cty.True}),
955				cty.ObjectVal(map[string]cty.Value{"b": cty.False}),
956			},
957			cty.NilVal,
958			true,
959		},
960		"no arguments": {
961			[]cty.Value{},
962			cty.NilVal,
963			true,
964		},
965	}
966
967	for name, test := range tests {
968		t.Run(name, func(t *testing.T) {
969			got, err := CoalesceList(test.Values...)
970
971			if test.Err {
972				if err == nil {
973					t.Fatal("succeeded; want error")
974				}
975				return
976			} else if err != nil {
977				t.Fatalf("unexpected error: %s", err)
978			}
979
980			if !got.RawEquals(test.Want) {
981				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
982			}
983		})
984	}
985}
986