fix a postgres-listener test
[websub-hub] / test / src / db / postgres-listener.js
1 /* eslint-env mocha */
2 'use strict';
3
4 const assert = require('assert');
5 const sinon = require('sinon');
6 const stubLogger = require('../../stub-logger');
7 const Listener = require('../../../src/db/postgres/listener');
8
9 const snooze = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
10 const noExpectedException = 'did not get expected exception';
11
12 describe('Postgres Listener', function () {
13 let listener, options, connectionStub, pgpStub;
14 beforeEach(function () {
15 connectionStub = {
16 client: {
17 on: sinon.stub(),
18 removeListener: sinon.stub(),
19 },
20 done: sinon.stub(),
21 none: sinon.stub(),
22 };
23 pgpStub = {
24 connect: sinon.stub().resolves(connectionStub),
25 };
26 options = {
27 dataCallback: sinon.stub(),
28 connectionLostCallback: sinon.stub(),
29 connectionEstablishedCallback: sinon.stub(),
30 pingDelayMs: 100,
31 reconnectDelayMs: 1000,
32 reconnectTimes: 1,
33 };
34 listener = new Listener(stubLogger, pgpStub, options);
35 });
36 afterEach(function () {
37 sinon.restore();
38 });
39
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);
47 });
48 }); // start
49
50 describe('stop', function () {
51 it('covers not started', async function () {
52 await listener.stop();
53 });
54 it('cancels pending reconnect', async function() {
55 this.slow(300);
56 const pendingReconnect = sinon.stub();
57 listener.reconnectPending = setTimeout(pendingReconnect, 100);
58 await listener.stop();
59 await snooze(110);
60 assert(!pendingReconnect.called);
61 });
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);
68 });
69 }); // stop
70
71 describe('_reconnect', function () {
72 it('reconnects', async function () {
73 await listener._reconnect(0, 1);
74 assert(listener.connection);
75 assert(options.connectionEstablishedCallback.called);
76 });
77 it('closes existing connection before reconnecting', async function () {
78 const existingConnection = {
79 done: sinon.stub(),
80 };
81 listener.connection = existingConnection;
82 await listener._reconnect(0, 1);
83 assert(existingConnection.done.called);
84 });
85 it('overrides a pending reconnect', async function () {
86 this.slow(300);
87 const pendingReconnect = sinon.stub();
88 listener.reconnectPending = setTimeout(pendingReconnect, 100);
89 await listener._reconnect(0, 1);
90 await snooze(110);
91 assert(!pendingReconnect.called);
92 });
93 it('fails with no remaining retries', async function () {
94 const expected = new Error('foo');
95 pgpStub.connect = sinon.stub().rejects(expected);
96 try {
97 await listener._reconnect(0, 0);
98 assert.fail(noExpectedException);
99 } catch (e) {
100 assert.deepStrictEqual(e, expected);
101 }
102 });
103 it('fails all remaining retries', async function () {
104 const expected = new Error('foo');
105 pgpStub.connect = sinon.stub().rejects(expected);
106 try {
107 await listener._reconnect(0, 1);
108 assert.fail(noExpectedException);
109 } catch (e) {
110 assert.deepStrictEqual(e, expected);
111 }
112 });
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);
118 });
119 }); // _reconnect
120
121 describe('_onConnectionLost', function () {
122 let error, event;
123 beforeEach(function () {
124 error = new Error('blah');
125 event = connectionStub;
126 sinon.stub(listener, '_reconnect');
127 });
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);
134 });
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);
142 });
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);
150 });
151 }); // _onConnectionLost
152
153 describe('_onNotification', function () {
154 it('sends data', async function () {
155 const data = {
156 payload: 'foo',
157 };
158 await listener._onNotification(data);
159 assert(listener.options.dataCallback.called);
160 });
161 it('ignores pings', async function () {
162 const data = {
163 payload: 'ping',
164 };
165 await listener._onNotification(data);
166 assert(!listener.options.dataCallback.called);
167 });
168 }); // _onNotification
169
170 describe('_sendPing', function () {
171 it('covers no connection', async function () {
172 this.slow(300);
173 await listener._sendPing();
174 await snooze(110);
175 clearTimeout(listener.nextPingTimeout);
176 });
177 it('success', async function () {
178 this.slow(300);
179 listener.connection = connectionStub;
180 await listener._sendPing();
181 await snooze(110);
182 clearTimeout(listener.nextPingTimeout);
183 assert(connectionStub.none.called);
184 });
185 it('covers error', async function () {
186 const err = new Error('blah');
187 this.slow(300);
188 listener.connection = connectionStub;
189 listener.connection.none.rejects(err);
190 await listener._sendPing();
191 await snooze(110);
192 clearTimeout(listener.nextPingTimeout);
193 assert(listener.connection.none.called);
194
195 });
196 }); // _sendPing
197
198 }); // Postgres Listener