amqplib 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- const { describe, it } = require('node:test');
1
+ const { describe, it, beforeEach, afterEach } = require('node:test');
2
2
  const assert = require('node:assert');
3
3
  const promisify = require('node:util').promisify;
4
4
  const Channel = require('../lib/channel').Channel;
@@ -609,4 +609,310 @@ describe('Channel', () => {
609
609
  cb();
610
610
  }));
611
611
  });
612
+
613
+ describe('Event handler errors - without handler-error listener', () => {
614
+ let prevUncaughtExceptionListeners;
615
+
616
+ beforeEach(() => {
617
+ prevUncaughtExceptionListeners = process.rawListeners('uncaughtException').slice();
618
+ process.removeAllListeners('uncaughtException');
619
+ });
620
+
621
+ afterEach(() => {
622
+ prevUncaughtExceptionListeners.forEach((h) => process.on('uncaughtException', h));
623
+ });
624
+
625
+ it('throw in close handler kills the connection without handler-error listener', channelTest((ch, cb, conn) => {
626
+ const expectedErr = new Error('user close handler explodes');
627
+ let connErrorSeen = false;
628
+ // The throw propagates to acceptLoop → frameError → onSocketError → connection.emit('error')
629
+ conn.on('error', (err) => {
630
+ assert.strictEqual(err, expectedErr);
631
+ connErrorSeen = true;
632
+ });
633
+ // The ChannelCloseOk buffered before the kill is flushed by the mux after the
634
+ // stream is ended, producing ERR_STREAM_PUSH_AFTER_EOF. Absorb it and use it
635
+ // as the signal to end the test, since it fires after the conn error.
636
+ process.once('uncaughtException', (err) => {
637
+ assert.strictEqual(err.code, 'ERR_STREAM_PUSH_AFTER_EOF');
638
+ assert.ok(connErrorSeen);
639
+ cb();
640
+ });
641
+ ch.once('error', (err) => { assert.match(err.message, /Channel closed by server/); });
642
+ ch.on('close', () => { throw expectedErr; });
643
+ open(ch);
644
+ }, (send, _wait, cb, ch) => {
645
+ completes(() => {
646
+ send(defs.ChannelClose, {
647
+ replyText: 'Forced close',
648
+ replyCode: defs.constants.CHANNEL_ERROR,
649
+ classId: 0,
650
+ methodId: 0,
651
+ }, ch);
652
+ }, cb);
653
+ }));
654
+
655
+ it('throw in error handler becomes uncaught exception', channelTest((ch, cb) => {
656
+ const expectedErr = new Error('user error handler explodes');
657
+ process.once('uncaughtException', (err) => {
658
+ assert.strictEqual(err, expectedErr);
659
+ cb();
660
+ });
661
+ ch.on('error', () => { throw expectedErr; });
662
+ open(ch);
663
+ }, (send, wait, cb, ch) => {
664
+ send(defs.ChannelClose, {
665
+ replyText: 'Forced close',
666
+ replyCode: defs.constants.CHANNEL_ERROR,
667
+ classId: 0,
668
+ methodId: 0,
669
+ }, ch);
670
+ wait(defs.ChannelCloseOk)().then(cb, cb);
671
+ }));
672
+
673
+ it('throw in drain handler becomes uncaught exception', channelTest((ch, cb) => {
674
+ const expectedErr = new Error('user drain handler explodes');
675
+ process.once('uncaughtException', (err) => {
676
+ assert.strictEqual(err, expectedErr);
677
+ cb();
678
+ });
679
+ ch.on('drain', () => { throw expectedErr; });
680
+ // Call outside a Promise chain so the throw becomes an uncaughtException
681
+ open(ch).then(() => setImmediate(() => ch.onBufferDrain()));
682
+ }, (_send, _wait, cb) => { cb(); }));
683
+
684
+ it('throw in ack handler becomes uncaught exception', channelTest((ch, cb) => {
685
+ const expectedErr = new Error('user ack handler explodes');
686
+ process.once('uncaughtException', (err) => {
687
+ assert.strictEqual(err, expectedErr);
688
+ cb();
689
+ });
690
+ ch.on('ack', () => { throw expectedErr; });
691
+ open(ch);
692
+ }, (send, _wait, cb, ch) => {
693
+ completes(() => {
694
+ send(defs.BasicAck, { deliveryTag: 1, multiple: false }, ch);
695
+ }, cb);
696
+ }));
697
+
698
+ it('throw in nack handler becomes uncaught exception', channelTest((ch, cb) => {
699
+ const expectedErr = new Error('user nack handler explodes');
700
+ process.once('uncaughtException', (err) => {
701
+ assert.strictEqual(err, expectedErr);
702
+ cb();
703
+ });
704
+ ch.on('nack', () => { throw expectedErr; });
705
+ open(ch);
706
+ }, (send, _wait, cb, ch) => {
707
+ completes(() => {
708
+ send(defs.BasicNack, { deliveryTag: 1, multiple: false, requeue: false }, ch);
709
+ }, cb);
710
+ }));
711
+
712
+ it('throw in cancel handler becomes uncaught exception', channelTest((ch, cb) => {
713
+ const expectedErr = new Error('user cancel handler explodes');
714
+ process.once('uncaughtException', (err) => {
715
+ assert.strictEqual(err, expectedErr);
716
+ cb();
717
+ });
718
+ ch.on('cancel', () => { throw expectedErr; });
719
+ open(ch);
720
+ }, (send, _wait, cb, ch) => {
721
+ completes(() => {
722
+ send(defs.BasicCancel, { consumerTag: 'tag', nowait: false }, ch);
723
+ }, cb);
724
+ }));
725
+
726
+ it('throw in delivery handler closes channel with error', channelTest((ch, cb) => {
727
+ // acceptMessageFrame catches the throw and routes it to closeWithError,
728
+ // which fires channel 'error' then 'close' — it does NOT become an uncaughtException.
729
+ const expectedErr = new Error('user delivery handler explodes');
730
+ const decrementLatch = latch(2, cb);
731
+ ch.on('error', (err) => {
732
+ assert.strictEqual(err, expectedErr);
733
+ decrementLatch();
734
+ });
735
+ ch.on('close', decrementLatch);
736
+ ch.on('delivery', () => { throw expectedErr; });
737
+ open(ch);
738
+ }, (send, wait, cb, ch) => {
739
+ send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from('hello'));
740
+ wait(defs.ChannelClose)()
741
+ .then(() => send(defs.ChannelCloseOk, {}, ch))
742
+ .then(cb, cb);
743
+ }));
744
+
745
+ it('throw in return handler closes channel with error', channelTest((ch, cb) => {
746
+ // acceptMessageFrame catches the throw and routes it to closeWithError.
747
+ const expectedErr = new Error('user return handler explodes');
748
+ const decrementLatch = latch(2, cb);
749
+ ch.on('error', (err) => {
750
+ assert.strictEqual(err, expectedErr);
751
+ decrementLatch();
752
+ });
753
+ ch.on('close', decrementLatch);
754
+ ch.on('return', () => { throw expectedErr; });
755
+ open(ch);
756
+ }, (send, wait, cb, ch) => {
757
+ send(defs.BasicReturn, DELIVER_FIELDS, ch, Buffer.from('hello'));
758
+ wait(defs.ChannelClose)()
759
+ .then(() => send(defs.ChannelCloseOk, {}, ch))
760
+ .then(cb, cb);
761
+ }));
762
+ });
763
+
764
+ describe('Event handler errors - with handler-error listener', () => {
765
+ let prevUncaughtExceptionListeners;
766
+
767
+ beforeEach(() => {
768
+ prevUncaughtExceptionListeners = process.rawListeners('uncaughtException').slice();
769
+ process.removeAllListeners('uncaughtException');
770
+ });
771
+
772
+ afterEach(() => {
773
+ prevUncaughtExceptionListeners.forEach((h) => process.on('uncaughtException', h));
774
+ });
775
+
776
+ it('throw in close handler is delivered via handler-error event', channelTest((ch, cb) => {
777
+ const expectedErr = new Error('user close handler explodes');
778
+ ch.on('handler-error', (err, event) => {
779
+ assert.strictEqual(err, expectedErr);
780
+ assert.strictEqual(event, 'close');
781
+ cb();
782
+ });
783
+ // A server-initiated ChannelClose always fires 'error' before 'close' (line 291 in channel.js).
784
+ // Handle it so only the throw from the close handler reaches handler-error.
785
+ ch.once('error', (err) => { assert.match(err.message, /Channel closed by server/); });
786
+ ch.on('close', () => { throw expectedErr; });
787
+ open(ch);
788
+ }, (send, wait, cb, ch) => {
789
+ send(defs.ChannelClose, {
790
+ replyText: 'Forced close',
791
+ replyCode: defs.constants.CHANNEL_ERROR,
792
+ classId: 0,
793
+ methodId: 0,
794
+ }, ch);
795
+ wait(defs.ChannelCloseOk)().then(cb, cb);
796
+ }));
797
+
798
+ it('throw in error handler is delivered via handler-error event', channelTest((ch, cb) => {
799
+ const expectedErr = new Error('user error handler explodes');
800
+ ch.on('handler-error', (err, event) => {
801
+ assert.strictEqual(err, expectedErr);
802
+ assert.strictEqual(event, 'error');
803
+ cb();
804
+ });
805
+ ch.on('error', () => { throw expectedErr; });
806
+ open(ch);
807
+ }, (send, wait, cb, ch) => {
808
+ send(defs.ChannelClose, {
809
+ replyText: 'Forced close',
810
+ replyCode: defs.constants.CHANNEL_ERROR,
811
+ classId: 0,
812
+ methodId: 0,
813
+ }, ch);
814
+ wait(defs.ChannelCloseOk)().then(cb, cb);
815
+ }));
816
+
817
+ it('throw in drain handler is delivered via handler-error event', channelTest((ch, cb) => {
818
+ const expectedErr = new Error('user drain handler explodes');
819
+ ch.on('handler-error', (err, event) => {
820
+ assert.strictEqual(err, expectedErr);
821
+ assert.strictEqual(event, 'drain');
822
+ cb();
823
+ });
824
+ ch.on('drain', () => { throw expectedErr; });
825
+ open(ch).then(() => setImmediate(() => ch.onBufferDrain()));
826
+ }, (_send, _wait, cb) => { cb(); }));
827
+
828
+ it('throw in ack handler is delivered via handler-error event', channelTest((ch, cb) => {
829
+ const expectedErr = new Error('user ack handler explodes');
830
+ ch.on('handler-error', (err, event) => {
831
+ assert.strictEqual(err, expectedErr);
832
+ assert.strictEqual(event, 'ack');
833
+ cb();
834
+ });
835
+ ch.on('ack', () => { throw expectedErr; });
836
+ open(ch);
837
+ }, (send, _wait, cb, ch) => {
838
+ completes(() => {
839
+ send(defs.BasicAck, { deliveryTag: 1, multiple: false }, ch);
840
+ }, cb);
841
+ }));
842
+
843
+ it('throw in nack handler is delivered via handler-error event', channelTest((ch, cb) => {
844
+ const expectedErr = new Error('user nack handler explodes');
845
+ ch.on('handler-error', (err, event) => {
846
+ assert.strictEqual(err, expectedErr);
847
+ assert.strictEqual(event, 'nack');
848
+ cb();
849
+ });
850
+ ch.on('nack', () => { throw expectedErr; });
851
+ open(ch);
852
+ }, (send, _wait, cb, ch) => {
853
+ completes(() => {
854
+ send(defs.BasicNack, { deliveryTag: 1, multiple: false, requeue: false }, ch);
855
+ }, cb);
856
+ }));
857
+
858
+ it('throw in cancel handler is delivered via handler-error event', channelTest((ch, cb) => {
859
+ const expectedErr = new Error('user cancel handler explodes');
860
+ ch.on('handler-error', (err, event) => {
861
+ assert.strictEqual(err, expectedErr);
862
+ assert.strictEqual(event, 'cancel');
863
+ cb();
864
+ });
865
+ ch.on('cancel', () => { throw expectedErr; });
866
+ open(ch);
867
+ }, (send, _wait, cb, ch) => {
868
+ completes(() => {
869
+ send(defs.BasicCancel, { consumerTag: 'tag', nowait: false }, ch);
870
+ }, cb);
871
+ }));
872
+
873
+ it('throw in delivery handler is delivered via handler-error event', channelTest((ch, cb) => {
874
+ const expectedErr = new Error('user delivery handler explodes');
875
+ ch.on('handler-error', (err, event) => {
876
+ assert.strictEqual(err, expectedErr);
877
+ assert.strictEqual(event, 'delivery');
878
+ cb();
879
+ });
880
+ ch.on('delivery', () => { throw expectedErr; });
881
+ open(ch);
882
+ }, (send, _wait, cb, ch) => {
883
+ completes(() => {
884
+ send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from('hello'));
885
+ }, cb);
886
+ }));
887
+
888
+ it('throw in return handler is delivered via handler-error event', channelTest((ch, cb) => {
889
+ const expectedErr = new Error('user return handler explodes');
890
+ ch.on('handler-error', (err, event) => {
891
+ assert.strictEqual(err, expectedErr);
892
+ assert.strictEqual(event, 'return');
893
+ cb();
894
+ });
895
+ ch.on('return', () => { throw expectedErr; });
896
+ open(ch);
897
+ }, (send, _wait, cb, ch) => {
898
+ completes(() => {
899
+ send(defs.BasicReturn, DELIVER_FIELDS, ch, Buffer.from('hello'));
900
+ }, cb);
901
+ }));
902
+
903
+ it('throw in handler-error handler becomes uncaught exception', channelTest((ch, cb) => {
904
+ const expectedErr = new Error('handler-error handler explodes');
905
+ process.once('uncaughtException', (err) => {
906
+ assert.strictEqual(err, expectedErr);
907
+ cb();
908
+ });
909
+ ch.on('handler-error', () => { throw expectedErr; });
910
+ ch.on('ack', () => { throw new Error('user ack handler explodes'); });
911
+ open(ch);
912
+ }, (send, _wait, cb, ch) => {
913
+ completes(() => {
914
+ send(defs.BasicAck, { deliveryTag: 1, multiple: false }, ch);
915
+ }, cb);
916
+ }));
917
+ });
612
918
  });
@@ -25,6 +25,15 @@ describe('updateSecret', () => {
25
25
  .finally(() => c.close())
26
26
  );
27
27
  });
28
+
29
+ it('emits update-secret-ok event', () => {
30
+ return connect().then((c) =>
31
+ new Promise((resolve, reject) => {
32
+ c.on('update-secret-ok', resolve);
33
+ c.updateSecret(Buffer.from('new secret'), 'no reason').catch(reject);
34
+ }).finally(() => c.close())
35
+ );
36
+ });
28
37
  });
29
38
 
30
39
  describe('assert, check, delete', () => {
@@ -300,6 +300,357 @@ describe('Connection', () => {
300
300
  }));
301
301
  });
302
302
 
303
+ describe('Event handler errors - without handler-event listener', () => {
304
+ let prevUncaughtExceptionListeners;
305
+
306
+ beforeEach(() => {
307
+ prevUncaughtExceptionListeners = process.rawListeners('uncaughtException').slice();
308
+ process.removeAllListeners('uncaughtException');
309
+ });
310
+
311
+ afterEach(() => {
312
+ prevUncaughtExceptionListeners.forEach((h) => process.on('uncaughtException', h));
313
+ heartbeat.UNITS_TO_MS = 1000;
314
+ });
315
+
316
+ it('throw in close handler from server-initiated close is swallowed without handler-error listener', connectionTest((c, cb) => {
317
+ c.on('close', () => { throw new Error('user handler explodes'); });
318
+ c.open(OPEN_OPTS);
319
+ cb();
320
+ }, (send, wait, cb) => {
321
+ handshake(send, wait)
322
+ .then(() => send(defs.ConnectionClose, {
323
+ replyText: 'Begone',
324
+ replyCode: defs.constants.CONNECTION_FORCED,
325
+ methodId: 0,
326
+ classId: 0,
327
+ }))
328
+ .then(wait(defs.ConnectionCloseOk))
329
+ .then(cb, cb);
330
+ }));
331
+
332
+ it('throw in close handler from client-initiated close is swallowed without handler-error listener', connectionTest((c, cb) => {
333
+ c.open(OPEN_OPTS, (err) => {
334
+ assert.ifError(err);
335
+ c.on('close', () => { throw new Error('user handler explodes on client close'); });
336
+ c.close();
337
+ cb();
338
+ });
339
+ }, (send, wait, cb) => {
340
+ handshake(send, wait)
341
+ .then(wait(defs.ConnectionClose))
342
+ .then(() => send(defs.ConnectionCloseOk, {}))
343
+ .then(cb, cb);
344
+ }));
345
+
346
+ it('throw in error handler becomes uncaught exception', connectionTest((c, cb) => {
347
+ const expectedErr = new Error('user error handler explodes');
348
+ process.once('uncaughtException', (err) => {
349
+ assert.strictEqual(err, expectedErr);
350
+ cb();
351
+ });
352
+ c.on('error', () => { throw expectedErr; });
353
+ c.open(OPEN_OPTS);
354
+ }, (send, wait, cb) => {
355
+ handshake(send, wait)
356
+ .then(() => send(defs.ConnectionClose, {
357
+ replyText: 'Begone',
358
+ replyCode: defs.constants.INTERNAL_ERROR,
359
+ methodId: 0,
360
+ classId: 0,
361
+ }))
362
+ .then(wait(defs.ConnectionCloseOk))
363
+ .then(cb, cb);
364
+ }));
365
+
366
+ it('throw in blocked handler becomes uncaught exception', connectionTest((c, cb) => {
367
+ const expectedErr = new Error('user blocked handler explodes');
368
+ process.once('uncaughtException', (err) => {
369
+ assert.strictEqual(err, expectedErr);
370
+ cb();
371
+ });
372
+ c.on('blocked', () => { throw expectedErr; });
373
+ c.open(OPEN_OPTS);
374
+ }, (send, wait, cb) => {
375
+ handshake(send, wait)
376
+ .then(() => send(defs.ConnectionBlocked, { reason: 'memory' }, 0))
377
+ .then(cb, cb);
378
+ }));
379
+
380
+ it('throw in unblocked handler becomes uncaught exception', connectionTest((c, cb) => {
381
+ const expectedErr = new Error('user unblocked handler explodes');
382
+ process.once('uncaughtException', (err) => {
383
+ assert.strictEqual(err, expectedErr);
384
+ cb();
385
+ });
386
+ c.on('unblocked', () => { throw expectedErr; });
387
+ c.open(OPEN_OPTS);
388
+ }, (send, wait, cb) => {
389
+ handshake(send, wait)
390
+ .then(() => send(defs.ConnectionUnblocked, {}, 0))
391
+ .then(cb, cb);
392
+ }));
393
+
394
+ it('throw in update-secret-ok handler becomes uncaught exception', connectionTest((c, cb) => {
395
+ const expectedErr = new Error('user update-secret-ok handler explodes');
396
+ process.once('uncaughtException', (err) => {
397
+ assert.strictEqual(err, expectedErr);
398
+ cb();
399
+ });
400
+ c.open(OPEN_OPTS, (err) => {
401
+ assert.ifError(err);
402
+ c.on('update-secret-ok', () => { throw expectedErr; });
403
+ c._updateSecret(Buffer.from('new secret'), 'reason', () => {});
404
+ });
405
+ }, (send, wait, cb) => {
406
+ handshake(send, wait)
407
+ .then(wait(defs.ConnectionUpdateSecret))
408
+ .then(() => send(defs.ConnectionUpdateSecretOk, {}, 0))
409
+ .then(cb, cb);
410
+ }));
411
+
412
+ it('throw in error handler from closeWithError becomes uncaught exception', connectionTest((c, cb) => {
413
+ const expectedErr = new Error('user error handler explodes on closeWithError');
414
+ process.once('uncaughtException', (err) => {
415
+ assert.strictEqual(err, expectedErr);
416
+ cb();
417
+ });
418
+ c.once('error', (err) => {
419
+ assert.match(err.message, /Unexpected frame on channel 0/);
420
+ throw expectedErr;
421
+ });
422
+ c.open(OPEN_OPTS);
423
+ }, (send, wait, cb) => {
424
+ handshake(send, wait)
425
+ .then(() => send(defs.ChannelOpenOk, { channelId: Buffer.from('') }, 0))
426
+ .then(cb, cb);
427
+ }));
428
+
429
+ it('throw in error handler from onSocketError', connectionTest((c, cb) => {
430
+ const expectedErr = new Error('user error handler explodes on socket error');
431
+ process.once('uncaughtException', (err) => {
432
+ assert.strictEqual(err, expectedErr);
433
+ cb();
434
+ });
435
+ c.once('error', (err) => {
436
+ assert.match(err.message, /Unexpected close/);
437
+ throw expectedErr;
438
+ });
439
+ c.open(OPEN_OPTS, (err) => {
440
+ assert.ifError(err);
441
+ c.sendHeartbeat();
442
+ });
443
+ }, (send, wait, cb, socket) => {
444
+ handshake(send, wait)
445
+ .then(wait())
446
+ .then(() => socket.end())
447
+ .then(cb, cb);
448
+ }));
449
+
450
+ it('throw in error handler from heartbeat timeout', connectionTest((c, cb) => {
451
+ heartbeat.UNITS_TO_MS = 20;
452
+ const expectedErr = new Error('user error handler explodes on heartbeat timeout');
453
+ const opts = Object.create(OPEN_OPTS);
454
+ opts.heartbeat = 1;
455
+ process.once('uncaughtException', (err) => {
456
+ assert.strictEqual(err, expectedErr);
457
+ c.heartbeater.clear();
458
+ cb();
459
+ });
460
+ c.on('error', (err) => {
461
+ assert.match(err.message, /Heartbeat timeout/);
462
+ throw expectedErr;
463
+ });
464
+ c.open(opts);
465
+ }, (send, wait, cb) => {
466
+ handshake(send, wait)
467
+ .then(cb, cb);
468
+ // conspicuously not sending anything ...
469
+ }));
470
+ });
471
+
472
+ describe('Event handler errors - with handler-error listener', () => {
473
+ let prevUncaughtExceptionListeners;
474
+
475
+ beforeEach(() => {
476
+ prevUncaughtExceptionListeners = process.rawListeners('uncaughtException').slice();
477
+ process.removeAllListeners('uncaughtException');
478
+ });
479
+
480
+ afterEach(() => {
481
+ prevUncaughtExceptionListeners.forEach((h) => process.on('uncaughtException', h));
482
+ heartbeat.UNITS_TO_MS = 1000;
483
+ });
484
+
485
+ it('throw in close handler is delivered via handler-error event', connectionTest((c, cb) => {
486
+ const expectedErr = new Error('user close handler explodes');
487
+ c.on('handler-error', (err, event) => {
488
+ assert.strictEqual(err, expectedErr);
489
+ assert.strictEqual(event, 'close');
490
+ cb();
491
+ });
492
+ c.on('close', () => { throw expectedErr; });
493
+ c.open(OPEN_OPTS);
494
+ }, (send, wait, cb) => {
495
+ handshake(send, wait)
496
+ .then(() => send(defs.ConnectionClose, {
497
+ replyText: 'Begone',
498
+ replyCode: defs.constants.CONNECTION_FORCED,
499
+ methodId: 0,
500
+ classId: 0,
501
+ }))
502
+ .then(wait(defs.ConnectionCloseOk))
503
+ .then(cb, cb);
504
+ }));
505
+
506
+ it('throw in error handler is delivered via handler-error event', connectionTest((c, cb) => {
507
+ const expectedErr = new Error('user error handler explodes');
508
+ c.on('handler-error', (err, event) => {
509
+ assert.strictEqual(err, expectedErr);
510
+ assert.strictEqual(event, 'error');
511
+ cb();
512
+ });
513
+ c.on('error', () => { throw expectedErr; });
514
+ c.open(OPEN_OPTS);
515
+ }, (send, wait, cb) => {
516
+ handshake(send, wait)
517
+ .then(() => send(defs.ConnectionClose, {
518
+ replyText: 'Begone',
519
+ replyCode: defs.constants.INTERNAL_ERROR,
520
+ methodId: 0,
521
+ classId: 0,
522
+ }))
523
+ .then(wait(defs.ConnectionCloseOk))
524
+ .then(cb, cb);
525
+ }));
526
+
527
+ it('throw in blocked handler is delivered via handler-error event', connectionTest((c, cb) => {
528
+ const expectedErr = new Error('user blocked handler explodes');
529
+ c.on('handler-error', (err, event) => {
530
+ assert.strictEqual(err, expectedErr);
531
+ assert.strictEqual(event, 'blocked');
532
+ cb();
533
+ });
534
+ c.on('blocked', () => { throw expectedErr; });
535
+ c.open(OPEN_OPTS);
536
+ }, (send, wait, cb) => {
537
+ handshake(send, wait)
538
+ .then(() => send(defs.ConnectionBlocked, { reason: 'memory' }, 0))
539
+ .then(cb, cb);
540
+ }));
541
+
542
+ it('throw in unblocked handler is delivered via handler-error event', connectionTest((c, cb) => {
543
+ const expectedErr = new Error('user unblocked handler explodes');
544
+ c.on('handler-error', (err, event) => {
545
+ assert.strictEqual(err, expectedErr);
546
+ assert.strictEqual(event, 'unblocked');
547
+ cb();
548
+ });
549
+ c.on('unblocked', () => { throw expectedErr; });
550
+ c.open(OPEN_OPTS);
551
+ }, (send, wait, cb) => {
552
+ handshake(send, wait)
553
+ .then(() => send(defs.ConnectionUnblocked, {}, 0))
554
+ .then(cb, cb);
555
+ }));
556
+
557
+ it('throw in update-secret-ok handler is delivered via handler-error event', connectionTest((c, cb) => {
558
+ const expectedErr = new Error('user update-secret-ok handler explodes');
559
+ c.on('handler-error', (err, event) => {
560
+ assert.strictEqual(err, expectedErr);
561
+ assert.strictEqual(event, 'update-secret-ok');
562
+ cb();
563
+ });
564
+ c.open(OPEN_OPTS, (err) => {
565
+ assert.ifError(err);
566
+ c.on('update-secret-ok', () => { throw expectedErr; });
567
+ c._updateSecret(Buffer.from('new secret'), 'reason', () => {});
568
+ });
569
+ }, (send, wait, cb) => {
570
+ handshake(send, wait)
571
+ .then(wait(defs.ConnectionUpdateSecret))
572
+ .then(() => send(defs.ConnectionUpdateSecretOk, {}, 0))
573
+ .then(cb, cb);
574
+ }));
575
+
576
+ it('throw in error handler from heartbeat timeout is delivered via handler-error event', connectionTest((c, cb) => {
577
+ heartbeat.UNITS_TO_MS = 20;
578
+ const expectedErr = new Error('user error handler explodes on heartbeat timeout');
579
+ const opts = Object.create(OPEN_OPTS);
580
+ opts.heartbeat = 1;
581
+ c.on('handler-error', (err, event) => {
582
+ assert.strictEqual(err, expectedErr);
583
+ assert.strictEqual(event, 'error');
584
+ c.heartbeater.clear();
585
+ cb();
586
+ });
587
+ c.on('error', (err) => {
588
+ assert.match(err.message, /Heartbeat timeout/);
589
+ throw expectedErr;
590
+ });
591
+ c.open(opts);
592
+ }, (send, wait, cb) => {
593
+ handshake(send, wait)
594
+ .then(cb, cb);
595
+ // conspicuously not sending anything ...
596
+ }));
597
+
598
+ it('throw in error handler from closeWithError is delivered via handler-error event', connectionTest((c, cb) => {
599
+ const expectedErr = new Error('user error handler explodes on closeWithError');
600
+ c.on('handler-error', (err, event) => {
601
+ assert.strictEqual(err, expectedErr);
602
+ assert.strictEqual(event, 'error');
603
+ cb();
604
+ });
605
+ c.once('error', (err) => {
606
+ assert.match(err.message, /Unexpected frame on channel 0/);
607
+ throw expectedErr;
608
+ });
609
+ c.open(OPEN_OPTS);
610
+ }, (send, wait, cb) => {
611
+ handshake(send, wait)
612
+ .then(() => send(defs.ChannelOpenOk, { channelId: Buffer.from('') }, 0))
613
+ .then(cb, cb);
614
+ }));
615
+
616
+ it('throw in error handler from onSocketError is delivered via handler-error event', connectionTest((c, cb) => {
617
+ const expectedErr = new Error('user error handler explodes on socket error');
618
+ c.on('handler-error', (err, event) => {
619
+ assert.strictEqual(err, expectedErr);
620
+ assert.strictEqual(event, 'error');
621
+ cb();
622
+ });
623
+ c.once('error', (err) => {
624
+ assert.match(err.message, /Unexpected close/);
625
+ throw expectedErr;
626
+ });
627
+ c.open(OPEN_OPTS, (err) => {
628
+ assert.ifError(err);
629
+ c.sendHeartbeat();
630
+ });
631
+ }, (send, wait, cb, socket) => {
632
+ handshake(send, wait)
633
+ .then(wait())
634
+ .then(() => socket.end())
635
+ .then(cb, cb);
636
+ }));
637
+
638
+ it('throw in handler-error handler becomes uncaught exception', connectionTest((c, cb) => {
639
+ const expectedErr = new Error('handler-error handler explodes');
640
+ process.once('uncaughtException', (err) => {
641
+ assert.strictEqual(err, expectedErr);
642
+ cb();
643
+ });
644
+ c.on('handler-error', () => { throw expectedErr; });
645
+ c.on('blocked', () => { throw new Error('user blocked handler explodes'); });
646
+ c.open(OPEN_OPTS);
647
+ }, (send, wait, cb) => {
648
+ handshake(send, wait)
649
+ .then(() => send(defs.ConnectionBlocked, { reason: 'memory' }, 0))
650
+ .then(cb, cb);
651
+ }));
652
+ });
653
+
303
654
  describe('heartbeats', () => {
304
655
 
305
656
  beforeEach(() => {