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  }