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() {
56 const pendingReconnect
= sinon
.stub();
57 listener
.reconnectPending
= setTimeout(pendingReconnect
, 100);
58 await listener
.stop();
60 assert(!pendingReconnect
.called
);
62 it('closes existing connection', async
function () {
63 listener
.connection
= connectionStub
;
64 await listener
.stop();
65 assert(connectionStub
.client
.removeListener
.called
);
66 assert
.strictEqual(listener
.connection
, null);
67 assert(options
.connectionLostCallback
.called
);
71 describe('_reconnect', function () {
72 it('reconnects', async
function () {
73 await listener
._reconnect(0, 1);
74 assert(listener
.connection
);
75 assert(options
.connectionEstablishedCallback
.called
);
77 it('closes existing connection before reconnecting', async
function () {
78 const existingConnection
= {
81 listener
.connection
= existingConnection
;
82 await listener
._reconnect(0, 1);
83 assert(existingConnection
.done
.called
);
85 it('overrides a pending reconnect', async
function () {
87 const pendingReconnect
= sinon
.stub();
88 listener
.reconnectPending
= setTimeout(pendingReconnect
, 100);
89 await listener
._reconnect(0, 1);
91 assert(!pendingReconnect
.called
);
93 it('fails with no remaining retries', async
function () {
94 const expected
= new Error('foo');
95 pgpStub
.connect
= sinon
.stub().rejects(expected
);
97 await listener
._reconnect(0, 0);
98 assert
.fail(noExpectedException
);
100 assert
.deepStrictEqual(e
, expected
);
103 it('fails all remaining retries', async
function () {
104 const expected
= new Error('foo');
105 pgpStub
.connect
= sinon
.stub().rejects(expected
);
107 await listener
._reconnect(0, 1);
108 assert
.fail(noExpectedException
);
110 assert
.deepStrictEqual(e
, expected
);
113 it('fails first retry', async
function () {
114 const expected
= new Error('foo');
115 pgpStub
.connect
= sinon
.stub().onCall(0).rejects(expected
).resolves(connectionStub
);
116 await listener
._reconnect(0, 1);
117 assert(options
.connectionEstablishedCallback
.called
);
121 describe('_onConnectionLost', function () {
123 beforeEach(function () {
124 error
= new Error('blah');
125 event
= connectionStub
;
126 sinon
.stub(listener
, '_reconnect');
128 it('success', async
function () {
129 await listener
._onConnectionLost(error
, event
);
130 assert
.strictEqual(listener
.connection
, null);
131 assert(event
.client
.removeListener
.called
);
132 assert(listener
.options
.connectionLostCallback
.called
);
133 assert(listener
._reconnect
.called
);
135 it('covers reconnect failure', async
function () {
136 listener
._reconnect
.rejects(error
);
137 await listener
._onConnectionLost(error
, event
);
138 assert
.strictEqual(listener
.connection
, null);
139 assert(event
.client
.removeListener
.called
);
140 assert(listener
.options
.connectionLostCallback
.called
);
141 assert(listener
._reconnect
.called
);
143 it('covers listener removal failure', async
function () {
144 event
.client
.removeListener
.throws(error
);
145 await listener
._onConnectionLost(error
, event
);
146 assert
.strictEqual(listener
.connection
, null);
147 assert(event
.client
.removeListener
.called
);
148 assert(listener
.options
.connectionLostCallback
.called
);
149 assert(listener
._reconnect
.called
);
151 }); // _onConnectionLost
153 describe('_onNotification', function () {
154 it('sends data', async
function () {
158 await listener
._onNotification(data
);
159 assert(listener
.options
.dataCallback
.called
);
161 it('ignores pings', async
function () {
165 await listener
._onNotification(data
);
166 assert(!listener
.options
.dataCallback
.called
);
168 }); // _onNotification
170 describe('_sendPing', function () {
171 it('covers no connection', async
function () {
173 await listener
._sendPing();
175 clearTimeout(listener
.nextPingTimeout
);
177 it('success', async
function () {
179 listener
.connection
= connectionStub
;
180 await listener
._sendPing();
182 clearTimeout(listener
.nextPingTimeout
);
183 assert(connectionStub
.none
.called
);
185 it('covers error', async
function () {
186 const err
= new Error('blah');
188 listener
.connection
= connectionStub
;
189 listener
.connection
.none
.rejects(err
);
190 await listener
._sendPing();
192 clearTimeout(listener
.nextPingTimeout
);
193 assert(listener
.connection
.none
.called
);
198 }); // Postgres Listener