11package query
22
33import (
4+ "context"
5+ "errors"
6+ "runtime"
7+ "runtime/debug"
8+ "sync"
49 "sync/atomic"
510 "testing"
611 "time"
@@ -9,6 +14,7 @@ import (
914 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
1015 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
1116 "go.uber.org/mock/gomock"
17+ "google.golang.org/grpc"
1218
1319 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
1420)
@@ -58,5 +64,126 @@ func TestSessionCoreCancelAttachOnDone(t *testing.T) {
5864 core .closeOnce ()
5965 require .GreaterOrEqual (t , recvMsgCounter .Load (), uint32 (2 ))
6066 require .LessOrEqual (t , recvMsgCounter .Load (), uint32 (3 ))
67+ require .Equal (t , core .Status (), StatusClosed .String ())
68+ }, xtest .StopAfter (time .Second ))
69+ }
70+
71+ func TestSessionCoreAttachError (t * testing.T ) {
72+ xtest .TestManyTimes (t , func (t testing.TB ) {
73+ ctx := xtest .Context (t )
74+ ctrl := gomock .NewController (t )
75+ client := NewMockQueryServiceClient (ctrl )
76+ client .EXPECT ().CreateSession (gomock .Any (), gomock .Any ()).Return (& Ydb_Query.CreateSessionResponse {
77+ Status : Ydb .StatusIds_SUCCESS ,
78+ SessionId : "123" ,
79+ }, nil )
80+ var sessionDeletes atomic.Int32
81+ done := make (chan struct {})
82+ sessionDeletes .Store (0 )
83+ client .EXPECT ().DeleteSession (gomock .Any (), gomock .Any ()).DoAndReturn (
84+ func (
85+ context.Context ,
86+ * Ydb_Query.DeleteSessionRequest ,
87+ ... grpc.CallOption ,
88+ ) (* Ydb_Query.DeleteSessionResponse , error ) {
89+ sessionDeletes .Add (1 )
90+
91+ return & Ydb_Query.DeleteSessionResponse {}, nil
92+ })
93+ attachStream := NewMockQueryService_AttachSessionClient (ctrl )
94+ attachStream .EXPECT ().Recv ().DoAndReturn (func () (* Ydb_Query.SessionState , error ) {
95+ return nil , errSessionClosed
96+ }).AnyTimes ()
97+ client .EXPECT ().AttachSession (gomock .Any (), & Ydb_Query.AttachSessionRequest {
98+ SessionId : "123" ,
99+ }).Return (attachStream , nil )
100+ core , err := Open (ctx , client , func (core * sessionCore ) {
101+ core .done = done
102+ })
103+ require .Error (t , err , errSessionClosed )
104+ require .Nil (t , core )
105+ }, xtest .StopAfter (time .Second ))
106+ }
107+
108+ func TestSessionCoreClose (t * testing.T ) {
109+ debug .SetTraceback ("all" )
110+ xtest .TestManyTimes (t , func (t testing.TB ) {
111+ ctx := xtest .Context (t )
112+ ctrl := gomock .NewController (t )
113+ client := NewMockQueryServiceClient (ctrl )
114+ client .EXPECT ().CreateSession (gomock .Any (), gomock .Any ()).Return (& Ydb_Query.CreateSessionResponse {
115+ Status : Ydb .StatusIds_SUCCESS ,
116+ SessionId : "123" ,
117+ }, nil )
118+ attachStream := NewMockQueryService_AttachSessionClient (ctrl )
119+ var (
120+ done chan struct {}
121+ startRecv = make (chan struct {}, 1 )
122+ stopRecv = make (chan struct {}, 1 )
123+ unblock atomic.Bool
124+ sessionDeletes atomic.Uint32
125+ )
126+ unblock .Store (false )
127+ sessionDeletes .Store (0 )
128+ attachStream .EXPECT ().Recv ().DoAndReturn (func () (* Ydb_Query.SessionState , error ) {
129+ startRecv <- struct {}{}
130+ select {
131+ case <- done :
132+ return nil , errSessionClosed
133+ case stopRecv <- struct {}{}:
134+ return & Ydb_Query.SessionState {
135+ Status : Ydb .StatusIds_SUCCESS ,
136+ }, nil
137+ }
138+ }).AnyTimes ()
139+ client .EXPECT ().AttachSession (gomock .Any (), & Ydb_Query.AttachSessionRequest {
140+ SessionId : "123" ,
141+ }).Return (attachStream , nil )
142+ client .EXPECT ().DeleteSession (gomock .Any (), gomock .Any ()).DoAndReturn (
143+ func (
144+ context.Context ,
145+ * Ydb_Query.DeleteSessionRequest ,
146+ ... grpc.CallOption ,
147+ ) (* Ydb_Query.DeleteSessionResponse , error ) {
148+ if sessionDeletes .CompareAndSwap (0 , 1 ) {
149+ return & Ydb_Query.DeleteSessionResponse {
150+ Status : Ydb .StatusIds_SUCCESS ,
151+ }, nil
152+ }
153+ sessionDeletes .Add (1 )
154+
155+ return nil , errors .New ("session not found" )
156+ }).AnyTimes ()
157+ core , err := Open (ctx , client , func (core * sessionCore ) {
158+ done = core .done
159+ })
160+ require .NoError (t , err )
161+ require .NotNil (t , core )
162+ <- stopRecv
163+
164+ var wg sync.WaitGroup
165+ parallel := runtime .GOMAXPROCS (0 )
166+ if parallel > 10 {
167+ parallel = 10
168+ }
169+ for i := 0 ; i < parallel ; i ++ {
170+ wg .Add (1 )
171+ go func (i int ) {
172+ defer wg .Done ()
173+ for {
174+ if unblock .Load () {
175+ _ = core .Close (ctx )
176+
177+ break
178+ }
179+ }
180+ }(i )
181+ }
182+ unblock .Store (true )
183+ wg .Wait ()
184+ _ , ok := <- done
185+ require .False (t , ok )
186+ require .GreaterOrEqual (t , sessionDeletes .Load (), uint32 (1 ))
187+ require .LessOrEqual (t , sessionDeletes .Load (), uint32 (10 ))
61188 }, xtest .StopAfter (time .Second ))
62189}
0 commit comments