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 }