4 const assert
= require('assert');
5 const sinon
= require('sinon');
6 const stubLogger
= require('../../stub-logger');
7 const Listener
= require('../../../src/db/postgres/listener');
9 const snooze
= async (ms
) => new Promise((resolve
) => setTimeout(resolve
, ms
));
10 const noExpectedException
= 'did not get expected exception';
12 describe('Postgres Listener', function () {
13 let listener
, options
, connectionStub
, pgpStub
;
14 beforeEach(function () {
18 removeListener: sinon
.stub(),
24 connect: sinon
.stub().resolves(connectionStub
),
27 dataCallback: sinon
.stub(),
28 connectionLostCallback: sinon
.stub(),
29 connectionEstablishedCallback: sinon
.stub(),
31 reconnectDelayMs: 1000,
34 listener
= new Listener(stubLogger
, pgpStub
, options
);
36 afterEach(function () {
40 describe('start', function () {
41 it('covers', async
function () {
42 sinon
.stub(listener
, '_reconnect').resolves();
43 sinon
.stub(listener
, '_sendPing').resolves();
44 await listener
.start();
45 assert(listener
._reconnect
.called
);
46 assert(listener
._sendPing
.called
);
50 describe('stop', function () {
51 it('covers not started', async
function () {
52 await listener
.stop();
54 it('cancels pending reconnect', async
function() {
55 const pendingReconnect
= sinon
.stub();
56 listener
.reconnectPending
= setTimeout(pendingReconnect
, 100);
57 await listener
.stop();
59 assert(!pendingReconnect
.called
);
61 it('closes existing connection', async
function () {
62 listener
.connection
= connectionStub
;
63 await listener
.stop();
64 assert(connectionStub
.client
.removeListener
.called
);
65 assert
.strictEqual(listener
.connection
, null);
66 assert(options
.connectionLostCallback
.called
);
70 describe('_reconnect', function () {
71 it('reconnects', async
function () {
72 await listener
._reconnect(0, 1);
73 assert(listener
.connection
);
74 assert(options
.connectionEstablishedCallback
.called
);
76 it('closes existing connection before reconnecting', async
function () {
77 const existingConnection
= {
80 listener
.connection
= existingConnection
;
81 await listener
._reconnect(0, 1);
82 assert(existingConnection
.done
.called
);
84 it('overrides a pending reconnect', async
function () {
86 const pendingReconnect
= sinon
.stub();
87 listener
.reconnectPending
= setTimeout(pendingReconnect
, 100);
88 await listener
._reconnect(0, 1);
90 assert(!pendingReconnect
.called
);
92 it('fails with no remaining retries', async
function () {
93 const expected
= new Error('foo');
94 pgpStub
.connect
= sinon
.stub().rejects(expected
);
96 await listener
._reconnect(0, 0);
97 assert
.fail(noExpectedException
);
99 assert
.deepStrictEqual(e
, expected
);
102 it('fails all remaining retries', async
function () {
103 const expected
= new Error('foo');
104 pgpStub
.connect
= sinon
.stub().rejects(expected
);
106 await listener
._reconnect(0, 1);
107 assert
.fail(noExpectedException
);
109 assert
.deepStrictEqual(e
, expected
);
112 it('fails first retry', async
function () {
113 const expected
= new Error('foo');
114 pgpStub
.connect
= sinon
.stub().onCall(0).rejects(expected
).resolves(connectionStub
);
115 await listener
._reconnect(0, 1);
116 assert(options
.connectionEstablishedCallback
.called
);
120 describe('_onConnectionLost', function () {
122 beforeEach(function () {
123 error
= new Error('blah');
124 event
= connectionStub
;
125 sinon
.stub(listener
, '_reconnect');
127 it('success', async
function () {
128 await listener
._onConnectionLost(error
, event
);
129 assert
.strictEqual(listener
.connection
, null);
130 assert(event
.client
.removeListener
.called
);
131 assert(listener
.options
.connectionLostCallback
.called
);
132 assert(listener
._reconnect
.called
);
134 it('covers reconnect failure', async
function () {
135 listener
._reconnect
.rejects(error
);
136 await listener
._onConnectionLost(error
, event
);
137 assert
.strictEqual(listener
.connection
, null);
138 assert(event
.client
.removeListener
.called
);
139 assert(listener
.options
.connectionLostCallback
.called
);
140 assert(listener
._reconnect
.called
);
142 it('covers listener removal failure', async
function () {
143 event
.client
.removeListener
.throws(error
);
144 await listener
._onConnectionLost(error
, event
);
145 assert
.strictEqual(listener
.connection
, null);
146 assert(event
.client
.removeListener
.called
);
147 assert(listener
.options
.connectionLostCallback
.called
);
148 assert(listener
._reconnect
.called
);
150 }); // _onConnectionLost
152 describe('_onNotification', function () {
153 it('sends data', async
function () {
157 await listener
._onNotification(data
);
158 assert(listener
.options
.dataCallback
.called
);
160 it('ignores pings', async
function () {
164 await listener
._onNotification(data
);
165 assert(!listener
.options
.dataCallback
.called
);
167 }); // _onNotification
169 describe('_sendPing', function () {
170 it('covers no connection', async
function () {
172 await listener
._sendPing();
174 clearTimeout(listener
.nextPingTimeout
);
176 it('success', async
function () {
178 listener
.connection
= connectionStub
;
179 await listener
._sendPing();
181 clearTimeout(listener
.nextPingTimeout
);
182 assert(connectionStub
.none
.called
);
184 it('covers error', async
function () {
185 const err
= new Error('blah');
187 listener
.connection
= connectionStub
;
188 listener
.connection
.none
.rejects(err
);
189 await listener
._sendPing();
191 clearTimeout(listener
.nextPingTimeout
);
192 assert(listener
.connection
.none
.called
);
197 }); // Postgres Listener