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 }