mirror of
https://github.com/danog/liquid.git
synced 2024-12-02 12:47:45 +01:00
c32908a4f3
When user-defined function is called, it does convert all the arguments using MustConvert, which panics on error with an error. However 'func (e expression) Evaluate(ctx Context)' from sub package "expressions" catches those panics and converts some of them to errors. Raw errors returned from strconv functions are "repanicked", but proper error types such as values.TypeError are not. Hence we should use it here.
192 lines
4.9 KiB
Go
192 lines
4.9 KiB
Go
package values
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
yaml "gopkg.in/yaml.v2"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type redConvertible struct{}
|
|
|
|
func (c redConvertible) ToLiquid() interface{} {
|
|
return "red"
|
|
}
|
|
|
|
var convertTests = []struct {
|
|
value, expected interface{}
|
|
}{
|
|
{nil, false},
|
|
{false, 0},
|
|
{false, int(0)},
|
|
{false, int8(0)},
|
|
{false, int16(0)},
|
|
{false, int32(0)},
|
|
{false, int64(0)},
|
|
{false, uint(0)},
|
|
{false, uint8(0)},
|
|
{false, uint16(0)},
|
|
{false, uint32(0)},
|
|
{false, uint64(0)},
|
|
{true, 1},
|
|
{true, int(1)},
|
|
{true, int8(1)},
|
|
{true, int16(1)},
|
|
{true, int32(1)},
|
|
{true, int64(1)},
|
|
{true, uint(1)},
|
|
{true, uint8(1)},
|
|
{true, uint16(1)},
|
|
{true, uint32(1)},
|
|
{true, uint64(1)},
|
|
{false, false},
|
|
{true, true},
|
|
{true, "true"},
|
|
{false, "false"},
|
|
{0, true},
|
|
{2, 2},
|
|
{2, "2"},
|
|
{2, 2.0},
|
|
{"", true},
|
|
{"2", int(2)},
|
|
{"2", int8(2)},
|
|
{"2", int16(2)},
|
|
{"2", int32(2)},
|
|
{"2", int64(2)},
|
|
{"2", uint(2)},
|
|
{"2", uint8(2)},
|
|
{"2", uint16(2)},
|
|
{"2", uint32(2)},
|
|
{"2", uint64(2)},
|
|
{"2", 2},
|
|
{"2", 2.0},
|
|
{"2.0", 2.0},
|
|
{"2.1", 2.1},
|
|
{"2.1", float32(2.1)},
|
|
{"2.1", float64(2.1)},
|
|
{"string", "string"},
|
|
{[]interface{}{1, 2}, []interface{}{1, 2}},
|
|
{[]int{1, 2}, []int{1, 2}},
|
|
{[]int{1, 2}, []interface{}{1, 2}},
|
|
{[]interface{}{1, 2}, []int{1, 2}},
|
|
{[]int{1, 2}, []string{"1", "2"}},
|
|
{yaml.MapSlice{{Key: 1, Value: 1}}, []interface{}{1}},
|
|
{yaml.MapSlice{{Key: 1, Value: 1}}, []string{"1"}},
|
|
{yaml.MapSlice{{Key: 1, Value: "a"}}, []string{"a"}},
|
|
{yaml.MapSlice{{Key: 1, Value: "a"}}, map[interface{}]interface{}{1: "a"}},
|
|
{yaml.MapSlice{{Key: 1, Value: "a"}}, map[int]string{1: "a"}},
|
|
{yaml.MapSlice{{Key: 1, Value: "a"}}, map[string]string{"1": "a"}},
|
|
{yaml.MapSlice{{Key: "a", Value: 1}}, map[string]string{"a": "1"}},
|
|
{yaml.MapSlice{{Key: "a", Value: nil}}, map[string]interface{}{"a": nil}},
|
|
{yaml.MapSlice{{Key: nil, Value: 1}}, map[interface{}]string{nil: "1"}},
|
|
// {"March 14, 2016", time.Now(), timeMustParse("2016-03-14T00:00:00Z")},
|
|
{redConvertible{}, "red"},
|
|
}
|
|
|
|
var convertErrorTests = []struct {
|
|
value, proto interface{}
|
|
expected []string
|
|
}{
|
|
{map[string]bool{"k": true}, map[int]bool{}, []string{"map key"}},
|
|
{map[string]string{"k": "v"}, map[string]int{}, []string{"map element"}},
|
|
{map[interface{}]interface{}{"k": "v"}, map[string]int{}, []string{"map element"}},
|
|
{"notanumber", int(0), []string{"can't convert string", "to type int"}},
|
|
{"notanumber", uint(0), []string{"can't convert string", "to type uint"}},
|
|
{"notanumber", float64(0), []string{"can't convert string", "to type float64"}},
|
|
}
|
|
|
|
func TestConvert(t *testing.T) {
|
|
for i, test := range convertTests {
|
|
t.Run(fmt.Sprintf("%02d", i+1), func(t *testing.T) {
|
|
typ := reflect.TypeOf(test.expected)
|
|
name := fmt.Sprintf("Convert %#v -> %v", test.value, typ)
|
|
value, err := Convert(test.value, typ)
|
|
require.NoErrorf(t, err, name)
|
|
require.Equalf(t, test.expected, value, name)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConvert_errors(t *testing.T) {
|
|
for i, test := range convertErrorTests {
|
|
t.Run(fmt.Sprintf("%02d", i+1), func(t *testing.T) {
|
|
typ := reflect.TypeOf(test.proto)
|
|
name := fmt.Sprintf("Convert %#v -> %v", test.value, typ)
|
|
_, err := Convert(test.value, typ)
|
|
require.Errorf(t, err, name)
|
|
for _, expected := range test.expected {
|
|
require.Containsf(t, err.Error(), expected, name)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConvert_map(t *testing.T) {
|
|
typ := reflect.TypeOf(map[string]string{})
|
|
v, err := Convert(map[interface{}]interface{}{"key": "value"}, typ)
|
|
require.NoError(t, err)
|
|
m, ok := v.(map[string]string)
|
|
require.True(t, ok)
|
|
require.Equal(t, "value", m["key"])
|
|
}
|
|
|
|
func TestConvert_map_synonym(t *testing.T) {
|
|
type VariableMap map[interface{}]interface{}
|
|
typ := reflect.TypeOf(map[string]string{})
|
|
v, err := Convert(VariableMap{"key": "value"}, typ)
|
|
require.NoError(t, err)
|
|
m, ok := v.(map[string]string)
|
|
require.True(t, ok)
|
|
require.Equal(t, "value", m["key"])
|
|
}
|
|
|
|
func TestConvert_map_to_array(t *testing.T) {
|
|
typ := reflect.TypeOf([]string{})
|
|
v, err := Convert(map[int]string{1: "b", 2: "a"}, typ)
|
|
require.NoError(t, err)
|
|
array, ok := v.([]string)
|
|
require.True(t, ok)
|
|
sort.Strings(array)
|
|
require.Equal(t, []string{"a", "b"}, array)
|
|
}
|
|
|
|
// func TestConvert_ptr(t *testing.T) {
|
|
// typ := reflect.PtrTo(reflect.TypeOf(""))
|
|
// v, err := Convert("a", typ)
|
|
// require.NoError(t, err)
|
|
// ptr, ok := v.(*string)
|
|
// fmt.Printf("%#v %T\n", v, v)
|
|
// require.True(t, ok)
|
|
// require.NotNil(t, ptr)
|
|
// require.Equal(t, "ab", *ptr)
|
|
// }
|
|
|
|
func TestMustConvert(t *testing.T) {
|
|
typ := reflect.TypeOf("")
|
|
v := MustConvert(2, typ)
|
|
require.Equal(t, "2", v)
|
|
|
|
typ = reflect.TypeOf(2)
|
|
require.Panics(t, func() { MustConvert("x", typ) })
|
|
}
|
|
|
|
func TestMustConvertItem(t *testing.T) {
|
|
v := MustConvertItem(2, []string{})
|
|
require.Equal(t, "2", v)
|
|
|
|
require.Panics(t, func() { MustConvertItem("x", []int{}) })
|
|
}
|
|
|
|
func timeMustParse(s string) time.Time {
|
|
t, err := time.Parse(time.RFC3339, s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return t
|
|
}
|