github.com/MontFerret/ferret@v0.18.0/pkg/runtime/expressions/member_test.go (about)

     1  package expressions_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/mock"
     8  
     9  	"github.com/MontFerret/ferret/pkg/runtime/core"
    10  	"github.com/MontFerret/ferret/pkg/runtime/expressions"
    11  	"github.com/MontFerret/ferret/pkg/runtime/values"
    12  	"github.com/MontFerret/ferret/pkg/runtime/values/types"
    13  
    14  	. "github.com/smartystreets/goconvey/convey"
    15  )
    16  
    17  type (
    18  	TestObject struct {
    19  		mock.Mock
    20  		*values.Object
    21  		failAt string
    22  	}
    23  )
    24  
    25  func NewTestObject() *TestObject {
    26  	o := new(TestObject)
    27  	o.Object = values.NewObject()
    28  
    29  	return o
    30  }
    31  
    32  func (to *TestObject) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) {
    33  	to.Mock.Called(path)
    34  
    35  	var current core.Value = to.Object
    36  
    37  	for i, segment := range path {
    38  		if segment.String() == to.failAt {
    39  			return values.None, core.NewPathError(core.ErrInvalidPath, i)
    40  		}
    41  
    42  		next, err := values.GetIn(ctx, current, []core.Value{segment})
    43  
    44  		if err != nil {
    45  			return values.None, core.NewPathError(err, i)
    46  		}
    47  
    48  		current = next
    49  	}
    50  
    51  	return current, nil
    52  }
    53  
    54  func (to *TestObject) String() string {
    55  	return to.Object.String()
    56  }
    57  
    58  func TestMemberExpression(t *testing.T) {
    59  	Convey(".Exec", t, func() {
    60  		Convey("Should use .Getter interface if a source implements it", func() {
    61  			o := NewTestObject()
    62  			o.Set("foo", values.NewObjectWith(
    63  				values.NewObjectProperty("bar", values.NewObjectWith(
    64  					values.NewObjectProperty("baz", values.NewObject()),
    65  				)),
    66  			))
    67  
    68  			args := []core.Value{
    69  				values.NewString("foo"),
    70  				values.NewString("bar"),
    71  				values.NewString("baz"),
    72  			}
    73  
    74  			o.On("GetIn", args)
    75  
    76  			s1, _ := expressions.NewMemberPathSegment(
    77  				core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
    78  					return args[0], nil
    79  				}),
    80  				false,
    81  			)
    82  
    83  			s2, _ := expressions.NewMemberPathSegment(
    84  				core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
    85  					return args[1], nil
    86  				}),
    87  				false,
    88  			)
    89  
    90  			s3, _ := expressions.NewMemberPathSegment(
    91  				core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
    92  					return args[2], nil
    93  				}),
    94  				false,
    95  			)
    96  
    97  			segments := []*expressions.MemberPathSegment{s1, s2, s3}
    98  
    99  			exp, err := expressions.NewMemberExpression(
   100  				core.SourceMap{},
   101  				core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
   102  					return o, nil
   103  				}),
   104  				segments,
   105  				nil,
   106  			)
   107  
   108  			So(err, ShouldBeNil)
   109  
   110  			root, cancel := core.NewRootScope()
   111  
   112  			defer func() {
   113  				if err := cancel(); err != nil {
   114  					panic(err)
   115  				}
   116  			}()
   117  
   118  			out, err := exp.Exec(context.Background(), root.Fork())
   119  			So(err, ShouldBeNil)
   120  			So(out.Type().String(), ShouldNotEqual, types.None.String())
   121  
   122  			o.AssertExpectations(t)
   123  		})
   124  
   125  		Convey("Should use generic traverse logic if a source does not implement Getter interface", func() {
   126  			o := values.NewString("abcdefg")
   127  
   128  			args := []core.Value{
   129  				values.NewInt(0),
   130  			}
   131  
   132  			s1, _ := expressions.NewMemberPathSegment(
   133  				core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
   134  					return args[0], nil
   135  				}),
   136  				false,
   137  			)
   138  
   139  			segments := []*expressions.MemberPathSegment{s1}
   140  
   141  			exp, err := expressions.NewMemberExpression(
   142  				core.SourceMap{},
   143  				core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
   144  					return o, nil
   145  				}),
   146  				segments,
   147  				nil,
   148  			)
   149  
   150  			So(err, ShouldBeNil)
   151  
   152  			root, cancel := core.NewRootScope()
   153  
   154  			defer func() {
   155  				if err := cancel(); err != nil {
   156  					panic(err)
   157  				}
   158  			}()
   159  
   160  			out, err := exp.Exec(context.Background(), root.Fork())
   161  			So(err, ShouldBeNil)
   162  			So(out.String(), ShouldEqual, "a")
   163  		})
   164  
   165  		Convey("When path is not optional", func() {
   166  			Convey("Should return an error if it occurs during path resolution", func() {
   167  				o := NewTestObject()
   168  				o.failAt = "bar"
   169  				o.Set("foo", values.NewObjectWith(
   170  					values.NewObjectProperty("bar", values.NewObjectWith(
   171  						values.NewObjectProperty("baz", values.NewObject()),
   172  					)),
   173  				))
   174  
   175  				args := []core.Value{
   176  					values.NewString("foo"),
   177  					values.NewString("bar"),
   178  					values.NewString("baz"),
   179  				}
   180  
   181  				o.On("GetIn", mock.Anything)
   182  
   183  				s1, _ := expressions.NewMemberPathSegment(
   184  					core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
   185  						return args[0], nil
   186  					}),
   187  					false,
   188  				)
   189  
   190  				s2, _ := expressions.NewMemberPathSegment(
   191  					core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
   192  						return args[1], nil
   193  					}),
   194  					false,
   195  				)
   196  
   197  				s3, _ := expressions.NewMemberPathSegment(
   198  					core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
   199  						return args[2], nil
   200  					}),
   201  					false,
   202  				)
   203  
   204  				segments := []*expressions.MemberPathSegment{s1, s2, s3}
   205  
   206  				exp, err := expressions.NewMemberExpression(
   207  					core.SourceMap{},
   208  					core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
   209  						return o, nil
   210  					}),
   211  					segments,
   212  					nil,
   213  				)
   214  
   215  				So(err, ShouldBeNil)
   216  
   217  				root, cancel := core.NewRootScope()
   218  
   219  				defer func() {
   220  					if err := cancel(); err != nil {
   221  						panic(err)
   222  					}
   223  				}()
   224  
   225  				_, err = exp.Exec(context.Background(), root.Fork())
   226  				So(err, ShouldNotBeNil)
   227  				So(err.Error(), ShouldEqual, core.NewPathError(core.ErrInvalidPath, 1).Format(args))
   228  
   229  				o.AssertExpectations(t)
   230  			})
   231  		})
   232  
   233  		Convey("When path is optional", func() {
   234  			Convey("Should return None if it occurs during path resolution", func() {
   235  				o := NewTestObject()
   236  				o.failAt = "bar"
   237  				o.Set("foo", values.NewObjectWith(
   238  					values.NewObjectProperty("bar", values.NewObjectWith(
   239  						values.NewObjectProperty("baz", values.NewObject()),
   240  					)),
   241  				))
   242  
   243  				args := []core.Value{
   244  					values.NewString("foo"),
   245  					values.NewString("bar"),
   246  					values.NewString("baz"),
   247  				}
   248  
   249  				o.On("GetIn", mock.Anything)
   250  
   251  				s1, _ := expressions.NewMemberPathSegment(
   252  					core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
   253  						return args[0], nil
   254  					}),
   255  					true,
   256  				)
   257  
   258  				s2, _ := expressions.NewMemberPathSegment(
   259  					core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
   260  						return args[1], nil
   261  					}),
   262  					true,
   263  				)
   264  
   265  				s3, _ := expressions.NewMemberPathSegment(
   266  					core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
   267  						return args[2], nil
   268  					}),
   269  					true,
   270  				)
   271  
   272  				segments := []*expressions.MemberPathSegment{s1, s2, s3}
   273  
   274  				exp, err := expressions.NewMemberExpression(
   275  					core.SourceMap{},
   276  					core.AsExpression(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
   277  						return o, nil
   278  					}),
   279  					segments,
   280  					nil,
   281  				)
   282  
   283  				So(err, ShouldBeNil)
   284  
   285  				root, cancel := core.NewRootScope()
   286  
   287  				defer func() {
   288  					if err := cancel(); err != nil {
   289  						panic(err)
   290  					}
   291  				}()
   292  
   293  				out, err := exp.Exec(context.Background(), root.Fork())
   294  				So(err, ShouldBeNil)
   295  				So(out.Type().String(), ShouldEqual, values.None.Type().String())
   296  
   297  				o.AssertExpectations(t)
   298  			})
   299  		})
   300  	})
   301  }