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 }