github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/pipeline/async_test.go (about)

     1  // Copyright (c) 2021-present Voedger Authors.
     2  // This source code is licensed under the MIT license found in the
     3  // LICENSE file in the root directory of this source tree.
     4  
     5  package pipeline
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func Test_p_release(t *testing.T) {
    18  	t.Run("Should release workpiece", func(t *testing.T) {
    19  		release := false
    20  
    21  		p_release(testWorkpiece{func() {
    22  			release = true
    23  		}})
    24  
    25  		require.True(t, release)
    26  	})
    27  	t.Run("Should not release workpiece because workpiece is nil", func(t *testing.T) {
    28  		release := false
    29  
    30  		p_release(nil)
    31  
    32  		require.False(t, release)
    33  	})
    34  }
    35  
    36  func Test_p_flush(t *testing.T) {
    37  	testErr := errors.New("error occurred")
    38  	t.Run("Should flush", func(t *testing.T) {
    39  		operator := WireAsyncOperator("operator", mockAsyncOp().
    40  			flush(func(callback OpFuncFlush) (err error) {
    41  				callback(testWorkpiece{})
    42  				return nil
    43  			}).create())
    44  		operator.ctx = context.Background()
    45  
    46  		p_flush(operator, "test")
    47  
    48  		require.Len(t, operator.Stdout, 1)
    49  		require.Equal(t, testWorkpiece{}, <-operator.Stdout)
    50  	})
    51  	t.Run("Should no flush by error reason", func(t *testing.T) {
    52  		operator := WireAsyncOperator("operator", mockAsyncOp().
    53  			flush(func(callback OpFuncFlush) (err error) {
    54  				return testErr
    55  			}).create())
    56  		operator.ctx = context.Background()
    57  
    58  		p_flush(operator, "test")
    59  
    60  		require.Len(t, operator.Stdout, 1)
    61  		err := <-operator.Stdout
    62  		require.ErrorIs(t, err.(*errPipeline), testErr)
    63  	})
    64  	t.Run("Should no flush by error in ctx reason", func(t *testing.T) {
    65  		operator := WireAsyncOperator("operator", mockAsyncOp().
    66  			flush(func(callback OpFuncFlush) (err error) {
    67  				callback(testWorkpiece{})
    68  				return nil
    69  			}).create())
    70  		operator.ctx = testContext{err: testErr}
    71  
    72  		p_flush(operator, "test")
    73  
    74  		require.Empty(t, operator.Stdout)
    75  	})
    76  }
    77  
    78  func Test_flushTimer_stop(t *testing.T) {
    79  	flushTimer := flushTimer{
    80  		active: true,
    81  		timer:  time.NewTimer(time.Millisecond),
    82  	}
    83  	time.Sleep(time.Duration(2) * time.Millisecond)
    84  
    85  	require.NotPanics(t, func() {
    86  		flushTimer.stop()
    87  	})
    88  }
    89  
    90  func Test_puller_async(t *testing.T) {
    91  	t.Run("Should release workpiece and continue when wired operator isn not active", func(t *testing.T) {
    92  		release := false
    93  		doAsync := false
    94  		wg := new(sync.WaitGroup)
    95  		wg.Add(1)
    96  		work := testWorkpiece{func() {
    97  			release = true
    98  			wg.Done()
    99  		}}
   100  		operator := &WiredOperator{
   101  			Operator: mockAsyncOp().doAsync(func(ctx context.Context, work IWorkpiece) (outWork IWorkpiece, err error) {
   102  				doAsync = true
   103  				return nil, err
   104  			}).create(),
   105  			Stdin:  make(chan interface{}, 1),
   106  			Stdout: make(chan interface{}, 1),
   107  			ctx:    context.Background(),
   108  			err:    errPipeline{},
   109  		}
   110  		go puller_async(operator)
   111  		operator.Stdin <- work
   112  		wg.Wait()
   113  		close(operator.Stdout)
   114  
   115  		require.True(t, release)
   116  		require.False(t, doAsync)
   117  	})
   118  	t.Run("Should forward error and continue", func(t *testing.T) {
   119  		doAsync := false
   120  		operator := &WiredOperator{
   121  			Operator: mockAsyncOp().doAsync(func(ctx context.Context, work IWorkpiece) (outWork IWorkpiece, err error) {
   122  				doAsync = true
   123  				return nil, err
   124  			}).create(),
   125  			Stdin:  make(chan interface{}, 1),
   126  			Stdout: make(chan interface{}, 1),
   127  			ctx:    context.Background(),
   128  		}
   129  		go puller_async(operator)
   130  		operator.Stdin <- errPipeline{}
   131  		err := <-operator.Stdout
   132  		close(operator.Stdout)
   133  
   134  		require.Equal(t, errPipeline{}, err)
   135  		require.False(t, doAsync)
   136  	})
   137  	t.Run("Should send error when error occurred at doAsync method", func(t *testing.T) {
   138  		doAsync := false
   139  		operator := &WiredOperator{
   140  			Operator: mockAsyncOp().
   141  				doAsync(func(ctx context.Context, work IWorkpiece) (outWork IWorkpiece, err error) {
   142  					doAsync = true
   143  					return nil, errors.New("boom")
   144  				}).create(),
   145  			Stdin:  make(chan interface{}, 1),
   146  			Stdout: make(chan interface{}, 1),
   147  			ctx:    context.Background(),
   148  		}
   149  		go puller_async(operator)
   150  		operator.Stdin <- testWorkpiece{}
   151  		err := <-operator.Stdout
   152  		close(operator.Stdout)
   153  
   154  		require.IsType(t, new(errPipeline), err)
   155  		require.True(t, doAsync)
   156  	})
   157  }