Skip to content

Commit 1cb2902

Browse files
committed
feat: add proxy read test
1 parent 0c2671e commit 1cb2902

File tree

2 files changed

+112
-9
lines changed

2 files changed

+112
-9
lines changed

vm/runtime/runtime.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,18 @@ import (
1212

1313
// Proxy is an interface that allows intercepting object property read and write operations.
1414
type Proxy interface {
15-
ProxyGetter
16-
}
17-
18-
// ProxyGetter is an interface that allows intercepting object property access.
19-
type ProxyGetter interface {
2015
// GetProperty returns the value of the property with the given key.
21-
GetProperty(key string) (any, bool)
16+
GetProperty(key any) (any, bool)
17+
18+
// SetProperty sets the value of the property with the given key.
19+
SetProperty(key, value any)
2220
}
2321

2422
func Fetch(from, i any) any {
25-
if proxy, ok := from.(ProxyGetter); ok {
26-
r, ok := proxy.GetProperty(i.(string))
23+
if proxy, ok := from.(Proxy); ok {
24+
r, ok := proxy.GetProperty(i)
2725
if !ok {
28-
panic(fmt.Sprintf("cannot fetch %v from %T", i, from))
26+
panic(fmt.Sprintf("cannot fetch %v from proxy", i))
2927
}
3028

3129
return r

vm/vm_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/expr-lang/expr/file"
1212
"github.com/expr-lang/expr/internal/testify/require"
13+
"github.com/expr-lang/expr/vm/runtime"
1314

1415
"github.com/expr-lang/expr"
1516
"github.com/expr-lang/expr/checker"
@@ -267,6 +268,110 @@ func TestRun_InnerMethodWithError_NilSafe(t *testing.T) {
267268
require.Equal(t, nil, out)
268269
}
269270

271+
var _ runtime.Proxy = (*proxyNode)(nil)
272+
273+
type proxyNode struct {
274+
parent *proxyNode
275+
values map[any]any
276+
}
277+
278+
func (n *proxyNode) GetProperty(key any) (any, bool) {
279+
if value, ok := n.values[key]; ok {
280+
return value, true
281+
}
282+
if n.parent != nil {
283+
return n.parent.GetProperty(key)
284+
}
285+
return nil, false
286+
}
287+
288+
func (n *proxyNode) SetProperty(key any, value any) {
289+
n.values[key] = value
290+
}
291+
292+
func TestRun_Proxy_Read(t *testing.T) {
293+
cases := []struct {
294+
label string
295+
expr string
296+
env any
297+
expect any
298+
}{
299+
{
300+
label: "proxy env map member",
301+
expr: `foo.bar`,
302+
env: map[string]any{
303+
"foo": &proxyNode{
304+
values: map[any]any{
305+
"bar": "baz",
306+
},
307+
},
308+
},
309+
expect: "baz",
310+
},
311+
{
312+
label: "read from root",
313+
expr: `value`,
314+
env: &proxyNode{
315+
values: map[any]any{
316+
"value": "hello world",
317+
},
318+
},
319+
expect: "hello world",
320+
},
321+
{
322+
label: "read from child",
323+
expr: `child.value`,
324+
env: &proxyNode{
325+
values: map[any]any{
326+
"child": &proxyNode{
327+
values: map[any]any{
328+
"value": "hello world",
329+
},
330+
},
331+
},
332+
},
333+
expect: "hello world",
334+
},
335+
{
336+
label: "read from grandchild",
337+
expr: `grandchild.child.value`,
338+
env: &proxyNode{
339+
values: map[any]any{
340+
"grandchild": &proxyNode{
341+
values: map[any]any{
342+
"child": &proxyNode{
343+
values: map[any]any{
344+
"value": "hello world",
345+
},
346+
},
347+
},
348+
},
349+
},
350+
},
351+
expect: "hello world",
352+
},
353+
}
354+
355+
for _, c := range cases {
356+
t.Run(c.label, func(t *testing.T) {
357+
tree, err := parser.Parse(c.expr)
358+
require.NoError(t, err)
359+
360+
funcConf := conf.CreateNew()
361+
_, err = checker.Check(tree, funcConf)
362+
require.NoError(t, err)
363+
364+
program, err := compiler.Compile(tree, funcConf)
365+
require.NoError(t, err)
366+
367+
out, err := vm.Run(program, c.env)
368+
require.NoError(t, err)
369+
370+
require.Equal(t, c.expect, out)
371+
})
372+
}
373+
}
374+
270375
func TestRun_TaggedFieldName(t *testing.T) {
271376
input := `value`
272377

0 commit comments

Comments
 (0)