github.com/expr-lang/expr@v1.16.9/test/deref/deref_test.go (about)

     1  package deref_test
     2  
     3  import (
     4  	"context"
     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  )
    13  
    14  func TestDeref_binary(t *testing.T) {
    15  	i := 1
    16  	env := map[string]any{
    17  		"i": &i,
    18  		"obj": map[string]any{
    19  			"i": &i,
    20  		},
    21  	}
    22  	t.Run("==", func(t *testing.T) {
    23  		program, err := expr.Compile(`i == 1 && obj.i == 1`, expr.Env(env))
    24  		require.NoError(t, err)
    25  
    26  		out, err := expr.Run(program, env)
    27  		require.NoError(t, err)
    28  		require.Equal(t, true, out)
    29  	})
    30  	t.Run("><", func(t *testing.T) {
    31  		program, err := expr.Compile(`i > 0 && obj.i < 99`, expr.Env(env))
    32  		require.NoError(t, err)
    33  
    34  		out, err := expr.Run(program, env)
    35  		require.NoError(t, err)
    36  		require.Equal(t, true, out)
    37  	})
    38  	t.Run("??+", func(t *testing.T) {
    39  		program, err := expr.Compile(`(i ?? obj.i) + 1`, expr.Env(env))
    40  		require.NoError(t, err)
    41  
    42  		out, err := expr.Run(program, env)
    43  		require.NoError(t, err)
    44  		require.Equal(t, 2, out)
    45  	})
    46  }
    47  
    48  func TestDeref_unary(t *testing.T) {
    49  	i := 1
    50  	ok := true
    51  	env := map[string]any{
    52  		"i": &i,
    53  		"obj": map[string]any{
    54  			"ok": &ok,
    55  		},
    56  	}
    57  
    58  	program, err := expr.Compile(`-i < 0 && !!obj.ok`, expr.Env(env))
    59  	require.NoError(t, err)
    60  
    61  	out, err := expr.Run(program, env)
    62  	require.NoError(t, err)
    63  	require.Equal(t, true, out)
    64  }
    65  
    66  func TestDeref_eval(t *testing.T) {
    67  	i := 1
    68  	env := map[string]any{
    69  		"i": &i,
    70  		"obj": map[string]any{
    71  			"i": &i,
    72  		},
    73  	}
    74  	out, err := expr.Eval(`i == 1 && obj.i == 1`, env)
    75  	require.NoError(t, err)
    76  	require.Equal(t, true, out)
    77  }
    78  
    79  func TestDeref_emptyCtx(t *testing.T) {
    80  	program, err := expr.Compile(`ctx`)
    81  	require.NoError(t, err)
    82  
    83  	output, err := expr.Run(program, map[string]any{
    84  		"ctx": context.Background(),
    85  	})
    86  	require.NoError(t, err)
    87  	require.Implements(t, new(context.Context), output)
    88  }
    89  
    90  func TestDeref_emptyCtx_Eval(t *testing.T) {
    91  	output, err := expr.Eval(`ctx`, map[string]any{
    92  		"ctx": context.Background(),
    93  	})
    94  	require.NoError(t, err)
    95  	require.Implements(t, new(context.Context), output)
    96  }
    97  
    98  func TestDeref_context_WithValue(t *testing.T) {
    99  	program, err := expr.Compile(`ctxWithValue`)
   100  	require.NoError(t, err)
   101  
   102  	output, err := expr.Run(program, map[string]any{
   103  		"ctxWithValue": context.WithValue(context.Background(), "value", "test"),
   104  	})
   105  	require.NoError(t, err)
   106  	require.Implements(t, new(context.Context), output)
   107  }
   108  
   109  func TestDeref_method_on_int_pointer(t *testing.T) {
   110  	output, err := expr.Eval(`foo.Bar()`, map[string]any{
   111  		"foo": new(foo),
   112  	})
   113  	require.NoError(t, err)
   114  	require.Equal(t, 42, output)
   115  }
   116  
   117  type foo int
   118  
   119  func (f *foo) Bar() int {
   120  	return 42
   121  }
   122  
   123  func TestDeref_multiple_pointers(t *testing.T) {
   124  	a := 42
   125  	b := &a
   126  	c := &b
   127  	t.Run("returned as is", func(t *testing.T) {
   128  		output, err := expr.Eval(`c`, map[string]any{
   129  			"c": c,
   130  		})
   131  		require.NoError(t, err)
   132  		require.Equal(t, c, output)
   133  		require.IsType(t, (**int)(nil), output)
   134  	})
   135  	t.Run("+ works", func(t *testing.T) {
   136  		output, err := expr.Eval(`c+2`, map[string]any{
   137  			"c": c,
   138  		})
   139  		require.NoError(t, err)
   140  		require.Equal(t, 44, output)
   141  	})
   142  }
   143  
   144  func TestDeref_pointer_of_interface(t *testing.T) {
   145  	v := 42
   146  	a := &v
   147  	b := any(a)
   148  	c := any(&b)
   149  	t.Run("returned as is", func(t *testing.T) {
   150  		output, err := expr.Eval(`c`, map[string]any{
   151  			"c": c,
   152  		})
   153  		require.NoError(t, err)
   154  		require.Equal(t, c, output)
   155  		require.IsType(t, (*interface{})(nil), output)
   156  	})
   157  	t.Run("+ works", func(t *testing.T) {
   158  		output, err := expr.Eval(`c+2`, map[string]any{
   159  			"c": c,
   160  		})
   161  		require.NoError(t, err)
   162  		require.Equal(t, 44, output)
   163  	})
   164  }
   165  
   166  func TestDeref_nil(t *testing.T) {
   167  	var b *int = nil
   168  	c := &b
   169  	t.Run("returned as is", func(t *testing.T) {
   170  		output, err := expr.Eval(`c`, map[string]any{
   171  			"c": c,
   172  		})
   173  		require.NoError(t, err)
   174  		require.Equal(t, c, output)
   175  		require.IsType(t, (**int)(nil), output)
   176  	})
   177  	t.Run("== nil works", func(t *testing.T) {
   178  		output, err := expr.Eval(`c == nil`, map[string]any{
   179  			"c": c,
   180  		})
   181  		require.NoError(t, err)
   182  		require.Equal(t, true, output)
   183  	})
   184  }
   185  
   186  func TestDeref_nil_in_pointer_of_interface(t *testing.T) {
   187  	var a *int32 = nil
   188  	b := any(a)
   189  	c := any(&b)
   190  	t.Run("returned as is", func(t *testing.T) {
   191  		output, err := expr.Eval(`c`, map[string]any{
   192  			"c": c,
   193  		})
   194  		require.NoError(t, err)
   195  		require.Equal(t, c, output)
   196  		require.IsType(t, (*interface{})(nil), output)
   197  	})
   198  	t.Run("== nil works", func(t *testing.T) {
   199  		output, err := expr.Eval(`c == nil`, map[string]any{
   200  			"c": c,
   201  		})
   202  		require.NoError(t, err)
   203  		require.Equal(t, true, output)
   204  	})
   205  }
   206  
   207  func TestDeref_сommutative(t *testing.T) {
   208  	a := "ok"
   209  	b := "ok"
   210  
   211  	type Env struct {
   212  		A string
   213  		B *string
   214  	}
   215  
   216  	env := Env{
   217  		A: a,
   218  		B: &b,
   219  	}
   220  
   221  	tests := []struct {
   222  		code string
   223  		want bool
   224  	}{
   225  		{`A == B`, true},
   226  		{`B == A`, true},
   227  		{`A != B`, false},
   228  		{`B != A`, false},
   229  	}
   230  
   231  	for _, test := range tests {
   232  		t.Run(test.code, func(t *testing.T) {
   233  			program, err := expr.Compile(test.code, expr.Env(env))
   234  			require.NoError(t, err)
   235  
   236  			out, err := expr.Run(program, env)
   237  			require.NoError(t, err)
   238  			require.Equal(t, test.want, out)
   239  		})
   240  	}
   241  }
   242  
   243  func TestDeref_fetch_from_interface_mix_pointer(t *testing.T) {
   244  	type FooBar struct {
   245  		Value string
   246  	}
   247  	foobar := &FooBar{"waldo"}
   248  	var foobarAny any = foobar
   249  	var foobarPtrAny any = &foobarAny
   250  
   251  	res, err := expr.Eval("foo.Value", map[string]any{
   252  		"foo": foobarPtrAny,
   253  	})
   254  	assert.NoError(t, err)
   255  	assert.Equal(t, "waldo", res)
   256  }
   257  
   258  func TestDeref_func_args(t *testing.T) {
   259  	i := 20
   260  	env := map[string]any{
   261  		"var": &i,
   262  		"fn": func(p int) int {
   263  			return p + 1
   264  		},
   265  	}
   266  
   267  	program, err := expr.Compile(`fn(var) + fn(var + 0)`, expr.Env(env))
   268  	require.NoError(t, err)
   269  
   270  	out, err := expr.Run(program, env)
   271  	require.NoError(t, err)
   272  	require.Equal(t, 42, out)
   273  }
   274  
   275  func TestDeref_struct_func_args(t *testing.T) {
   276  	n, _ := time.Parse(time.RFC3339, "2024-05-12T18:30:00+00:00")
   277  	duration := 30 * time.Minute
   278  	env := map[string]any{
   279  		"time":     n,
   280  		"duration": &duration,
   281  	}
   282  
   283  	program, err := expr.Compile(`time.Add(duration).Format('2006-01-02T15:04:05Z07:00')`, expr.Env(env))
   284  	require.NoError(t, err)
   285  
   286  	out, err := expr.Run(program, env)
   287  	require.NoError(t, err)
   288  	require.Equal(t, "2024-05-12T19:00:00Z", out)
   289  }
   290  
   291  func TestDeref_ignore_func_args(t *testing.T) {
   292  	f := foo(1)
   293  	env := map[string]any{
   294  		"foo": &f,
   295  		"fn": func(f *foo) int {
   296  			return f.Bar()
   297  		},
   298  	}
   299  
   300  	program, err := expr.Compile(`fn(foo)`, expr.Env(env))
   301  	require.NoError(t, err)
   302  
   303  	out, err := expr.Run(program, env)
   304  	require.NoError(t, err)
   305  	require.Equal(t, 42, out)
   306  }
   307  
   308  func TestDeref_ignore_struct_func_args(t *testing.T) {
   309  	n := time.Now()
   310  	location, _ := time.LoadLocation("UTC")
   311  	env := map[string]any{
   312  		"time":     n,
   313  		"location": location,
   314  	}
   315  
   316  	program, err := expr.Compile(`time.In(location).Location().String()`, expr.Env(env))
   317  	require.NoError(t, err)
   318  
   319  	out, err := expr.Run(program, env)
   320  	require.NoError(t, err)
   321  	require.Equal(t, "UTC", out)
   322  }