github.com/expr-lang/expr@v1.16.9/test/operator/operator_test.go (about) 1 package operator_test 2 3 import ( 4 "fmt" 5 "testing" 6 "time" 7 8 "github.com/expr-lang/expr/internal/testify/assert" 9 "github.com/expr-lang/expr/internal/testify/require" 10 11 "github.com/expr-lang/expr" 12 "github.com/expr-lang/expr/test/mock" 13 ) 14 15 func TestOperator_struct(t *testing.T) { 16 env := mock.Env{ 17 Time: time.Date(2017, time.October, 23, 18, 30, 0, 0, time.UTC), 18 } 19 20 code := `Time == "2017-10-23"` 21 22 program, err := expr.Compile(code, expr.Env(mock.Env{}), expr.Operator("==", "TimeEqualString")) 23 require.NoError(t, err) 24 25 output, err := expr.Run(program, env) 26 require.NoError(t, err) 27 require.Equal(t, true, output) 28 } 29 30 func TestOperator_no_env(t *testing.T) { 31 code := `Time == "2017-10-23"` 32 require.Panics(t, func() { 33 _, _ = expr.Compile(code, expr.Operator("==", "TimeEqualString")) 34 }) 35 } 36 37 func TestOperator_interface(t *testing.T) { 38 env := mock.Env{} 39 40 code := `Foo == "Foo.String" && "Foo.String" == Foo && Time != Foo && Time == Time` 41 42 program, err := expr.Compile( 43 code, 44 expr.Env(mock.Env{}), 45 expr.Operator("==", "StringerStringEqual", "StringStringerEqual", "StringerStringerEqual"), 46 expr.Operator("!=", "NotStringerStringEqual", "NotStringStringerEqual", "NotStringerStringerEqual"), 47 ) 48 require.NoError(t, err) 49 50 output, err := expr.Run(program, env) 51 require.NoError(t, err) 52 require.Equal(t, true, output) 53 } 54 55 type Value struct { 56 Int int 57 } 58 59 func TestOperator_Function(t *testing.T) { 60 env := map[string]interface{}{ 61 "foo": Value{1}, 62 "bar": Value{2}, 63 } 64 65 tests := []struct { 66 input string 67 want int 68 }{ 69 { 70 input: `foo + bar`, 71 want: 3, 72 }, 73 { 74 input: `2 + 4`, 75 want: 6, 76 }, 77 } 78 79 for _, tt := range tests { 80 t.Run(fmt.Sprintf(`operator function helper test %s`, tt.input), func(t *testing.T) { 81 program, err := expr.Compile( 82 tt.input, 83 expr.Env(env), 84 expr.Operator("+", "Add", "AddInt"), 85 expr.Function("Add", func(args ...interface{}) (interface{}, error) { 86 return args[0].(Value).Int + args[1].(Value).Int, nil 87 }, 88 new(func(_ Value, __ Value) int), 89 ), 90 expr.Function("AddInt", func(args ...interface{}) (interface{}, error) { 91 return args[0].(int) + args[1].(int), nil 92 }, 93 new(func(_ int, __ int) int), 94 ), 95 ) 96 require.NoError(t, err) 97 98 output, err := expr.Run(program, env) 99 require.NoError(t, err) 100 require.Equal(t, tt.want, output) 101 }) 102 } 103 104 } 105 106 func TestOperator_Function_WithTypes(t *testing.T) { 107 env := map[string]interface{}{ 108 "foo": Value{1}, 109 "bar": Value{2}, 110 } 111 112 assert.PanicsWithError(t, `function "Add" for "+" operator misses types`, func() { 113 _, _ = expr.Compile( 114 `foo + bar`, 115 expr.Env(env), 116 expr.Operator("+", "Add", "AddInt"), 117 expr.Function("Add", func(args ...interface{}) (interface{}, error) { 118 return args[0].(Value).Int + args[1].(Value).Int, nil 119 }), 120 ) 121 }) 122 123 assert.PanicsWithError(t, `function "Add" for "+" operator does not have a correct signature`, func() { 124 _, _ = expr.Compile( 125 `foo + bar`, 126 expr.Env(env), 127 expr.Operator("+", "Add", "AddInt"), 128 expr.Function("Add", func(args ...interface{}) (interface{}, error) { 129 return args[0].(Value).Int + args[1].(Value).Int, nil 130 }, 131 new(func(_ Value) int), 132 ), 133 ) 134 }) 135 } 136 137 func TestOperator_FunctionOverTypesPrecedence(t *testing.T) { 138 env := struct { 139 Add func(a, b int) int 140 }{ 141 Add: func(a, b int) int { 142 return a + b 143 }, 144 } 145 146 program, err := expr.Compile( 147 `1 + 2`, 148 expr.Env(env), 149 expr.Operator("+", "Add"), 150 expr.Function("Add", func(args ...interface{}) (interface{}, error) { 151 // Wierd function that returns 100 + a + b in testing purposes. 152 return args[0].(int) + args[1].(int) + 100, nil 153 }, 154 new(func(_ int, __ int) int), 155 ), 156 ) 157 require.NoError(t, err) 158 159 output, err := expr.Run(program, env) 160 require.NoError(t, err) 161 require.Equal(t, 103, output) 162 } 163 164 func TestOperator_CanBeDefinedEitherInTypesOrInFunctions(t *testing.T) { 165 env := struct { 166 Add func(a, b int) int 167 }{ 168 Add: func(a, b int) int { 169 return a + b 170 }, 171 } 172 173 program, err := expr.Compile( 174 `1 + 2`, 175 expr.Env(env), 176 expr.Operator("+", "Add", "AddValues"), 177 expr.Function("AddValues", func(args ...interface{}) (interface{}, error) { 178 return args[0].(Value).Int + args[1].(Value).Int, nil 179 }, 180 new(func(_ Value, __ Value) int), 181 ), 182 ) 183 require.NoError(t, err) 184 185 output, err := expr.Run(program, env) 186 require.NoError(t, err) 187 require.Equal(t, 3, output) 188 } 189 190 func TestOperator_Polymorphic(t *testing.T) { 191 env := struct { 192 Add func(a, b int) int 193 Foo Value 194 Bar Value 195 }{ 196 Add: func(a, b int) int { 197 return a + b 198 }, 199 Foo: Value{1}, 200 Bar: Value{2}, 201 } 202 203 program, err := expr.Compile( 204 `1 + 2 + (Foo + Bar)`, 205 expr.Env(env), 206 expr.Operator("+", "Add", "AddValues"), 207 expr.Function("AddValues", func(args ...interface{}) (interface{}, error) { 208 return args[0].(Value).Int + args[1].(Value).Int, nil 209 }, 210 new(func(_ Value, __ Value) int), 211 ), 212 ) 213 require.NoError(t, err) 214 215 output, err := expr.Run(program, env) 216 require.NoError(t, err) 217 require.Equal(t, 6, output) 218 } 219 220 func TestOperator_recursive_apply(t *testing.T) { 221 type Decimal struct { 222 Int int 223 } 224 225 env := map[string]any{ 226 "add": func(a, b Decimal) Decimal { 227 return Decimal{ 228 Int: a.Int + b.Int, 229 } 230 }, 231 "addInt": func(a Decimal, b int) Decimal { 232 return Decimal{ 233 Int: a.Int + b, 234 } 235 }, 236 "a": Decimal{1}, 237 "b": Decimal{2}, 238 "c": Decimal{3}, 239 "d": Decimal{4}, 240 "e": Decimal{5}, 241 } 242 243 program, err := expr.Compile( 244 `a + b + 100 + c + d + e`, 245 expr.Env(env), 246 expr.Operator("+", "add"), 247 expr.Operator("+", "addInt"), 248 ) 249 require.NoError(t, err) 250 require.Equal(t, `add(add(add(addInt(add(a, b), 100), c), d), e)`, program.Node().String()) 251 252 output, err := expr.Run(program, env) 253 require.NoError(t, err) 254 require.Equal(t, 115, output.(Decimal).Int) 255 }