github.com/MontFerret/ferret@v0.18.0/pkg/runtime/collections/tap_test.go (about) 1 package collections_test 2 3 import ( 4 "context" 5 "testing" 6 7 . "github.com/smartystreets/goconvey/convey" 8 9 "github.com/MontFerret/ferret/pkg/runtime/collections" 10 "github.com/MontFerret/ferret/pkg/runtime/core" 11 "github.com/MontFerret/ferret/pkg/runtime/values" 12 ) 13 14 func tapIterator(values collections.Iterator, predicate core.Expression) collections.Iterator { 15 iter, _ := collections.NewTapIterator(values, predicate) 16 17 return iter 18 } 19 20 type TestExpression struct { 21 fn func(ctx context.Context, scope *core.Scope) (core.Value, error) 22 } 23 24 func (exp *TestExpression) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) { 25 return exp.fn(ctx, scope) 26 } 27 28 type ErrorIterator struct{} 29 30 func (iterator *ErrorIterator) Next(ctx context.Context, scope *core.Scope) (*core.Scope, error) { 31 return nil, core.ErrInvalidOperation 32 } 33 34 func TestTapIterator(t *testing.T) { 35 Convey("Should iterate over a given iterator and execute a predicate", t, func() { 36 arr := values.NewArrayWith( 37 values.NewInt(1), 38 values.NewInt(2), 39 values.NewInt(3), 40 values.NewInt(4), 41 values.NewInt(5), 42 ) 43 44 counter := 0 45 46 iter := tapIterator(arrayIterator(arr), &TestExpression{ 47 fn: func(ctx context.Context, scope *core.Scope) (core.Value, error) { 48 counter++ 49 50 return values.None, nil 51 }, 52 }) 53 54 ctx := context.Background() 55 scope, _ := core.NewRootScope() 56 res, err := collections.ToSlice(ctx, scope, iter) 57 58 So(err, ShouldBeNil) 59 So(res, ShouldHaveLength, int(arr.Length())) 60 So(counter, ShouldEqual, int(arr.Length())) 61 }) 62 63 Convey("Should stop when a predicate return an error", t, func() { 64 arr := values.NewArrayWith( 65 values.NewInt(1), 66 values.NewInt(2), 67 values.NewInt(3), 68 values.NewInt(4), 69 values.NewInt(5), 70 ) 71 72 counter := 0 73 74 iter := tapIterator(arrayIterator(arr), &TestExpression{ 75 fn: func(ctx context.Context, scope *core.Scope) (core.Value, error) { 76 counter++ 77 78 return values.None, core.ErrInvalidOperation 79 }, 80 }) 81 82 ctx := context.Background() 83 scope, _ := core.NewRootScope() 84 _, err := collections.ToSlice(ctx, scope, iter) 85 86 So(err, ShouldNotBeNil) 87 So(counter, ShouldEqual, 1) 88 }) 89 90 Convey("Should not invoke a predicate when underlying iterator returns error", t, func() { 91 counter := 0 92 93 iter := tapIterator(&ErrorIterator{}, &TestExpression{ 94 fn: func(ctx context.Context, scope *core.Scope) (core.Value, error) { 95 counter++ 96 97 return values.None, core.ErrInvalidOperation 98 }, 99 }) 100 101 ctx := context.Background() 102 scope, _ := core.NewRootScope() 103 _, err := collections.ToSlice(ctx, scope, iter) 104 105 So(err, ShouldNotBeNil) 106 So(counter, ShouldEqual, 0) 107 }) 108 109 Convey("Should not invoke a predicate when underlying iterator is empty", t, func() { 110 arr := values.NewArray(0) 111 112 counter := 0 113 114 iter := tapIterator(arrayIterator(arr), &TestExpression{ 115 fn: func(ctx context.Context, scope *core.Scope) (core.Value, error) { 116 counter++ 117 118 return values.None, core.ErrInvalidOperation 119 }, 120 }) 121 122 ctx := context.Background() 123 scope, _ := core.NewRootScope() 124 res, err := collections.ToSlice(ctx, scope, iter) 125 126 So(err, ShouldBeNil) 127 So(res, ShouldHaveLength, 0) 128 So(counter, ShouldEqual, 0) 129 }) 130 }