opentofu/lang/funcs/collection_test.go
Martin Atkins 096b1bb87b lang/funcs: Port the "reverse" function from the old functions set
This has the same functionality as the "reverse" function that was
implemented in the "config" package, but adapted to the new language type
system.
2019-03-19 17:32:19 -07:00

2676 lines
56 KiB
Go

package funcs
import (
"fmt"
"testing"
"github.com/zclconf/go-cty/cty"
)
func TestElement(t *testing.T) {
tests := []struct {
List cty.Value
Index cty.Value
Want cty.Value
}{
{
cty.ListVal([]cty.Value{
cty.StringVal("hello"),
}),
cty.NumberIntVal(0),
cty.StringVal("hello"),
},
{
cty.ListVal([]cty.Value{
cty.StringVal("hello"),
}),
cty.NumberIntVal(1),
cty.StringVal("hello"),
},
{
cty.ListVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("bonjour"),
}),
cty.NumberIntVal(0),
cty.StringVal("hello"),
},
{
cty.ListVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("bonjour"),
}),
cty.NumberIntVal(1),
cty.StringVal("bonjour"),
},
{
cty.ListVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("bonjour"),
}),
cty.NumberIntVal(2),
cty.StringVal("hello"),
},
{
cty.TupleVal([]cty.Value{
cty.StringVal("hello"),
}),
cty.NumberIntVal(0),
cty.StringVal("hello"),
},
{
cty.TupleVal([]cty.Value{
cty.StringVal("hello"),
}),
cty.NumberIntVal(1),
cty.StringVal("hello"),
},
{
cty.TupleVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("bonjour"),
}),
cty.NumberIntVal(0),
cty.StringVal("hello"),
},
{
cty.TupleVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("bonjour"),
}),
cty.NumberIntVal(1),
cty.StringVal("bonjour"),
},
{
cty.TupleVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("bonjour"),
}),
cty.NumberIntVal(2),
cty.StringVal("hello"),
},
{
cty.TupleVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("bonjour"),
}),
cty.UnknownVal(cty.Number),
cty.DynamicVal,
},
{
cty.UnknownVal(cty.Tuple([]cty.Type{cty.String, cty.Bool})),
cty.NumberIntVal(1),
cty.UnknownVal(cty.Bool),
},
{
cty.UnknownVal(cty.Tuple([]cty.Type{cty.String, cty.String})),
cty.UnknownVal(cty.Number),
cty.DynamicVal,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("Element(%#v, %#v)", test.List, test.Index), func(t *testing.T) {
got, err := Element(test.List, test.Index)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestLength(t *testing.T) {
tests := []struct {
Value cty.Value
Want cty.Value
}{
{
cty.ListValEmpty(cty.Number),
cty.NumberIntVal(0),
},
{
cty.ListVal([]cty.Value{cty.True}),
cty.NumberIntVal(1),
},
{
cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}),
cty.NumberIntVal(1),
},
{
cty.SetValEmpty(cty.Number),
cty.NumberIntVal(0),
},
{
cty.SetVal([]cty.Value{cty.True}),
cty.NumberIntVal(1),
},
{
cty.MapValEmpty(cty.Bool),
cty.NumberIntVal(0),
},
{
cty.MapVal(map[string]cty.Value{"hello": cty.True}),
cty.NumberIntVal(1),
},
{
cty.EmptyTupleVal,
cty.NumberIntVal(0),
},
{
cty.UnknownVal(cty.EmptyTuple),
cty.NumberIntVal(0),
},
{
cty.TupleVal([]cty.Value{cty.True}),
cty.NumberIntVal(1),
},
{
cty.EmptyObjectVal,
cty.NumberIntVal(0),
},
{
cty.UnknownVal(cty.EmptyObject),
cty.NumberIntVal(0),
},
{
cty.ObjectVal(map[string]cty.Value{"true": cty.True}),
cty.NumberIntVal(1),
},
{
cty.UnknownVal(cty.List(cty.Bool)),
cty.UnknownVal(cty.Number),
},
{
cty.DynamicVal,
cty.UnknownVal(cty.Number),
},
{
cty.StringVal("hello"),
cty.NumberIntVal(5),
},
{
cty.StringVal(""),
cty.NumberIntVal(0),
},
{
cty.StringVal("1"),
cty.NumberIntVal(1),
},
{
cty.StringVal("Живой Журнал"),
cty.NumberIntVal(12),
},
{
// note that the dieresis here is intentionally a combining
// ligature.
cty.StringVal("noël"),
cty.NumberIntVal(4),
},
{
// The Es in this string has three combining acute accents.
// This tests something that NFC-normalization cannot collapse
// into a single precombined codepoint, since otherwise we might
// be cheating and relying on the single-codepoint forms.
cty.StringVal("wé́́é́́é́́!"),
cty.NumberIntVal(5),
},
{
// Go's normalization forms don't handle this ligature, so we
// will produce the wrong result but this is now a compatibility
// constraint and so we'll test it.
cty.StringVal("baffle"),
cty.NumberIntVal(4),
},
{
cty.StringVal("😸😾"),
cty.NumberIntVal(2),
},
{
cty.UnknownVal(cty.String),
cty.UnknownVal(cty.Number),
},
{
cty.DynamicVal,
cty.UnknownVal(cty.Number),
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("Length(%#v)", test.Value), func(t *testing.T) {
got, err := Length(test.Value)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestCoalesceList(t *testing.T) {
tests := []struct {
Values []cty.Value
Want cty.Value
Err bool
}{
{
[]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("first"), cty.StringVal("second"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("third"), cty.StringVal("fourth"),
}),
},
cty.ListVal([]cty.Value{
cty.StringVal("first"), cty.StringVal("second"),
}),
false,
},
{
[]cty.Value{
cty.ListValEmpty(cty.String),
cty.ListVal([]cty.Value{
cty.StringVal("third"), cty.StringVal("fourth"),
}),
},
cty.ListVal([]cty.Value{
cty.StringVal("third"), cty.StringVal("fourth"),
}),
false,
},
{
[]cty.Value{
cty.ListValEmpty(cty.Number),
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
}),
},
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
}),
false,
},
{ // lists with mixed types
[]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("first"), cty.StringVal("second"),
}),
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
}),
},
cty.ListVal([]cty.Value{
cty.StringVal("first"), cty.StringVal("second"),
}),
false,
},
{ // lists with mixed types
[]cty.Value{
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
}),
cty.ListVal([]cty.Value{
cty.StringVal("first"), cty.StringVal("second"),
}),
},
cty.ListVal([]cty.Value{
cty.StringVal("1"), cty.StringVal("2"),
}),
false,
},
{ // list with unknown values
[]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("first"), cty.StringVal("second"),
}),
cty.ListVal([]cty.Value{
cty.UnknownVal(cty.String),
}),
},
cty.ListVal([]cty.Value{
cty.StringVal("first"), cty.StringVal("second"),
}),
false,
},
{ // list with unknown values
[]cty.Value{
cty.ListVal([]cty.Value{
cty.UnknownVal(cty.String),
}),
cty.ListVal([]cty.Value{
cty.StringVal("third"), cty.StringVal("fourth"),
}),
},
cty.ListVal([]cty.Value{
cty.UnknownVal(cty.String),
}),
false,
},
{
[]cty.Value{
cty.MapValEmpty(cty.DynamicPseudoType),
cty.ListVal([]cty.Value{
cty.StringVal("third"), cty.StringVal("fourth"),
}),
},
cty.NilVal,
true,
},
{ // unknown list
[]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("third"), cty.StringVal("fourth"),
}),
cty.UnknownVal(cty.List(cty.String)),
},
cty.ListVal([]cty.Value{
cty.StringVal("third"), cty.StringVal("fourth"),
}),
false,
},
{ // unknown list
[]cty.Value{
cty.ListValEmpty(cty.String),
cty.UnknownVal(cty.List(cty.String)),
},
cty.UnknownVal(cty.List(cty.String)),
false,
},
{ // unknown list
[]cty.Value{
cty.UnknownVal(cty.List(cty.String)),
cty.ListVal([]cty.Value{
cty.StringVal("third"), cty.StringVal("fourth"),
}),
},
cty.UnknownVal(cty.List(cty.String)),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("coalescelist(%#v)", test.Values), func(t *testing.T) {
got, err := CoalesceList(test.Values...)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestCompact(t *testing.T) {
tests := []struct {
List cty.Value
Want cty.Value
Err bool
}{
{
cty.ListVal([]cty.Value{
cty.StringVal("test"),
cty.StringVal(""),
cty.StringVal("test"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("test"),
cty.StringVal("test"),
}),
false,
},
{
cty.ListVal([]cty.Value{
cty.StringVal(""),
cty.StringVal(""),
cty.StringVal(""),
}),
cty.ListValEmpty(cty.String),
false,
},
{
cty.ListValEmpty(cty.String),
cty.ListValEmpty(cty.String),
false,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("test"),
cty.StringVal("test"),
cty.StringVal(""),
}),
cty.ListVal([]cty.Value{
cty.StringVal("test"),
cty.StringVal("test"),
}),
false,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("test"),
cty.UnknownVal(cty.String),
cty.StringVal(""),
}),
cty.UnknownVal(cty.List(cty.String)),
false,
},
{ // errors on list of lists
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("test"),
}),
cty.ListVal([]cty.Value{
cty.StringVal(""),
}),
}),
cty.NilVal,
true,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("compact(%#v)", test.List), func(t *testing.T) {
got, err := Compact(test.List)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestContains(t *testing.T) {
listOfStrings := cty.ListVal([]cty.Value{
cty.StringVal("the"),
cty.StringVal("quick"),
cty.StringVal("brown"),
cty.StringVal("fox"),
})
listOfInts := cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
cty.NumberIntVal(3),
cty.NumberIntVal(4),
})
listWithUnknown := cty.ListVal([]cty.Value{
cty.StringVal("the"),
cty.StringVal("quick"),
cty.StringVal("brown"),
cty.UnknownVal(cty.String),
})
tests := []struct {
List cty.Value
Value cty.Value
Want cty.Value
Err bool
}{
{
listOfStrings,
cty.StringVal("the"),
cty.BoolVal(true),
false,
},
{
listWithUnknown,
cty.StringVal("the"),
cty.BoolVal(true),
false,
},
{
listOfStrings,
cty.StringVal("penguin"),
cty.BoolVal(false),
false,
},
{
listOfInts,
cty.NumberIntVal(1),
cty.BoolVal(true),
false,
},
{
listOfInts,
cty.NumberIntVal(42),
cty.BoolVal(false),
false,
},
{ // And now we mix and match
listOfInts,
cty.StringVal("1"),
cty.BoolVal(false),
false,
},
{ // Check a list with an unknown value
cty.ListVal([]cty.Value{
cty.UnknownVal(cty.String),
cty.StringVal("quick"),
cty.StringVal("brown"),
cty.StringVal("fox"),
}),
cty.StringVal("quick"),
cty.BoolVal(true),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("contains(%#v, %#v)", test.List, test.Value), func(t *testing.T) {
got, err := Contains(test.List, test.Value)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestIndex(t *testing.T) {
tests := []struct {
List cty.Value
Value cty.Value
Want cty.Value
Err bool
}{
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
cty.StringVal("a"),
cty.NumberIntVal(0),
false,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.UnknownVal(cty.String),
}),
cty.StringVal("a"),
cty.NumberIntVal(0),
false,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
cty.StringVal("b"),
cty.NumberIntVal(1),
false,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
cty.StringVal("z"),
cty.NilVal,
true,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("1"),
cty.StringVal("2"),
cty.StringVal("3"),
}),
cty.NumberIntVal(1),
cty.NumberIntVal(0),
true,
},
{
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
cty.NumberIntVal(3),
}),
cty.NumberIntVal(2),
cty.NumberIntVal(1),
false,
},
{
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
cty.NumberIntVal(3),
}),
cty.NumberIntVal(4),
cty.NilVal,
true,
},
{
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
cty.NumberIntVal(3),
}),
cty.StringVal("1"),
cty.NumberIntVal(0),
true,
},
{
cty.TupleVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
cty.NumberIntVal(3),
}),
cty.NumberIntVal(1),
cty.NumberIntVal(0),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("index(%#v, %#v)", test.List, test.Value), func(t *testing.T) {
got, err := Index(test.List, test.Value)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestDistinct(t *testing.T) {
tests := []struct {
List cty.Value
Want cty.Value
Err bool
}{
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("a"),
cty.StringVal("b"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
}),
false,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("a"),
cty.UnknownVal(cty.String),
}),
cty.UnknownVal(cty.List(cty.String)),
false,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
cty.StringVal("d"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
cty.StringVal("d"),
}),
false,
},
{
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
cty.NumberIntVal(1),
cty.NumberIntVal(2),
}),
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
}),
false,
},
{
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
}),
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
}),
}),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
}),
}),
false,
},
{
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
}),
cty.ListVal([]cty.Value{
cty.NumberIntVal(3),
cty.NumberIntVal(4),
}),
}),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
}),
cty.ListVal([]cty.Value{
cty.NumberIntVal(3),
cty.NumberIntVal(4),
}),
}),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("distinct(%#v)", test.List), func(t *testing.T) {
got, err := Distinct(test.List)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestChunklist(t *testing.T) {
tests := []struct {
List cty.Value
Size cty.Value
Want cty.Value
Err bool
}{
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
cty.NumberIntVal(1),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("b"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("c"),
}),
}),
false,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
cty.NumberIntVal(-1),
cty.NilVal,
true,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
cty.NumberIntVal(0),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
}),
false,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.UnknownVal(cty.String),
}),
cty.NumberIntVal(1),
cty.UnknownVal(cty.List(cty.List(cty.String))),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("chunklist(%#v, %#v)", test.List, test.Size), func(t *testing.T) {
got, err := Chunklist(test.List, test.Size)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestFlatten(t *testing.T) {
tests := []struct {
List cty.Value
Want cty.Value
Err bool
}{
{
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("c"),
cty.StringVal("d"),
}),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
cty.StringVal("d"),
}),
false,
},
{
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
}),
cty.ListVal([]cty.Value{
cty.UnknownVal(cty.String),
cty.StringVal("d"),
}),
}),
cty.UnknownVal(cty.List(cty.DynamicPseudoType)),
false,
},
{
cty.ListValEmpty(cty.String),
cty.ListValEmpty(cty.DynamicPseudoType),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("flatten(%#v)", test.List), func(t *testing.T) {
got, err := Flatten(test.List)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestKeys(t *testing.T) {
tests := []struct {
Map cty.Value
Want cty.Value
Err bool
}{
{
cty.MapVal(map[string]cty.Value{
"hello": cty.NumberIntVal(1),
"goodbye": cty.NumberIntVal(42),
}),
cty.ListVal([]cty.Value{
cty.StringVal("goodbye"),
cty.StringVal("hello"),
}),
false,
},
{ // same as above, but an object type
cty.ObjectVal(map[string]cty.Value{
"hello": cty.NumberIntVal(1),
"goodbye": cty.StringVal("adieu"),
}),
cty.TupleVal([]cty.Value{
cty.StringVal("goodbye"),
cty.StringVal("hello"),
}),
false,
},
{ // for an unknown object we can still return the keys, since they are part of the type
cty.UnknownVal(cty.Object(map[string]cty.Type{
"hello": cty.Number,
"goodbye": cty.String,
})),
cty.TupleVal([]cty.Value{
cty.StringVal("goodbye"),
cty.StringVal("hello"),
}),
false,
},
{ // an empty object has no keys
cty.EmptyObjectVal,
cty.EmptyTupleVal,
false,
},
{ // an empty map has no keys, but the result should still be properly typed
cty.MapValEmpty(cty.Number),
cty.ListValEmpty(cty.String),
false,
},
{ // Unknown map has unknown keys
cty.UnknownVal(cty.Map(cty.String)),
cty.UnknownVal(cty.List(cty.String)),
false,
},
{ // Not a map at all, so invalid
cty.StringVal("foo"),
cty.NilVal,
true,
},
{ // Can't get keys from a null object
cty.NullVal(cty.Object(map[string]cty.Type{
"hello": cty.Number,
"goodbye": cty.String,
})),
cty.NilVal,
true,
},
{ // Can't get keys from a null map
cty.NullVal(cty.Map(cty.Number)),
cty.NilVal,
true,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("keys(%#v)", test.Map), func(t *testing.T) {
got, err := Keys(test.Map)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestList(t *testing.T) {
tests := []struct {
Values []cty.Value
Want cty.Value
Err bool
}{
{
[]cty.Value{
cty.NilVal,
},
cty.NilVal,
true,
},
{
[]cty.Value{
cty.StringVal("Hello"),
},
cty.ListVal([]cty.Value{
cty.StringVal("Hello"),
}),
false,
},
{
[]cty.Value{
cty.StringVal("Hello"),
cty.StringVal("World"),
},
cty.ListVal([]cty.Value{
cty.StringVal("Hello"),
cty.StringVal("World"),
}),
false,
},
{
[]cty.Value{
cty.StringVal("Hello"),
cty.NumberIntVal(42),
},
cty.ListVal([]cty.Value{
cty.StringVal("Hello"),
cty.StringVal("42"),
}),
false,
},
{
[]cty.Value{
cty.StringVal("Hello"),
cty.UnknownVal(cty.String),
},
cty.ListVal([]cty.Value{
cty.StringVal("Hello"),
cty.UnknownVal(cty.String),
}),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("list(%#v)", test.Values), func(t *testing.T) {
got, err := List(test.Values...)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestLookup(t *testing.T) {
simpleMap := cty.MapVal(map[string]cty.Value{
"foo": cty.StringVal("bar"),
})
intsMap := cty.MapVal(map[string]cty.Value{
"foo": cty.NumberIntVal(42),
})
mapOfLists := cty.MapVal(map[string]cty.Value{
"foo": cty.ListVal([]cty.Value{
cty.StringVal("bar"),
cty.StringVal("baz"),
}),
})
mapWithUnknowns := cty.MapVal(map[string]cty.Value{
"foo": cty.StringVal("bar"),
"baz": cty.UnknownVal(cty.String),
})
mapWithObjects := cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("bar"),
"baz": cty.NumberIntVal(42),
})
tests := []struct {
Values []cty.Value
Want cty.Value
Err bool
}{
{
[]cty.Value{
simpleMap,
cty.StringVal("foo"),
},
cty.StringVal("bar"),
false,
},
{
[]cty.Value{
mapWithObjects,
cty.StringVal("foo"),
},
cty.StringVal("bar"),
false,
},
{
[]cty.Value{
intsMap,
cty.StringVal("foo"),
},
cty.NumberIntVal(42),
false,
},
{ // Invalid key
[]cty.Value{
simpleMap,
cty.StringVal("bar"),
},
cty.NilVal,
true,
},
{ // Invalid key
[]cty.Value{
mapWithObjects,
cty.StringVal("bar"),
},
cty.NilVal,
true,
},
{ // Supplied default with valid key
[]cty.Value{
simpleMap,
cty.StringVal("foo"),
cty.StringVal(""),
},
cty.StringVal("bar"),
false,
},
{ // Supplied default with valid (int) key
[]cty.Value{
simpleMap,
cty.StringVal("foo"),
cty.NumberIntVal(-1),
},
cty.StringVal("bar"),
false,
},
{ // Supplied default with valid key
[]cty.Value{
mapWithObjects,
cty.StringVal("foobar"),
cty.StringVal(""),
},
cty.StringVal(""),
false,
},
{ // Supplied default with invalid key
[]cty.Value{
simpleMap,
cty.StringVal("baz"),
cty.StringVal(""),
},
cty.StringVal(""),
false,
},
{ // Supplied non-empty default with invalid key
[]cty.Value{
simpleMap,
cty.StringVal("bar"),
cty.StringVal("xyz"),
},
cty.StringVal("xyz"),
false,
},
{ // too many args
[]cty.Value{
simpleMap,
cty.StringVal("foo"),
cty.StringVal("bar"),
cty.StringVal("baz"),
},
cty.NilVal,
true,
},
{ // cannot search a map of lists
[]cty.Value{
mapOfLists,
cty.StringVal("baz"),
},
cty.NilVal,
true,
},
{
[]cty.Value{
mapWithUnknowns,
cty.StringVal("baz"),
},
cty.UnknownVal(cty.String),
false,
},
{
[]cty.Value{
simpleMap,
cty.UnknownVal(cty.String),
},
cty.UnknownVal(cty.String),
false,
},
{
[]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("a"),
"bar": cty.StringVal("b"),
}),
cty.UnknownVal(cty.String),
},
cty.DynamicVal, // if the key is unknown then we don't know which object attribute and thus can't know the type
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("lookup(%#v)", test.Values), func(t *testing.T) {
got, err := Lookup(test.Values...)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestMap(t *testing.T) {
tests := []struct {
Values []cty.Value
Want cty.Value
Err bool
}{
{
[]cty.Value{
cty.StringVal("hello"),
cty.StringVal("world"),
},
cty.MapVal(map[string]cty.Value{
"hello": cty.StringVal("world"),
}),
false,
},
{
[]cty.Value{
cty.StringVal("hello"),
cty.UnknownVal(cty.String),
},
cty.UnknownVal(cty.Map(cty.String)),
false,
},
{
[]cty.Value{
cty.StringVal("hello"),
cty.StringVal("world"),
cty.StringVal("what's"),
cty.StringVal("up"),
},
cty.MapVal(map[string]cty.Value{
"hello": cty.StringVal("world"),
"what's": cty.StringVal("up"),
}),
false,
},
{
[]cty.Value{
cty.StringVal("hello"),
cty.NumberIntVal(1),
cty.StringVal("goodbye"),
cty.NumberIntVal(42),
},
cty.MapVal(map[string]cty.Value{
"hello": cty.NumberIntVal(1),
"goodbye": cty.NumberIntVal(42),
}),
false,
},
{ // convert numbers to strings
[]cty.Value{
cty.StringVal("hello"),
cty.NumberIntVal(1),
cty.StringVal("goodbye"),
cty.StringVal("42"),
},
cty.MapVal(map[string]cty.Value{
"hello": cty.StringVal("1"),
"goodbye": cty.StringVal("42"),
}),
false,
},
{ // map of lists is okay
[]cty.Value{
cty.StringVal("hello"),
cty.ListVal([]cty.Value{
cty.StringVal("world"),
}),
cty.StringVal("what's"),
cty.ListVal([]cty.Value{
cty.StringVal("up"),
}),
},
cty.MapVal(map[string]cty.Value{
"hello": cty.ListVal([]cty.Value{cty.StringVal("world")}),
"what's": cty.ListVal([]cty.Value{cty.StringVal("up")}),
}),
false,
},
{ // map of maps is okay
[]cty.Value{
cty.StringVal("hello"),
cty.MapVal(map[string]cty.Value{
"there": cty.StringVal("world"),
}),
cty.StringVal("what's"),
cty.MapVal(map[string]cty.Value{
"really": cty.StringVal("up"),
}),
},
cty.MapVal(map[string]cty.Value{
"hello": cty.MapVal(map[string]cty.Value{
"there": cty.StringVal("world"),
}),
"what's": cty.MapVal(map[string]cty.Value{
"really": cty.StringVal("up"),
}),
}),
false,
},
{ // single argument returns an error
[]cty.Value{
cty.StringVal("hello"),
},
cty.NilVal,
true,
},
{ // duplicate keys returns an error
[]cty.Value{
cty.StringVal("hello"),
cty.StringVal("world"),
cty.StringVal("hello"),
cty.StringVal("universe"),
},
cty.NilVal,
true,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("map(%#v)", test.Values), func(t *testing.T) {
got, err := Map(test.Values...)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestMatchkeys(t *testing.T) {
tests := []struct {
Keys cty.Value
Values cty.Value
Searchset cty.Value
Want cty.Value
Err bool
}{
{ // normal usage
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("ref1"),
cty.StringVal("ref2"),
cty.StringVal("ref3"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("ref1"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
false,
},
{ // normal usage 2, check the order
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("ref1"),
cty.StringVal("ref2"),
cty.StringVal("ref3"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("ref2"),
cty.StringVal("ref1"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
}),
false,
},
{ // no matches
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("ref1"),
cty.StringVal("ref2"),
cty.StringVal("ref3"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("ref4"),
}),
cty.ListValEmpty(cty.String),
false,
},
{ // no matches 2
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("ref1"),
cty.StringVal("ref2"),
cty.StringVal("ref3"),
}),
cty.ListValEmpty(cty.String),
cty.ListValEmpty(cty.String),
false,
},
{ // zero case
cty.ListValEmpty(cty.String),
cty.ListValEmpty(cty.String),
cty.ListVal([]cty.Value{cty.StringVal("nope")}),
cty.ListValEmpty(cty.String),
false,
},
{ // complex values
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("a"),
}),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("a"),
}),
}),
false,
},
{ // unknowns
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.UnknownVal(cty.String),
}),
cty.ListVal([]cty.Value{
cty.StringVal("ref1"),
cty.StringVal("ref2"),
cty.UnknownVal(cty.String),
}),
cty.ListVal([]cty.Value{
cty.StringVal("ref1"),
}),
cty.UnknownVal(cty.List(cty.String)),
false,
},
// errors
{ // different types
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.NilVal,
true,
},
{ // different types
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.NilVal,
true,
},
{ // lists of different length
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.NilVal,
true,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("matchkeys(%#v, %#v, %#v)", test.Keys, test.Values, test.Searchset), func(t *testing.T) {
got, err := Matchkeys(test.Keys, test.Values, test.Searchset)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestMerge(t *testing.T) {
tests := []struct {
Values []cty.Value
Want cty.Value
Err bool
}{
{
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("b"),
}),
cty.MapVal(map[string]cty.Value{
"c": cty.StringVal("d"),
}),
},
cty.ObjectVal(map[string]cty.Value{
"a": cty.StringVal("b"),
"c": cty.StringVal("d"),
}),
false,
},
{ // handle unknowns
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.UnknownVal(cty.String),
}),
cty.MapVal(map[string]cty.Value{
"c": cty.StringVal("d"),
}),
},
cty.DynamicVal,
false,
},
{ // merge with conflicts is ok, last in wins
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("b"),
"c": cty.StringVal("d"),
}),
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("x"),
}),
},
cty.ObjectVal(map[string]cty.Value{
"a": cty.StringVal("x"),
"c": cty.StringVal("d"),
}),
false,
},
{ // only accept maps
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("b"),
"c": cty.StringVal("d"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("x"),
}),
},
cty.NilVal,
true,
},
{ // merge maps of maps
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.MapVal(map[string]cty.Value{
"b": cty.StringVal("c"),
}),
}),
cty.MapVal(map[string]cty.Value{
"d": cty.MapVal(map[string]cty.Value{
"e": cty.StringVal("f"),
}),
}),
},
cty.ObjectVal(map[string]cty.Value{
"a": cty.MapVal(map[string]cty.Value{
"b": cty.StringVal("c"),
}),
"d": cty.MapVal(map[string]cty.Value{
"e": cty.StringVal("f"),
}),
}),
false,
},
{ // map of lists
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
cty.StringVal("c"),
}),
}),
cty.MapVal(map[string]cty.Value{
"d": cty.ListVal([]cty.Value{
cty.StringVal("e"),
cty.StringVal("f"),
}),
}),
},
cty.ObjectVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
cty.StringVal("c"),
}),
"d": cty.ListVal([]cty.Value{
cty.StringVal("e"),
cty.StringVal("f"),
}),
}),
false,
},
{ // merge map of various kinds
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
cty.StringVal("c"),
}),
}),
cty.MapVal(map[string]cty.Value{
"d": cty.MapVal(map[string]cty.Value{
"e": cty.StringVal("f"),
}),
}),
},
cty.ObjectVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
cty.StringVal("c"),
}),
"d": cty.MapVal(map[string]cty.Value{
"e": cty.StringVal("f"),
}),
}),
false,
},
{ // argument error: non map type
[]cty.Value{
cty.MapVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("b"),
cty.StringVal("c"),
}),
}),
cty.ListVal([]cty.Value{
cty.StringVal("d"),
cty.StringVal("e"),
}),
},
cty.NilVal,
true,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("merge(%#v)", test.Values), func(t *testing.T) {
got, err := Merge(test.Values...)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestReverse(t *testing.T) {
tests := []struct {
List cty.Value
Want cty.Value
Err string
}{
{
cty.ListValEmpty(cty.String),
cty.ListValEmpty(cty.String),
"",
},
{
cty.ListVal([]cty.Value{cty.StringVal("a")}),
cty.ListVal([]cty.Value{cty.StringVal("a")}),
"",
},
{
cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}),
cty.ListVal([]cty.Value{cty.StringVal("b"), cty.StringVal("a")}),
"",
},
{
cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b"), cty.StringVal("c")}),
cty.ListVal([]cty.Value{cty.StringVal("c"), cty.StringVal("b"), cty.StringVal("a")}),
"",
},
{
cty.ListVal([]cty.Value{cty.UnknownVal(cty.String), cty.StringVal("b"), cty.StringVal("c")}),
cty.ListVal([]cty.Value{cty.StringVal("c"), cty.StringVal("b"), cty.UnknownVal(cty.String)}),
"",
},
{
cty.EmptyTupleVal,
cty.EmptyTupleVal,
"",
},
{
cty.TupleVal([]cty.Value{cty.StringVal("a")}),
cty.TupleVal([]cty.Value{cty.StringVal("a")}),
"",
},
{
cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.True}),
cty.TupleVal([]cty.Value{cty.True, cty.StringVal("a")}),
"",
},
{
cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.True, cty.Zero}),
cty.TupleVal([]cty.Value{cty.Zero, cty.True, cty.StringVal("a")}),
"",
},
{
cty.SetValEmpty(cty.String),
cty.ListValEmpty(cty.String),
"",
},
{
cty.SetVal([]cty.Value{cty.StringVal("a")}),
cty.ListVal([]cty.Value{cty.StringVal("a")}),
"",
},
{
cty.SetVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}),
cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}),
"",
},
{
cty.SetVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b"), cty.StringVal("c")}),
cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("c"), cty.StringVal("b")}),
"",
},
{
cty.StringVal("no"),
cty.NilVal,
"can only reverse list or tuple values, not string",
},
{
cty.True,
cty.NilVal,
"can only reverse list or tuple values, not bool",
},
{
cty.MapValEmpty(cty.String),
cty.NilVal,
"can only reverse list or tuple values, not map of string",
},
{
cty.NullVal(cty.List(cty.String)),
cty.NilVal,
"argument must not be null",
},
{
cty.UnknownVal(cty.List(cty.String)),
cty.UnknownVal(cty.List(cty.String)),
"",
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("reverse(%#v)", test.List), func(t *testing.T) {
got, err := Reverse(test.List)
if test.Err != "" {
if err == nil {
t.Fatal("succeeded; want error")
}
if got, want := err.Error(), test.Err; got != want {
t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want)
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestSetProduct(t *testing.T) {
tests := []struct {
Sets []cty.Value
Want cty.Value
Err string
}{
{
nil,
cty.DynamicVal,
"at least two arguments are required",
},
{
[]cty.Value{
cty.SetValEmpty(cty.String),
},
cty.DynamicVal,
"at least two arguments are required",
},
{
[]cty.Value{
cty.SetValEmpty(cty.String),
cty.StringVal("hello"),
},
cty.DynamicVal,
"a set or a list is required", // this is an ArgError, so is presented against the second argument in particular
},
{
[]cty.Value{
cty.SetValEmpty(cty.String),
cty.SetValEmpty(cty.String),
},
cty.SetValEmpty(cty.Tuple([]cty.Type{cty.String, cty.String})),
"",
},
{
[]cty.Value{
cty.SetVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("stg"), cty.StringVal("prd")}),
cty.SetVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("bar")}),
},
cty.SetVal([]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("bar")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("bar")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("bar")}),
}),
"",
},
{
[]cty.Value{
cty.ListVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("stg"), cty.StringVal("prd")}),
cty.SetVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("bar")}),
},
cty.SetVal([]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("bar")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("bar")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("bar")}),
}),
"",
},
{
[]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("stg"), cty.StringVal("prd")}),
cty.SetVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("bar")}),
},
cty.SetVal([]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("bar")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("bar")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("bar")}),
}),
"",
},
{
[]cty.Value{
cty.ListVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("stg"), cty.StringVal("prd")}),
cty.ListVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("bar")}),
},
cty.ListVal([]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("bar")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("bar")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("bar")}),
}),
"",
},
{
[]cty.Value{
cty.ListVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("stg"), cty.StringVal("prd")}),
cty.TupleVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("bar")}),
},
cty.ListVal([]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("bar")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("bar")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("bar")}),
}),
"",
},
{
[]cty.Value{
cty.ListVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("stg"), cty.StringVal("prd")}),
cty.TupleVal([]cty.Value{cty.StringVal("foo"), cty.True}),
},
cty.ListVal([]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("true")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("true")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("true")}),
}),
"",
},
{
[]cty.Value{
cty.ListVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("stg"), cty.StringVal("prd")}),
cty.EmptyTupleVal,
},
cty.ListValEmpty(cty.Tuple([]cty.Type{cty.String, cty.DynamicPseudoType})),
"",
},
{
[]cty.Value{
cty.ListVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("stg"), cty.StringVal("prd")}),
cty.TupleVal([]cty.Value{cty.StringVal("foo"), cty.EmptyObjectVal}),
},
cty.DynamicVal,
"all elements must be of the same type", // this is an ArgError for the second argument
},
{
[]cty.Value{
cty.SetVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("stg"), cty.StringVal("prd")}),
cty.SetVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("bar")}),
cty.SetVal([]cty.Value{cty.StringVal("baz")}),
},
cty.SetVal([]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("foo"), cty.StringVal("baz")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("foo"), cty.StringVal("baz")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("foo"), cty.StringVal("baz")}),
cty.TupleVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("bar"), cty.StringVal("baz")}),
cty.TupleVal([]cty.Value{cty.StringVal("stg"), cty.StringVal("bar"), cty.StringVal("baz")}),
cty.TupleVal([]cty.Value{cty.StringVal("prd"), cty.StringVal("bar"), cty.StringVal("baz")}),
}),
"",
},
{
[]cty.Value{
cty.SetVal([]cty.Value{cty.StringVal("dev"), cty.StringVal("stg"), cty.StringVal("prd")}),
cty.SetValEmpty(cty.String),
},
cty.SetValEmpty(cty.Tuple([]cty.Type{cty.String, cty.String})),
"",
},
{
[]cty.Value{
cty.SetVal([]cty.Value{cty.StringVal("foo")}),
cty.SetVal([]cty.Value{cty.StringVal("bar")}),
},
cty.SetVal([]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("bar")}),
}),
"",
},
{
[]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("foo")}),
cty.TupleVal([]cty.Value{cty.StringVal("bar")}),
},
cty.ListVal([]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("bar")}),
}),
"",
},
{
[]cty.Value{
cty.SetVal([]cty.Value{cty.StringVal("foo")}),
cty.SetVal([]cty.Value{cty.DynamicVal}),
},
cty.SetVal([]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("foo"), cty.DynamicVal}),
}),
"",
},
{
[]cty.Value{
cty.SetVal([]cty.Value{cty.StringVal("foo")}),
cty.SetVal([]cty.Value{cty.True, cty.DynamicVal}),
},
cty.SetVal([]cty.Value{
cty.TupleVal([]cty.Value{cty.StringVal("foo"), cty.True}),
cty.TupleVal([]cty.Value{cty.StringVal("foo"), cty.UnknownVal(cty.Bool)}),
}),
"",
},
{
[]cty.Value{
cty.UnknownVal(cty.Set(cty.String)),
cty.SetVal([]cty.Value{cty.True, cty.False}),
},
cty.UnknownVal(cty.Set(cty.Tuple([]cty.Type{cty.String, cty.Bool}))),
"",
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("setproduct(%#v)", test.Sets), func(t *testing.T) {
got, err := SetProduct(test.Sets...)
if test.Err != "" {
if err == nil {
t.Fatal("succeeded; want error")
}
if got, want := err.Error(), test.Err; got != want {
t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want)
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestSlice(t *testing.T) {
listOfStrings := cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
})
listOfInts := cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(2),
})
listWithUnknowns := cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.UnknownVal(cty.String),
})
tests := []struct {
List cty.Value
StartIndex cty.Value
EndIndex cty.Value
Want cty.Value
Err bool
}{
{ // normal usage
listOfStrings,
cty.NumberIntVal(1),
cty.NumberIntVal(2),
cty.ListVal([]cty.Value{
cty.StringVal("b"),
}),
false,
},
{ // unknowns in the list
listWithUnknowns,
cty.NumberIntVal(1),
cty.NumberIntVal(2),
cty.UnknownVal(cty.List(cty.String)),
false,
},
{ // normal usage
listOfInts,
cty.NumberIntVal(1),
cty.NumberIntVal(2),
cty.ListVal([]cty.Value{
cty.NumberIntVal(2),
}),
false,
},
{ // empty result
listOfStrings,
cty.NumberIntVal(1),
cty.NumberIntVal(1),
cty.ListValEmpty(cty.String),
false,
},
{ // index out of bounds
listOfStrings,
cty.NumberIntVal(1),
cty.NumberIntVal(4),
cty.NilVal,
true,
},
{ // StartIndex index > EndIndex
listOfStrings,
cty.NumberIntVal(2),
cty.NumberIntVal(1),
cty.NilVal,
true,
},
{ // negative StartIndex
listOfStrings,
cty.NumberIntVal(-1),
cty.NumberIntVal(0),
cty.NilVal,
true,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("slice(%#v, %#v, %#v)", test.List, test.StartIndex, test.EndIndex), func(t *testing.T) {
got, err := Slice(test.List, test.StartIndex, test.EndIndex)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestTranspose(t *testing.T) {
tests := []struct {
Values cty.Value
Want cty.Value
Err bool
}{
{
cty.MapVal(map[string]cty.Value{
"key1": cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
}),
"key2": cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
"key3": cty.ListVal([]cty.Value{
cty.StringVal("c"),
}),
"key4": cty.ListValEmpty(cty.String),
}),
cty.MapVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.StringVal("key1"),
cty.StringVal("key2"),
}),
"b": cty.ListVal([]cty.Value{
cty.StringVal("key1"),
cty.StringVal("key2"),
}),
"c": cty.ListVal([]cty.Value{
cty.StringVal("key2"),
cty.StringVal("key3"),
}),
}),
false,
},
{ // map - unknown value
cty.MapVal(map[string]cty.Value{
"key1": cty.UnknownVal(cty.List(cty.String)),
}),
cty.UnknownVal(cty.Map(cty.List(cty.String))),
false,
},
{ // bad map - empty value
cty.MapVal(map[string]cty.Value{
"key1": cty.ListValEmpty(cty.String),
}),
cty.NilVal,
true,
},
{ // bad map - value not a list
cty.MapVal(map[string]cty.Value{
"key1": cty.StringVal("a"),
}),
cty.NilVal,
true,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("transpose(%#v)", test.Values), func(t *testing.T) {
got, err := Transpose(test.Values)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestValues(t *testing.T) {
tests := []struct {
Values cty.Value
Want cty.Value
Err bool
}{
{
cty.MapVal(map[string]cty.Value{
"hello": cty.StringVal("world"),
"what's": cty.StringVal("up"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("world"),
cty.StringVal("up"),
}),
false,
},
{
cty.ObjectVal(map[string]cty.Value{
"what's": cty.StringVal("up"),
"hello": cty.StringVal("world"),
}),
cty.TupleVal([]cty.Value{
cty.StringVal("world"),
cty.StringVal("up"),
}),
false,
},
{ // empty object
cty.EmptyObjectVal,
cty.EmptyTupleVal,
false,
},
{
cty.UnknownVal(cty.Object(map[string]cty.Type{
"what's": cty.String,
"hello": cty.Bool,
})),
cty.UnknownVal(cty.Tuple([]cty.Type{
cty.Bool,
cty.String,
})),
false,
},
{ // note ordering: keys are sorted first
cty.MapVal(map[string]cty.Value{
"hello": cty.NumberIntVal(1),
"goodbye": cty.NumberIntVal(42),
}),
cty.ListVal([]cty.Value{
cty.NumberIntVal(42),
cty.NumberIntVal(1),
}),
false,
},
{ // map of lists
cty.MapVal(map[string]cty.Value{
"hello": cty.ListVal([]cty.Value{cty.StringVal("world")}),
"what's": cty.ListVal([]cty.Value{cty.StringVal("up")}),
}),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{cty.StringVal("world")}),
cty.ListVal([]cty.Value{cty.StringVal("up")}),
}),
false,
},
{ // map with unknowns
cty.MapVal(map[string]cty.Value{
"hello": cty.ListVal([]cty.Value{cty.StringVal("world")}),
"what's": cty.UnknownVal(cty.List(cty.String)),
}),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{cty.StringVal("world")}),
cty.UnknownVal(cty.List(cty.String)),
}),
false,
},
{ // empty m
cty.MapValEmpty(cty.DynamicPseudoType),
cty.ListValEmpty(cty.DynamicPseudoType),
false,
},
{ // unknown m
cty.UnknownVal(cty.Map(cty.String)),
cty.UnknownVal(cty.List(cty.String)),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("values(%#v)", test.Values), func(t *testing.T) {
got, err := Values(test.Values)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestZipmap(t *testing.T) {
list1 := cty.ListVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("world"),
})
list2 := cty.ListVal([]cty.Value{
cty.StringVal("bar"),
cty.StringVal("baz"),
})
list3 := cty.ListVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("there"),
cty.StringVal("world"),
})
list4 := cty.ListVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(42),
})
list5 := cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("bar"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("baz"),
}),
})
tests := []struct {
Keys cty.Value
Values cty.Value
Want cty.Value
Err bool
}{
{
list1,
list2,
cty.MapVal(map[string]cty.Value{
"hello": cty.StringVal("bar"),
"world": cty.StringVal("baz"),
}),
false,
},
{
list1,
list4,
cty.MapVal(map[string]cty.Value{
"hello": cty.NumberIntVal(1),
"world": cty.NumberIntVal(42),
}),
false,
},
{ // length mismatch
list1,
list3,
cty.NilVal,
true,
},
{ // map of lists
list1,
list5,
cty.MapVal(map[string]cty.Value{
"hello": cty.ListVal([]cty.Value{cty.StringVal("bar")}),
"world": cty.ListVal([]cty.Value{cty.StringVal("baz")}),
}),
false,
},
{ // tuple values produce object
cty.ListVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("world"),
}),
cty.TupleVal([]cty.Value{
cty.StringVal("bar"),
cty.UnknownVal(cty.Bool),
}),
cty.ObjectVal(map[string]cty.Value{
"hello": cty.StringVal("bar"),
"world": cty.UnknownVal(cty.Bool),
}),
false,
},
{ // empty tuple produces empty object
cty.ListValEmpty(cty.String),
cty.EmptyTupleVal,
cty.EmptyObjectVal,
false,
},
{ // tuple with any unknown keys produces DynamicVal
cty.ListVal([]cty.Value{
cty.StringVal("hello"),
cty.UnknownVal(cty.String),
}),
cty.TupleVal([]cty.Value{
cty.StringVal("bar"),
cty.True,
}),
cty.DynamicVal,
false,
},
{ // tuple with all keys unknown produces DynamicVal
cty.UnknownVal(cty.List(cty.String)),
cty.TupleVal([]cty.Value{
cty.StringVal("bar"),
cty.True,
}),
cty.DynamicVal,
false,
},
{ // list with all keys unknown produces correctly-typed unknown map
cty.UnknownVal(cty.List(cty.String)),
cty.ListVal([]cty.Value{
cty.StringVal("bar"),
cty.StringVal("baz"),
}),
cty.UnknownVal(cty.Map(cty.String)),
false,
},
{ // unknown tuple as values produces correctly-typed unknown object
cty.ListVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("world"),
}),
cty.UnknownVal(cty.Tuple([]cty.Type{
cty.String,
cty.Bool,
})),
cty.UnknownVal(cty.Object(map[string]cty.Type{
"hello": cty.String,
"world": cty.Bool,
})),
false,
},
{ // unknown list as values produces correctly-typed unknown map
cty.ListVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("world"),
}),
cty.UnknownVal(cty.List(cty.String)),
cty.UnknownVal(cty.Map(cty.String)),
false,
},
{ // empty input returns an empty map
cty.ListValEmpty(cty.String),
cty.ListValEmpty(cty.String),
cty.MapValEmpty(cty.String),
false,
},
{ // keys cannot be a list of lists
list5,
list1,
cty.NilVal,
true,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("zipmap(%#v, %#v)", test.Keys, test.Values), func(t *testing.T) {
got, err := Zipmap(test.Keys, test.Values)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\n\nkeys: %#v\nvalues: %#v\ngot: %#v\nwant: %#v", test.Keys, test.Values, got, test.Want)
}
})
}
}