@@ -252,8 +252,7 @@ func TestPublisher_keepAliveAsyncPause(t *testing.T) {
252252 wg .Wait ()
253253}
254254
255- // Test case for key deletion and re-registration (covers lines 148-155)
256- func TestPublisher_keepAliveAsyncKeyDeletion (t * testing.T ) {
255+ func TestPublisher_keepAliveAsyncDeleteRePutsWhenLeaseAlive (t * testing.T ) {
257256 ctrl := gomock .NewController (t )
258257 defer ctrl .Finish ()
259258 const id clientv3.LeaseID = 1
@@ -262,33 +261,24 @@ func TestPublisher_keepAliveAsyncKeyDeletion(t *testing.T) {
262261 defer restore ()
263262 cli .EXPECT ().Ctx ().AnyTimes ()
264263 cli .EXPECT ().KeepAlive (gomock .Any (), id )
265-
266- // Create a watch channel that will send a delete event
267- watchChan := make (chan clientv3.WatchResponse , 1 )
268- watchResp := clientv3.WatchResponse {
269- Events : []* clientv3.Event {{
270- Type : clientv3 .EventTypeDelete ,
271- Kv : & mvccpb.KeyValue {
272- Key : []byte ("thekey" ),
273- },
274- }},
275- }
276- watchChan <- watchResp
277-
278- cli .EXPECT ().Watch (gomock .Any (), gomock .Any (), gomock .Any ()).Return ((<- chan clientv3.WatchResponse )(watchChan ))
264+ cli .EXPECT ().Watch (gomock .Any (), gomock .Any (), gomock .Any ()).Return (newDeleteWatchChan ("thekey" ))
265+ cli .EXPECT ().TimeToLive (gomock .Any (), id ).Return (& clientv3.LeaseTimeToLiveResponse {
266+ TTL : 1 ,
267+ }, nil )
279268
280269 var wg sync.WaitGroup
281- wg .Add (1 ) // Only wait for Revoke call
282-
283- // Use a channel to signal when Put has been called
270+ wg .Add (1 )
284271 putCalled := make (chan struct {})
285-
286- // Expect the re-put operation when key is deleted
287272 cli .EXPECT ().Put (gomock .Any (), "thekey" , "thevalue" , gomock .Any ()).Do (func (_ , _ , _ , _ any ) {
288- close (putCalled ) // Signal that Put has been called
273+ close (putCalled )
289274 }).Return (nil , nil )
290-
291- // Expect revoke when Stop is called
275+ cli .EXPECT ().Get (gomock .Any (), "thekey" ).Return (& clientv3.GetResponse {
276+ Kvs : []* mvccpb.KeyValue {{
277+ Key : []byte ("thekey" ),
278+ Value : []byte ("thevalue" ),
279+ Lease : int64 (id ),
280+ }},
281+ }, nil )
292282 cli .EXPECT ().Revoke (gomock .Any (), id ).Do (func (_ , _ any ) {
293283 wg .Done ()
294284 })
@@ -305,8 +295,7 @@ func TestPublisher_keepAliveAsyncKeyDeletion(t *testing.T) {
305295 wg .Wait ()
306296}
307297
308- // Test case for key deletion with re-put error (covers error branch in lines 151-152)
309- func TestPublisher_keepAliveAsyncKeyDeletionPutError (t * testing.T ) {
298+ func TestPublisher_keepAliveAsyncDeleteSkipsPutAndRestartsWhenLeaseExpired (t * testing.T ) {
310299 ctrl := gomock .NewController (t )
311300 defer ctrl .Finish ()
312301 const id clientv3.LeaseID = 1
@@ -315,47 +304,108 @@ func TestPublisher_keepAliveAsyncKeyDeletionPutError(t *testing.T) {
315304 defer restore ()
316305 cli .EXPECT ().Ctx ().AnyTimes ()
317306 cli .EXPECT ().KeepAlive (gomock .Any (), id )
307+ cli .EXPECT ().Watch (gomock .Any (), gomock .Any (), gomock .Any ()).Return (newDeleteWatchChan ("thekey" ))
308+ cli .EXPECT ().TimeToLive (gomock .Any (), id ).Return (& clientv3.LeaseTimeToLiveResponse {
309+ TTL : 0 ,
310+ }, nil )
318311
319- // Create a watch channel that will send a delete event
320- watchChan := make (chan clientv3.WatchResponse , 1 )
321- watchResp := clientv3.WatchResponse {
322- Events : []* clientv3.Event {{
323- Type : clientv3 .EventTypeDelete ,
324- Kv : & mvccpb.KeyValue {
325- Key : []byte ("thekey" ),
326- },
327- }},
328- }
329- watchChan <- watchResp
312+ restarted := make (chan struct {})
313+ cli .EXPECT ().Revoke (gomock .Any (), id ).Do (func (_ , _ any ) {
314+ close (restarted )
315+ })
330316
331- cli .EXPECT ().Watch (gomock .Any (), gomock .Any (), gomock .Any ()).Return ((<- chan clientv3.WatchResponse )(watchChan ))
317+ pub := NewPublisher (nil , "thekey" , "thevalue" )
318+ pub .lease = id
319+ pub .fullKey = "thekey"
332320
333- var wg sync.WaitGroup
334- wg .Add (1 ) // Only wait for Revoke call
321+ assert .Nil (t , pub .keepAliveAsync (cli ))
322+ waitForSignal (t , restarted )
323+ pub .Stop ()
324+ }
335325
336- // Use a channel to signal when Put has been called
337- putCalled := make (chan struct {})
326+ func TestPublisher_keepAliveAsyncDeleteSkipsPutWhenTTLUnavailable (t * testing.T ) {
327+ tests := []struct {
328+ name string
329+ resp * clientv3.LeaseTimeToLiveResponse
330+ err error
331+ leaseID clientv3.LeaseID
332+ }{
333+ {
334+ name : "ttl error" ,
335+ err : errors .New ("ttl error" ),
336+ leaseID : 1 ,
337+ },
338+ {
339+ name : "ttl nil response" ,
340+ resp : nil ,
341+ leaseID : 2 ,
342+ },
343+ }
338344
339- // Expect the re-put operation to fail
340- cli .EXPECT ().Put (gomock .Any (), "thekey" , "thevalue" , gomock .Any ()).Do (func (_ , _ , _ , _ any ) {
341- close (putCalled ) // Signal that Put has been called
342- }).Return (nil , errors .New ("put error" ))
345+ for _ , tt := range tests {
346+ t .Run (tt .name , func (t * testing.T ) {
347+ ctrl := gomock .NewController (t )
348+ defer ctrl .Finish ()
349+
350+ cli := internal .NewMockEtcdClient (ctrl )
351+ restore := setMockClient (cli )
352+ defer restore ()
353+
354+ cli .EXPECT ().Ctx ().AnyTimes ()
355+ cli .EXPECT ().KeepAlive (gomock .Any (), tt .leaseID )
356+ cli .EXPECT ().Watch (gomock .Any (), gomock .Any (), gomock .Any ()).Return (newDeleteWatchChan ("thekey" ))
357+ cli .EXPECT ().TimeToLive (gomock .Any (), tt .leaseID ).Return (tt .resp , tt .err )
358+
359+ restarted := make (chan struct {})
360+ cli .EXPECT ().Revoke (gomock .Any (), tt .leaseID ).Do (func (_ , _ any ) {
361+ close (restarted )
362+ })
363+
364+ pub := NewPublisher (nil , "thekey" , "thevalue" )
365+ pub .lease = tt .leaseID
366+ pub .fullKey = "thekey"
367+
368+ assert .Nil (t , pub .keepAliveAsync (cli ))
369+ waitForSignal (t , restarted )
370+ pub .Stop ()
371+ })
372+ }
373+ }
374+
375+ func TestPublisher_keepAliveAsyncDeleteRestartsWhenRePutLosesLease (t * testing.T ) {
376+ ctrl := gomock .NewController (t )
377+ defer ctrl .Finish ()
378+ const id clientv3.LeaseID = 1
379+ cli := internal .NewMockEtcdClient (ctrl )
380+ restore := setMockClient (cli )
381+ defer restore ()
382+ cli .EXPECT ().Ctx ().AnyTimes ()
383+ cli .EXPECT ().KeepAlive (gomock .Any (), id )
384+ cli .EXPECT ().Watch (gomock .Any (), gomock .Any (), gomock .Any ()).Return (newDeleteWatchChan ("thekey" ))
385+ cli .EXPECT ().TimeToLive (gomock .Any (), id ).Return (& clientv3.LeaseTimeToLiveResponse {
386+ TTL : 1 ,
387+ }, nil )
388+ cli .EXPECT ().Put (gomock .Any (), "thekey" , "thevalue" , gomock .Any ()).Return (nil , nil )
389+ cli .EXPECT ().Get (gomock .Any (), "thekey" ).Return (& clientv3.GetResponse {
390+ Kvs : []* mvccpb.KeyValue {{
391+ Key : []byte ("thekey" ),
392+ Value : []byte ("thevalue" ),
393+ Lease : 0 ,
394+ }},
395+ }, nil )
343396
344- // Expect revoke when Stop is called
397+ restarted := make ( chan struct {})
345398 cli .EXPECT ().Revoke (gomock .Any (), id ).Do (func (_ , _ any ) {
346- wg . Done ( )
399+ close ( restarted )
347400 })
348401
349402 pub := NewPublisher (nil , "thekey" , "thevalue" )
350403 pub .lease = id
351404 pub .fullKey = "thekey"
352405
353406 assert .Nil (t , pub .keepAliveAsync (cli ))
354-
355- // Wait for Put to be called, then stop
356- <- putCalled
407+ waitForSignal (t , restarted )
357408 pub .Stop ()
358- wg .Wait ()
359409}
360410
361411func TestPublisher_Resume (t * testing.T ) {
@@ -462,3 +512,27 @@ func createTempFile(t *testing.T, body []byte) string {
462512
463513 return tmpFile .Name ()
464514}
515+
516+ func newDeleteWatchChan (key string ) <- chan clientv3.WatchResponse {
517+ watchChan := make (chan clientv3.WatchResponse , 1 )
518+ watchChan <- clientv3.WatchResponse {
519+ Events : []* clientv3.Event {{
520+ Type : clientv3 .EventTypeDelete ,
521+ Kv : & mvccpb.KeyValue {
522+ Key : []byte (key ),
523+ },
524+ }},
525+ }
526+
527+ return watchChan
528+ }
529+
530+ func waitForSignal (t * testing.T , ch <- chan struct {}) {
531+ t .Helper ()
532+
533+ select {
534+ case <- ch :
535+ case <- time .After (time .Second ):
536+ t .Fatal ("timed out waiting for signal" )
537+ }
538+ }
0 commit comments