github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/topic/topicreaderinternal/reader_test.go (about)

     1  package topicreaderinternal
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"runtime"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  	"go.uber.org/mock/gomock"
    11  
    12  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/empty"
    13  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicreader"
    14  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/topic/topicreadercommon"
    15  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
    16  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
    17  )
    18  
    19  func TestReader_Close(t *testing.T) {
    20  	xtest.TestManyTimes(t, func(t testing.TB) {
    21  		mc := gomock.NewController(t)
    22  		defer mc.Finish()
    23  
    24  		testErr := errors.New("test error")
    25  		readerContext, readerCancel := xcontext.WithCancel(context.Background())
    26  		baseReader := NewMockbatchedStreamReader(mc)
    27  		baseReader.EXPECT().ReadMessageBatch(gomock.Any(), ReadMessageBatchOptions{}).
    28  			DoAndReturn(func(ctx context.Context, options ReadMessageBatchOptions) (*topicreadercommon.PublicBatch, error) {
    29  				<-readerContext.Done()
    30  
    31  				return nil, testErr
    32  			})
    33  		baseReader.EXPECT().ReadMessageBatch(
    34  			gomock.Any(),
    35  			ReadMessageBatchOptions{batcherGetOptions: batcherGetOptions{MaxCount: 1, MinCount: 1}},
    36  		).DoAndReturn(func(ctx context.Context, options ReadMessageBatchOptions) (*topicreadercommon.PublicBatch, error) {
    37  			<-readerContext.Done()
    38  
    39  			return nil, testErr
    40  		})
    41  		baseReader.EXPECT().Commit(gomock.Any(), gomock.Any()).DoAndReturn(
    42  			func(ctx context.Context, commitRange topicreadercommon.CommitRange) error {
    43  				<-readerContext.Done()
    44  
    45  				return testErr
    46  			})
    47  		baseReader.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, _ error) error {
    48  			readerCancel()
    49  
    50  			return nil
    51  		})
    52  
    53  		reader := &Reader{
    54  			reader: baseReader,
    55  		}
    56  
    57  		type callState struct {
    58  			callCompleted empty.Chan
    59  			err           error
    60  		}
    61  
    62  		isCallCompleted := func(state *callState) bool {
    63  			select {
    64  			case <-state.callCompleted:
    65  				return true
    66  			default:
    67  				return false
    68  			}
    69  		}
    70  
    71  		var allStates []*callState
    72  		newCallState := func() *callState {
    73  			state := &callState{
    74  				callCompleted: make(empty.Chan),
    75  			}
    76  			allStates = append(allStates, state)
    77  
    78  			return state
    79  		}
    80  
    81  		readerCommitState := newCallState()
    82  		readerReadMessageState := newCallState()
    83  		readerReadMessageBatchState := newCallState()
    84  
    85  		go func() {
    86  			readerCommitState.err = reader.Commit(
    87  				context.Background(),
    88  				topicreadercommon.MessageWithSetCommitRangeForTest(
    89  					&topicreadercommon.PublicMessage{},
    90  					topicreadercommon.CommitRange{
    91  						PartitionSession: &topicreadercommon.PartitionSession{},
    92  					},
    93  				),
    94  			)
    95  			close(readerCommitState.callCompleted)
    96  		}()
    97  
    98  		go func() {
    99  			_, readerReadMessageState.err = reader.ReadMessage(context.Background())
   100  			close(readerReadMessageState.callCompleted)
   101  		}()
   102  
   103  		go func() {
   104  			_, readerReadMessageBatchState.err = reader.ReadMessageBatch(context.Background())
   105  			close(readerReadMessageBatchState.callCompleted)
   106  		}()
   107  
   108  		runtime.Gosched()
   109  
   110  		// check about no methods finished before close
   111  		for i := range allStates {
   112  			require.False(t, isCallCompleted(allStates[i]))
   113  		}
   114  		require.NoError(t, reader.Close(context.Background()))
   115  
   116  		// check about all methods stop work after close
   117  		for i := range allStates {
   118  			<-allStates[i].callCompleted
   119  			require.Error(t, allStates[i].err, i)
   120  		}
   121  	})
   122  }
   123  
   124  func TestReader_Commit(t *testing.T) {
   125  	t.Run("OK", func(t *testing.T) {
   126  		mc := gomock.NewController(t)
   127  		defer mc.Finish()
   128  
   129  		readerID := topicreadercommon.NextReaderID()
   130  		baseReader := NewMockbatchedStreamReader(mc)
   131  		reader := &Reader{
   132  			reader:   baseReader,
   133  			readerID: readerID,
   134  		}
   135  
   136  		expectedRangeOk := topicreadercommon.CommitRange{
   137  			CommitOffsetStart: 1,
   138  			CommitOffsetEnd:   10,
   139  			PartitionSession:  newTestPartitionSessionReaderID(readerID, 10),
   140  		}
   141  		baseReader.EXPECT().Commit(gomock.Any(), expectedRangeOk).Return(nil)
   142  		require.NoError(t, reader.Commit(
   143  			context.Background(),
   144  			topicreadercommon.MessageWithSetCommitRangeForTest(&topicreadercommon.PublicMessage{}, expectedRangeOk),
   145  		))
   146  
   147  		expectedRangeErr := topicreadercommon.CommitRange{
   148  			CommitOffsetStart: 15,
   149  			CommitOffsetEnd:   20,
   150  			PartitionSession:  newTestPartitionSessionReaderID(readerID, 30),
   151  		}
   152  
   153  		testErr := errors.New("test err")
   154  		baseReader.EXPECT().Commit(gomock.Any(), expectedRangeErr).Return(testErr)
   155  		require.ErrorIs(t, reader.Commit(
   156  			context.Background(),
   157  			topicreadercommon.MessageWithSetCommitRangeForTest(
   158  				&topicreadercommon.PublicMessage{},
   159  				expectedRangeErr,
   160  			),
   161  		), testErr)
   162  	})
   163  
   164  	t.Run("CommitFromOtherReader", func(t *testing.T) {
   165  		ctx := xtest.Context(t)
   166  		reader := &Reader{readerID: 1}
   167  		forCommit := topicreadercommon.CommitRange{
   168  			CommitOffsetStart: 1,
   169  			CommitOffsetEnd:   2,
   170  			PartitionSession:  newTestPartitionSessionReaderID(2, 0),
   171  		}
   172  		err := reader.Commit(ctx, forCommit)
   173  		require.ErrorIs(t, err, errCommitSessionFromOtherReader)
   174  	})
   175  }
   176  
   177  func TestReader_WaitInit(t *testing.T) {
   178  	mc := gomock.NewController(t)
   179  	defer mc.Finish()
   180  
   181  	readerID := topicreadercommon.NextReaderID()
   182  	baseReader := NewMockbatchedStreamReader(mc)
   183  	reader := &Reader{
   184  		reader:   baseReader,
   185  		readerID: readerID,
   186  	}
   187  
   188  	baseReader.EXPECT().WaitInit(gomock.Any())
   189  	err := reader.WaitInit(context.Background())
   190  	require.NoError(t, err)
   191  }
   192  
   193  func newTestPartitionSessionReaderID(
   194  	readerID int64,
   195  	partitionSessionID rawtopicreader.PartitionSessionID,
   196  ) *topicreadercommon.PartitionSession {
   197  	return topicreadercommon.NewPartitionSession(
   198  		context.Background(),
   199  		"",
   200  		0,
   201  		readerID,
   202  		"",
   203  		partitionSessionID,
   204  		int64(partitionSessionID+100),
   205  		0,
   206  	)
   207  }