1 | 'use strict'
|
2 | const net = require('net')
|
3 | const co = require('co')
|
4 | const expect = require('expect.js')
|
5 |
|
6 | const describe = require('mocha').describe
|
7 | const it = require('mocha').it
|
8 |
|
9 | const Pool = require('../')
|
10 |
|
11 | describe('pool error handling', function () {
|
12 | it('Should complete these queries without dying', function (done) {
|
13 | const pool = new Pool()
|
14 | let errors = 0
|
15 | let shouldGet = 0
|
16 | function runErrorQuery() {
|
17 | shouldGet++
|
18 | return new Promise(function (resolve, reject) {
|
19 | pool
|
20 | .query("SELECT 'asd'+1 ")
|
21 | .then(function (res) {
|
22 | reject(res)
|
23 | })
|
24 | .catch(function (err) {
|
25 | errors++
|
26 | resolve(err)
|
27 | })
|
28 | })
|
29 | }
|
30 | const ps = []
|
31 | for (let i = 0; i < 5; i++) {
|
32 | ps.push(runErrorQuery())
|
33 | }
|
34 | Promise.all(ps).then(function () {
|
35 | expect(shouldGet).to.eql(errors)
|
36 | pool.end(done)
|
37 | })
|
38 | })
|
39 |
|
40 | it('Catches errors in client.query', async function () {
|
41 | let caught = false
|
42 | const pool = new Pool()
|
43 | try {
|
44 | await pool.query(null)
|
45 | } catch (e) {
|
46 | caught = true
|
47 | }
|
48 | pool.end()
|
49 | expect(caught).to.be(true)
|
50 | })
|
51 |
|
52 | describe('calling release more than once', () => {
|
53 | it(
|
54 | 'should throw each time',
|
55 | co.wrap(function* () {
|
56 | const pool = new Pool()
|
57 | const client = yield pool.connect()
|
58 | client.release()
|
59 | expect(() => client.release()).to.throwError()
|
60 | expect(() => client.release()).to.throwError()
|
61 | return yield pool.end()
|
62 | })
|
63 | )
|
64 |
|
65 | it('should throw each time with callbacks', function (done) {
|
66 | const pool = new Pool()
|
67 |
|
68 | pool.connect(function (err, client, clientDone) {
|
69 | expect(err).not.to.be.an(Error)
|
70 | clientDone()
|
71 |
|
72 | expect(() => clientDone()).to.throwError()
|
73 | expect(() => clientDone()).to.throwError()
|
74 |
|
75 | pool.end(done)
|
76 | })
|
77 | })
|
78 | })
|
79 |
|
80 | describe('using an ended pool', () => {
|
81 | it('rejects all additional promises', (done) => {
|
82 | const pool = new Pool()
|
83 | const promises = []
|
84 | pool.end().then(() => {
|
85 | const squash = (promise) => promise.catch((e) => 'okay!')
|
86 | promises.push(squash(pool.connect()))
|
87 | promises.push(squash(pool.query('SELECT NOW()')))
|
88 | promises.push(squash(pool.end()))
|
89 | Promise.all(promises).then((res) => {
|
90 | expect(res).to.eql(['okay!', 'okay!', 'okay!'])
|
91 | done()
|
92 | })
|
93 | })
|
94 | })
|
95 |
|
96 | it('returns an error on all additional callbacks', (done) => {
|
97 | const pool = new Pool()
|
98 | pool.end(() => {
|
99 | pool.query('SELECT *', (err) => {
|
100 | expect(err).to.be.an(Error)
|
101 | pool.connect((err) => {
|
102 | expect(err).to.be.an(Error)
|
103 | pool.end((err) => {
|
104 | expect(err).to.be.an(Error)
|
105 | done()
|
106 | })
|
107 | })
|
108 | })
|
109 | })
|
110 | })
|
111 | })
|
112 |
|
113 | describe('error from idle client', () => {
|
114 | it(
|
115 | 'removes client from pool',
|
116 | co.wrap(function* () {
|
117 | const pool = new Pool()
|
118 | const client = yield pool.connect()
|
119 | expect(pool.totalCount).to.equal(1)
|
120 | expect(pool.waitingCount).to.equal(0)
|
121 | expect(pool.idleCount).to.equal(0)
|
122 | client.release()
|
123 | yield new Promise((resolve, reject) => {
|
124 | process.nextTick(() => {
|
125 | let poolError
|
126 | pool.once('error', (err) => {
|
127 | poolError = err
|
128 | })
|
129 |
|
130 | let clientError
|
131 | client.once('error', (err) => {
|
132 | clientError = err
|
133 | })
|
134 |
|
135 | client.emit('error', new Error('expected'))
|
136 |
|
137 | expect(clientError.message).to.equal('expected')
|
138 | expect(poolError.message).to.equal('expected')
|
139 | expect(pool.idleCount).to.equal(0)
|
140 | expect(pool.totalCount).to.equal(0)
|
141 | pool.end().then(resolve, reject)
|
142 | })
|
143 | })
|
144 | })
|
145 | )
|
146 | })
|
147 |
|
148 | describe('error from in-use client', () => {
|
149 | it(
|
150 | 'keeps the client in the pool',
|
151 | co.wrap(function* () {
|
152 | const pool = new Pool()
|
153 | const client = yield pool.connect()
|
154 | expect(pool.totalCount).to.equal(1)
|
155 | expect(pool.waitingCount).to.equal(0)
|
156 | expect(pool.idleCount).to.equal(0)
|
157 |
|
158 | yield new Promise((resolve, reject) => {
|
159 | process.nextTick(() => {
|
160 | let poolError
|
161 | pool.once('error', (err) => {
|
162 | poolError = err
|
163 | })
|
164 |
|
165 | let clientError
|
166 | client.once('error', (err) => {
|
167 | clientError = err
|
168 | })
|
169 |
|
170 | client.emit('error', new Error('expected'))
|
171 |
|
172 | expect(clientError.message).to.equal('expected')
|
173 | expect(poolError).not.to.be.ok()
|
174 | expect(pool.idleCount).to.equal(0)
|
175 | expect(pool.totalCount).to.equal(1)
|
176 | client.release()
|
177 | pool.end().then(resolve, reject)
|
178 | })
|
179 | })
|
180 | })
|
181 | )
|
182 | })
|
183 |
|
184 | describe('passing a function to pool.query', () => {
|
185 | it('calls back with error', (done) => {
|
186 | const pool = new Pool()
|
187 | console.log('passing fn to query')
|
188 | pool.query((err) => {
|
189 | expect(err).to.be.an(Error)
|
190 | pool.end(done)
|
191 | })
|
192 | })
|
193 | })
|
194 |
|
195 | describe('pool with lots of errors', () => {
|
196 | it(
|
197 | 'continues to work and provide new clients',
|
198 | co.wrap(function* () {
|
199 | const pool = new Pool({ max: 1 })
|
200 | const errors = []
|
201 | for (var i = 0; i < 20; i++) {
|
202 | try {
|
203 | yield pool.query('invalid sql')
|
204 | } catch (err) {
|
205 | errors.push(err)
|
206 | }
|
207 | }
|
208 | expect(errors).to.have.length(20)
|
209 | expect(pool.idleCount).to.equal(0)
|
210 | expect(pool.query).to.be.a(Function)
|
211 | const res = yield pool.query('SELECT $1::text as name', ['brianc'])
|
212 | expect(res.rows).to.have.length(1)
|
213 | expect(res.rows[0].name).to.equal('brianc')
|
214 | return pool.end()
|
215 | })
|
216 | )
|
217 | })
|
218 |
|
219 | it('should continue with queued items after a connection failure', (done) => {
|
220 | const closeServer = net
|
221 | .createServer((socket) => {
|
222 | socket.destroy()
|
223 | })
|
224 | .unref()
|
225 |
|
226 | closeServer.listen(() => {
|
227 | const pool = new Pool({ max: 1, port: closeServer.address().port, host: 'localhost' })
|
228 | pool.connect((err) => {
|
229 | expect(err).to.be.an(Error)
|
230 | if (err.code) {
|
231 | expect(err.code).to.be('ECONNRESET')
|
232 | }
|
233 | })
|
234 | pool.connect((err) => {
|
235 | expect(err).to.be.an(Error)
|
236 | if (err.code) {
|
237 | expect(err.code).to.be('ECONNRESET')
|
238 | }
|
239 | closeServer.close(() => {
|
240 | pool.end(done)
|
241 | })
|
242 | })
|
243 | })
|
244 | })
|
245 |
|
246 | it('handles post-checkout client failures in pool.query', (done) => {
|
247 | const pool = new Pool({ max: 1 })
|
248 | pool.on('error', () => {
|
249 |
|
250 | })
|
251 | pool.query('SELECT pg_sleep(5)', [], (err) => {
|
252 | expect(err).to.be.an(Error)
|
253 | done()
|
254 | })
|
255 |
|
256 | setTimeout(() => {
|
257 | pool._clients[0].end()
|
258 | }, 1000)
|
259 | })
|
260 | })
|