mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
256 lines
5.6 KiB
Go
256 lines
5.6 KiB
Go
package operator_test
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/expr-lang/expr/internal/testify/assert"
|
|
"github.com/expr-lang/expr/internal/testify/require"
|
|
|
|
"github.com/expr-lang/expr"
|
|
"github.com/expr-lang/expr/test/mock"
|
|
)
|
|
|
|
func TestOperator_struct(t *testing.T) {
|
|
env := mock.Env{
|
|
Time: time.Date(2017, time.October, 23, 18, 30, 0, 0, time.UTC),
|
|
}
|
|
|
|
code := `Time == "2017-10-23"`
|
|
|
|
program, err := expr.Compile(code, expr.Env(mock.Env{}), expr.Operator("==", "TimeEqualString"))
|
|
require.NoError(t, err)
|
|
|
|
output, err := expr.Run(program, env)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, output)
|
|
}
|
|
|
|
func TestOperator_no_env(t *testing.T) {
|
|
code := `Time == "2017-10-23"`
|
|
require.Panics(t, func() {
|
|
_, _ = expr.Compile(code, expr.Operator("==", "TimeEqualString"))
|
|
})
|
|
}
|
|
|
|
func TestOperator_interface(t *testing.T) {
|
|
env := mock.Env{}
|
|
|
|
code := `Foo == "Foo.String" && "Foo.String" == Foo && Time != Foo && Time == Time`
|
|
|
|
program, err := expr.Compile(
|
|
code,
|
|
expr.Env(mock.Env{}),
|
|
expr.Operator("==", "StringerStringEqual", "StringStringerEqual", "StringerStringerEqual"),
|
|
expr.Operator("!=", "NotStringerStringEqual", "NotStringStringerEqual", "NotStringerStringerEqual"),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
output, err := expr.Run(program, env)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, output)
|
|
}
|
|
|
|
type Value struct {
|
|
Int int
|
|
}
|
|
|
|
func TestOperator_Function(t *testing.T) {
|
|
env := map[string]interface{}{
|
|
"foo": Value{1},
|
|
"bar": Value{2},
|
|
}
|
|
|
|
tests := []struct {
|
|
input string
|
|
want int
|
|
}{
|
|
{
|
|
input: `foo + bar`,
|
|
want: 3,
|
|
},
|
|
{
|
|
input: `2 + 4`,
|
|
want: 6,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(fmt.Sprintf(`operator function helper test %s`, tt.input), func(t *testing.T) {
|
|
program, err := expr.Compile(
|
|
tt.input,
|
|
expr.Env(env),
|
|
expr.Operator("+", "Add", "AddInt"),
|
|
expr.Function("Add", func(args ...interface{}) (interface{}, error) {
|
|
return args[0].(Value).Int + args[1].(Value).Int, nil
|
|
},
|
|
new(func(_ Value, __ Value) int),
|
|
),
|
|
expr.Function("AddInt", func(args ...interface{}) (interface{}, error) {
|
|
return args[0].(int) + args[1].(int), nil
|
|
},
|
|
new(func(_ int, __ int) int),
|
|
),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
output, err := expr.Run(program, env)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.want, output)
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func TestOperator_Function_WithTypes(t *testing.T) {
|
|
env := map[string]interface{}{
|
|
"foo": Value{1},
|
|
"bar": Value{2},
|
|
}
|
|
|
|
assert.PanicsWithError(t, `function "Add" for "+" operator misses types`, func() {
|
|
_, _ = expr.Compile(
|
|
`foo + bar`,
|
|
expr.Env(env),
|
|
expr.Operator("+", "Add", "AddInt"),
|
|
expr.Function("Add", func(args ...interface{}) (interface{}, error) {
|
|
return args[0].(Value).Int + args[1].(Value).Int, nil
|
|
}),
|
|
)
|
|
})
|
|
|
|
assert.PanicsWithError(t, `function "Add" for "+" operator does not have a correct signature`, func() {
|
|
_, _ = expr.Compile(
|
|
`foo + bar`,
|
|
expr.Env(env),
|
|
expr.Operator("+", "Add", "AddInt"),
|
|
expr.Function("Add", func(args ...interface{}) (interface{}, error) {
|
|
return args[0].(Value).Int + args[1].(Value).Int, nil
|
|
},
|
|
new(func(_ Value) int),
|
|
),
|
|
)
|
|
})
|
|
}
|
|
|
|
func TestOperator_FunctionOverTypesPrecedence(t *testing.T) {
|
|
env := struct {
|
|
Add func(a, b int) int
|
|
}{
|
|
Add: func(a, b int) int {
|
|
return a + b
|
|
},
|
|
}
|
|
|
|
program, err := expr.Compile(
|
|
`1 + 2`,
|
|
expr.Env(env),
|
|
expr.Operator("+", "Add"),
|
|
expr.Function("Add", func(args ...interface{}) (interface{}, error) {
|
|
// Weird function that returns 100 + a + b for testing purposes.
|
|
return args[0].(int) + args[1].(int) + 100, nil
|
|
},
|
|
new(func(_ int, __ int) int),
|
|
),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
output, err := expr.Run(program, env)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 103, output)
|
|
}
|
|
|
|
func TestOperator_CanBeDefinedEitherInTypesOrInFunctions(t *testing.T) {
|
|
env := struct {
|
|
Add func(a, b int) int
|
|
}{
|
|
Add: func(a, b int) int {
|
|
return a + b
|
|
},
|
|
}
|
|
|
|
program, err := expr.Compile(
|
|
`1 + 2`,
|
|
expr.Env(env),
|
|
expr.Operator("+", "Add", "AddValues"),
|
|
expr.Function("AddValues", func(args ...interface{}) (interface{}, error) {
|
|
return args[0].(Value).Int + args[1].(Value).Int, nil
|
|
},
|
|
new(func(_ Value, __ Value) int),
|
|
),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
output, err := expr.Run(program, env)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, output)
|
|
}
|
|
|
|
func TestOperator_Polymorphic(t *testing.T) {
|
|
env := struct {
|
|
Add func(a, b int) int
|
|
Foo Value
|
|
Bar Value
|
|
}{
|
|
Add: func(a, b int) int {
|
|
return a + b
|
|
},
|
|
Foo: Value{1},
|
|
Bar: Value{2},
|
|
}
|
|
|
|
program, err := expr.Compile(
|
|
`1 + 2 + (Foo + Bar)`,
|
|
expr.Env(env),
|
|
expr.Operator("+", "Add", "AddValues"),
|
|
expr.Function("AddValues", func(args ...interface{}) (interface{}, error) {
|
|
return args[0].(Value).Int + args[1].(Value).Int, nil
|
|
},
|
|
new(func(_ Value, __ Value) int),
|
|
),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
output, err := expr.Run(program, env)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 6, output)
|
|
}
|
|
|
|
func TestOperator_recursive_apply(t *testing.T) {
|
|
type Decimal struct {
|
|
Int int
|
|
}
|
|
|
|
env := map[string]any{
|
|
"add": func(a, b Decimal) Decimal {
|
|
return Decimal{
|
|
Int: a.Int + b.Int,
|
|
}
|
|
},
|
|
"addInt": func(a Decimal, b int) Decimal {
|
|
return Decimal{
|
|
Int: a.Int + b,
|
|
}
|
|
},
|
|
"a": Decimal{1},
|
|
"b": Decimal{2},
|
|
"c": Decimal{3},
|
|
"d": Decimal{4},
|
|
"e": Decimal{5},
|
|
}
|
|
|
|
program, err := expr.Compile(
|
|
`a + b + 100 + c + d + e`,
|
|
expr.Env(env),
|
|
expr.Operator("+", "add"),
|
|
expr.Operator("+", "addInt"),
|
|
)
|
|
require.NoError(t, err)
|
|
require.Equal(t, `add(add(add(addInt(add(a, b), 100), c), d), e)`, program.Node().String())
|
|
|
|
output, err := expr.Run(program, env)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 115, output.(Decimal).Int)
|
|
}
|