functions: add tests and support for unknown values
This commit is contained in:
parent
d802d5c624
commit
a213c4a648
|
@ -171,9 +171,16 @@ var CompactFunc = function.New(&function.Spec{
|
|||
},
|
||||
Type: function.StaticReturnType(cty.List(cty.String)),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
listVal := args[0]
|
||||
if !listVal.IsWhollyKnown() {
|
||||
// If some of the element values aren't known yet then we
|
||||
// can't yet return a compacted list
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
var outputList []cty.Value
|
||||
|
||||
for it := args[0].ElementIterator(); it.Next(); {
|
||||
for it := listVal.ElementIterator(); it.Next(); {
|
||||
_, v := it.Element()
|
||||
if v.AsString() == "" {
|
||||
continue
|
||||
|
@ -263,11 +270,19 @@ var DistinctFunc = function.New(&function.Spec{
|
|||
Type: cty.List(cty.DynamicPseudoType),
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.List(cty.DynamicPseudoType)),
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
return args[0].Type(), nil
|
||||
},
|
||||
// Type: function.StaticReturnType(cty.List(cty.DynamicPseudoType)),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
listVal := args[0]
|
||||
|
||||
if !listVal.IsWhollyKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
var list []cty.Value
|
||||
|
||||
for it := args[0].ElementIterator(); it.Next(); {
|
||||
for it := listVal.ElementIterator(); it.Next(); {
|
||||
_, v := it.Element()
|
||||
list, err = appendIfMissing(list, v)
|
||||
if err != nil {
|
||||
|
@ -296,6 +311,11 @@ var ChunklistFunc = function.New(&function.Spec{
|
|||
return cty.List(args[0].Type()), nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
listVal := args[0]
|
||||
if !listVal.IsWhollyKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
var size int
|
||||
err = gocty.FromCtyValue(args[1], &size)
|
||||
if err != nil {
|
||||
|
@ -310,7 +330,7 @@ var ChunklistFunc = function.New(&function.Spec{
|
|||
|
||||
// if size is 0, returns a list made of the initial list
|
||||
if size == 0 {
|
||||
output = append(output, args[0])
|
||||
output = append(output, listVal)
|
||||
return cty.ListVal(output), nil
|
||||
}
|
||||
|
||||
|
@ -319,7 +339,7 @@ var ChunklistFunc = function.New(&function.Spec{
|
|||
l := args[0].LengthInt()
|
||||
i := 0
|
||||
|
||||
for it := args[0].ElementIterator(); it.Next(); {
|
||||
for it := listVal.ElementIterator(); it.Next(); {
|
||||
_, v := it.Element()
|
||||
chunk = append(chunk, v)
|
||||
|
||||
|
@ -347,9 +367,12 @@ var FlattenFunc = function.New(&function.Spec{
|
|||
Type: function.StaticReturnType(cty.List(cty.DynamicPseudoType)),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
inputList := args[0]
|
||||
if !inputList.IsWhollyKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
if inputList.LengthInt() == 0 {
|
||||
return cty.ListValEmpty(cty.DynamicPseudoType), nil
|
||||
return cty.ListValEmpty(retType.ElementType()), nil
|
||||
}
|
||||
outputList := make([]cty.Value, 0)
|
||||
|
||||
|
@ -458,11 +481,14 @@ var LookupFunc = function.New(&function.Spec{
|
|||
AllowDynamicType: true,
|
||||
AllowNull: true,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.DynamicPseudoType),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
Type: func(args []cty.Value) (ret cty.Type, err error) {
|
||||
if len(args) < 1 || len(args) > 3 {
|
||||
return cty.NilVal, fmt.Errorf("lookup() takes two or three arguments, got %d", len(args))
|
||||
return cty.NilType, fmt.Errorf("lookup() takes two or three arguments, got %d", len(args))
|
||||
}
|
||||
|
||||
return args[0].Type().ElementType(), nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
var defaultVal cty.Value
|
||||
defaultValueSet := false
|
||||
|
||||
|
@ -474,6 +500,10 @@ var LookupFunc = function.New(&function.Spec{
|
|||
mapVar := args[0]
|
||||
lookupKey := args[1].AsString()
|
||||
|
||||
if !mapVar.IsWhollyKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
if mapVar.HasIndex(cty.StringVal(lookupKey)) == cty.True {
|
||||
v := mapVar.Index(cty.StringVal(lookupKey))
|
||||
if ty := v.Type(); !ty.Equals(cty.NilType) {
|
||||
|
@ -489,13 +519,11 @@ var LookupFunc = function.New(&function.Spec{
|
|||
}
|
||||
|
||||
if defaultValueSet {
|
||||
defaultType := defaultVal.Type()
|
||||
switch {
|
||||
case defaultType.Equals(cty.String):
|
||||
return cty.StringVal(defaultVal.AsString()), nil
|
||||
case defaultType.Equals(cty.Number):
|
||||
return cty.NumberVal(defaultVal.AsBigFloat()), nil
|
||||
defaultVal, err = convert.Convert(defaultVal, retType)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
return defaultVal, nil
|
||||
}
|
||||
|
||||
return cty.UnknownVal(cty.DynamicPseudoType), fmt.Errorf(
|
||||
|
@ -537,6 +565,12 @@ var MapFunc = function.New(&function.Spec{
|
|||
return cty.Map(valType), nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
for _, arg := range args {
|
||||
if !arg.IsWhollyKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
}
|
||||
|
||||
outputMap := make(map[string]cty.Value)
|
||||
|
||||
for i := 0; i < len(args); i += 2 {
|
||||
|
@ -612,6 +646,10 @@ var MatchkeysFunc = function.New(&function.Spec{
|
|||
return cty.ListValEmpty(retType.ElementType()), nil
|
||||
}
|
||||
|
||||
if !args[0].IsWhollyKnown() || !args[0].IsWhollyKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
i := 0
|
||||
for it := keys.ElementIterator(); it.Next(); {
|
||||
_, key := it.Element()
|
||||
|
@ -659,7 +697,9 @@ var MergeFunc = function.New(&function.Spec{
|
|||
outputMap := make(map[string]cty.Value)
|
||||
|
||||
for _, arg := range args {
|
||||
|
||||
if !arg.IsWhollyKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
if !arg.Type().IsObjectType() && !arg.Type().IsMapType() {
|
||||
return cty.NilVal, fmt.Errorf("arguments must be maps or objects, got %#v", arg.Type().FriendlyName())
|
||||
}
|
||||
|
@ -694,6 +734,9 @@ var SliceFunc = function.New(&function.Spec{
|
|||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
inputList := args[0]
|
||||
if !inputList.IsWhollyKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
var startIndex, endIndex int
|
||||
|
||||
if err = gocty.FromCtyValue(args[1], &startIndex); err != nil {
|
||||
|
@ -791,25 +834,14 @@ var ValuesFunc = function.New(&function.Spec{
|
|||
},
|
||||
},
|
||||
Type: func(args []cty.Value) (ret cty.Type, err error) {
|
||||
values := args[0]
|
||||
argTypes := make([]cty.Type, values.LengthInt())
|
||||
index := 0
|
||||
|
||||
for it := values.ElementIterator(); it.Next(); {
|
||||
_, v := it.Element()
|
||||
argTypes[index] = v.Type()
|
||||
index++
|
||||
}
|
||||
|
||||
valType, _ := convert.UnifyUnsafe(argTypes)
|
||||
if valType == cty.NilType {
|
||||
return cty.NilType, fmt.Errorf("map elements must have the same type")
|
||||
}
|
||||
|
||||
return cty.List(valType), nil
|
||||
return cty.List(args[0].Type().ElementType()), nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
mapVar := args[0]
|
||||
if !mapVar.IsWhollyKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
keys, err := Keys(mapVar)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
|
|
|
@ -299,6 +299,34 @@ func TestCoalesceList(t *testing.T) {
|
|||
}),
|
||||
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),
|
||||
|
@ -375,6 +403,15 @@ func TestCompact(t *testing.T) {
|
|||
}),
|
||||
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{
|
||||
|
@ -422,6 +459,12 @@ func TestContains(t *testing.T) {
|
|||
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
|
||||
|
@ -435,6 +478,12 @@ func TestContains(t *testing.T) {
|
|||
cty.BoolVal(true),
|
||||
false,
|
||||
},
|
||||
{
|
||||
listWithUnknown,
|
||||
cty.StringVal("the"),
|
||||
cty.BoolVal(true),
|
||||
false,
|
||||
},
|
||||
{
|
||||
listOfStrings,
|
||||
cty.StringVal("penguin"),
|
||||
|
@ -509,6 +558,16 @@ func TestIndex(t *testing.T) {
|
|||
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"),
|
||||
|
@ -620,6 +679,16 @@ func TestDistinct(t *testing.T) {
|
|||
}),
|
||||
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"),
|
||||
|
@ -765,6 +834,16 @@ func TestChunklist(t *testing.T) {
|
|||
}),
|
||||
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 {
|
||||
|
@ -812,6 +891,20 @@ func TestFlatten(t *testing.T) {
|
|||
}),
|
||||
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),
|
||||
|
@ -861,6 +954,11 @@ func TestKeys(t *testing.T) {
|
|||
cty.NilVal,
|
||||
true,
|
||||
},
|
||||
{ // Unknown map
|
||||
cty.UnknownVal(cty.Map(cty.String)),
|
||||
cty.UnknownVal(cty.List(cty.String)),
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
@ -927,6 +1025,17 @@ func TestList(t *testing.T) {
|
|||
}),
|
||||
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 {
|
||||
|
@ -962,6 +1071,10 @@ func TestLookup(t *testing.T) {
|
|||
cty.StringVal("baz"),
|
||||
}),
|
||||
})
|
||||
mapWithUnknowns := cty.MapVal(map[string]cty.Value{
|
||||
"foo": cty.StringVal("bar"),
|
||||
"baz": cty.UnknownVal(cty.String),
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
Values []cty.Value
|
||||
|
@ -1046,6 +1159,14 @@ func TestLookup(t *testing.T) {
|
|||
cty.NilVal,
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]cty.Value{
|
||||
mapWithUnknowns,
|
||||
cty.StringVal("baz"),
|
||||
},
|
||||
cty.UnknownVal(cty.String),
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
@ -1084,6 +1205,14 @@ func TestMap(t *testing.T) {
|
|||
}),
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]cty.Value{
|
||||
cty.StringVal("hello"),
|
||||
cty.UnknownVal(cty.String),
|
||||
},
|
||||
cty.UnknownVal(cty.Map(cty.String)),
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]cty.Value{
|
||||
cty.StringVal("hello"),
|
||||
|
@ -1307,6 +1436,23 @@ func TestMatchkeys(t *testing.T) {
|
|||
}),
|
||||
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{
|
||||
|
@ -1396,6 +1542,18 @@ func TestMerge(t *testing.T) {
|
|||
}),
|
||||
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{
|
||||
|
@ -1548,6 +1706,10 @@ func TestSlice(t *testing.T) {
|
|||
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
|
||||
|
@ -1564,6 +1726,13 @@ func TestSlice(t *testing.T) {
|
|||
}),
|
||||
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),
|
||||
|
@ -1661,6 +1830,13 @@ func TestTranspose(t *testing.T) {
|
|||
}),
|
||||
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),
|
||||
|
@ -1736,6 +1912,14 @@ func TestValues(t *testing.T) {
|
|||
}),
|
||||
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.UnknownVal(cty.List(cty.List(cty.String))),
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
|
Loading…
Reference in New Issue