@@ -19,9 +19,16 @@ import {describe, it, before, beforeEach, afterEach} from 'mocha';
19
19
import { EventEmitter } from 'events' ;
20
20
import * as proxyquire from 'proxyquire' ;
21
21
import * as sinon from 'sinon' ;
22
+ import * as defer from 'p-defer' ;
22
23
23
24
import * as leaseTypes from '../src/lease-manager' ;
24
- import { Message , Subscriber } from '../src/subscriber' ;
25
+ import {
26
+ AckError ,
27
+ AckResponse ,
28
+ AckResponses ,
29
+ Message ,
30
+ Subscriber ,
31
+ } from '../src/subscriber' ;
25
32
import { defaultOptions } from '../src/default-options' ;
26
33
27
34
const FREE_MEM = 9376387072 ;
@@ -34,6 +41,10 @@ class FakeSubscriber extends EventEmitter {
34
41
isOpen = true ;
35
42
modAckLatency = 2000 ;
36
43
async modAck ( ) : Promise < void > { }
44
+ async modAckWithResponse ( ) : Promise < AckResponse > {
45
+ return AckResponses . Success ;
46
+ }
47
+ isExactlyOnceDelivery = false ;
37
48
}
38
49
39
50
class FakeMessage {
@@ -43,6 +54,21 @@ class FakeMessage {
43
54
this . received = Date . now ( ) ;
44
55
}
45
56
modAck ( ) : void { }
57
+ async modAckWithResponse ( ) : Promise < AckResponse > {
58
+ return AckResponses . Success ;
59
+ }
60
+ ackFailed ( ) { }
61
+ }
62
+
63
+ interface LeaseManagerInternals {
64
+ _extendDeadlines ( ) : void ;
65
+ _messages : Set < Message > ;
66
+ _isLeasing : boolean ;
67
+ _scheduleExtension ( ) : void ;
68
+ }
69
+
70
+ function getLMInternals ( mgr : leaseTypes . LeaseManager ) : LeaseManagerInternals {
71
+ return mgr as unknown as LeaseManagerInternals ;
46
72
}
47
73
48
74
describe ( 'LeaseManager' , ( ) => {
@@ -207,6 +233,18 @@ describe('LeaseManager', () => {
207
233
assert . strictEqual ( stub . callCount , 1 ) ;
208
234
} ) ;
209
235
236
+ it ( 'should schedule a lease extension for exactly-once delivery' , ( ) => {
237
+ const message = new FakeMessage ( ) as { } as Message ;
238
+ const stub = sandbox
239
+ . stub ( message , 'modAck' )
240
+ . withArgs ( subscriber . ackDeadline ) ;
241
+
242
+ leaseManager . add ( message ) ;
243
+ clock . tick ( expectedTimeout ) ;
244
+
245
+ assert . strictEqual ( stub . callCount , 1 ) ;
246
+ } ) ;
247
+
210
248
it ( 'should not schedule a lease extension if already in progress' , ( ) => {
211
249
const messages = [ new FakeMessage ( ) , new FakeMessage ( ) ] ;
212
250
const stubs = messages . map ( message => sandbox . stub ( message , 'modAck' ) ) ;
@@ -274,6 +312,32 @@ describe('LeaseManager', () => {
274
312
assert . strictEqual ( deadline , subscriber . ackDeadline ) ;
275
313
} ) ;
276
314
315
+ it ( 'should remove and ackFailed any messages that fail to ack' , done => {
316
+ ( subscriber as unknown as FakeSubscriber ) . isExactlyOnceDelivery = true ;
317
+
318
+ leaseManager . setOptions ( {
319
+ maxExtensionMinutes : 600 ,
320
+ } ) ;
321
+
322
+ const goodMessage = new FakeMessage ( ) ;
323
+
324
+ const removeStub = sandbox . stub ( leaseManager , 'remove' ) ;
325
+ const mawrStub = sandbox
326
+ . stub ( goodMessage , 'modAckWithResponse' )
327
+ . rejects ( new AckError ( AckResponses . Invalid ) ) ;
328
+ const failed = sandbox . stub ( goodMessage , 'ackFailed' ) ;
329
+
330
+ removeStub . callsFake ( ( ) => {
331
+ assert . strictEqual ( mawrStub . callCount , 1 ) ;
332
+ assert . strictEqual ( removeStub . callCount , 1 ) ;
333
+ assert . strictEqual ( failed . callCount , 1 ) ;
334
+ done ( ) ;
335
+ } ) ;
336
+
337
+ leaseManager . add ( goodMessage as { } as Message ) ;
338
+ clock . tick ( halfway * 2 + 1 ) ;
339
+ } ) ;
340
+
277
341
it ( 'should continuously extend the deadlines' , ( ) => {
278
342
const message = new FakeMessage ( ) ;
279
343
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -473,4 +537,86 @@ describe('LeaseManager', () => {
473
537
assert . strictEqual ( leaseManager . isFull ( ) , true ) ;
474
538
} ) ;
475
539
} ) ;
540
+
541
+ describe ( 'deadline extension' , ( ) => {
542
+ beforeEach ( ( ) => {
543
+ sandbox . useFakeTimers ( ) ;
544
+ } ) ;
545
+ afterEach ( ( ) => {
546
+ sandbox . clock . restore ( ) ;
547
+ } ) ;
548
+
549
+ it ( 'calls regular modAck periodically w/o exactly-once' , ( ) => {
550
+ const lmi = getLMInternals ( leaseManager ) ;
551
+ const msg = new Message ( subscriber , {
552
+ ackId : 'ackack' ,
553
+ message : { data : '' } ,
554
+ deliveryAttempt : 0 ,
555
+ } ) ;
556
+ sandbox . clock . tick ( 1 ) ;
557
+
558
+ const maStub = sandbox . stub ( msg , 'modAck' ) ;
559
+
560
+ lmi . _messages . add ( msg ) ;
561
+ lmi . _extendDeadlines ( ) ;
562
+
563
+ assert . ok ( maStub . calledOnce ) ;
564
+ } ) ;
565
+
566
+ it ( 'calls modAckWithResponse periodically w/exactly-once, successful' , async ( ) => {
567
+ const lmi = getLMInternals ( leaseManager ) ;
568
+ const msg = new Message ( subscriber , {
569
+ ackId : 'ackack' ,
570
+ message : { data : '' } ,
571
+ deliveryAttempt : 0 ,
572
+ } ) ;
573
+ sandbox . clock . tick ( 1 ) ;
574
+ ( subscriber as unknown as FakeSubscriber ) . isExactlyOnceDelivery = true ;
575
+
576
+ const done = defer ( ) ;
577
+ sandbox . stub ( msg , 'modAck' ) . callsFake ( ( ) => {
578
+ console . error ( 'oops we did it wrong' ) ;
579
+ } ) ;
580
+
581
+ const maStub = sandbox . stub ( msg , 'modAckWithResponse' ) ;
582
+ maStub . callsFake ( async ( ) => {
583
+ done . resolve ( ) ;
584
+ return AckResponses . Success ;
585
+ } ) ;
586
+
587
+ lmi . _messages . add ( msg ) ;
588
+ lmi . _extendDeadlines ( ) ;
589
+
590
+ await done . promise ;
591
+ assert . ok ( maStub . calledOnce ) ;
592
+ } ) ;
593
+
594
+ it ( 'calls modAckWithResponse periodically w/exactly-once, failure' , async ( ) => {
595
+ const lmi = getLMInternals ( leaseManager ) ;
596
+ const msg = new Message ( subscriber , {
597
+ ackId : 'ackack' ,
598
+ message : { data : '' } ,
599
+ deliveryAttempt : 0 ,
600
+ } ) ;
601
+ sandbox . clock . tick ( 1 ) ;
602
+ ( subscriber as unknown as FakeSubscriber ) . isExactlyOnceDelivery = true ;
603
+
604
+ const done = defer ( ) ;
605
+
606
+ const maStub = sandbox . stub ( msg , 'modAckWithResponse' ) ;
607
+ maStub . callsFake ( async ( ) => {
608
+ done . resolve ( ) ;
609
+ throw new AckError ( AckResponses . Invalid ) ;
610
+ } ) ;
611
+ const rmStub = sandbox . stub ( leaseManager , 'remove' ) ;
612
+
613
+ lmi . _messages . add ( msg ) ;
614
+ lmi . _extendDeadlines ( ) ;
615
+
616
+ await done . promise ;
617
+
618
+ assert . ok ( maStub . calledOnce ) ;
619
+ assert . ok ( rmStub . calledOnce ) ;
620
+ } ) ;
621
+ } ) ;
476
622
} ) ;
0 commit comments