| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 | 'use strict'const net = require('net')const co = require('co')const expect = require('expect.js')const describe = require('mocha').describeconst it = require('mocha').itconst before = require('mocha').beforeconst after = require('mocha').afterconst Pool = require('../')describe('connection timeout', () => {  const connectionFailure = new Error('Temporary connection failure')  before((done) => {    this.server = net.createServer((socket) => {      socket.on('data', () => {        // discard any buffered data or the server wont terminate      })    })    this.server.listen(() => {      this.port = this.server.address().port      done()    })  })  after((done) => {    this.server.close(done)  })  it('should callback with an error if timeout is passed', (done) => {    const pool = new Pool({ connectionTimeoutMillis: 10, port: this.port, host: 'localhost' })    pool.connect((err, client, release) => {      expect(err).to.be.an(Error)      expect(err.message).to.contain('timeout')      expect(client).to.equal(undefined)      expect(pool.idleCount).to.equal(0)      done()    })  })  it('should reject promise with an error if timeout is passed', (done) => {    const pool = new Pool({ connectionTimeoutMillis: 10, port: this.port, host: 'localhost' })    pool.connect().catch((err) => {      expect(err).to.be.an(Error)      expect(err.message).to.contain('timeout')      expect(pool.idleCount).to.equal(0)      done()    })  })  it(    'should handle multiple timeouts',    co.wrap(      function* () {        const errors = []        const pool = new Pool({ connectionTimeoutMillis: 1, port: this.port, host: 'localhost' })        for (var i = 0; i < 15; i++) {          try {            yield pool.connect()          } catch (e) {            errors.push(e)          }        }        expect(errors).to.have.length(15)      }.bind(this)    )  )  it('should timeout on checkout of used connection', (done) => {    const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 })    pool.connect((err, client, release) => {      expect(err).to.be(undefined)      expect(client).to.not.be(undefined)      pool.connect((err, client) => {        expect(err).to.be.an(Error)        expect(client).to.be(undefined)        release()        pool.end(done)      })    })  })  it('should not break further pending checkouts on a timeout', (done) => {    const pool = new Pool({ connectionTimeoutMillis: 200, max: 1 })    pool.connect((err, client, releaseOuter) => {      expect(err).to.be(undefined)      pool.connect((err, client) => {        expect(err).to.be.an(Error)        expect(client).to.be(undefined)        releaseOuter()      })      setTimeout(() => {        pool.connect((err, client, releaseInner) => {          expect(err).to.be(undefined)          expect(client).to.not.be(undefined)          releaseInner()          pool.end(done)        })      }, 100)    })  })  it('should timeout on query if all clients are busy', (done) => {    const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 })    pool.connect((err, client, release) => {      expect(err).to.be(undefined)      expect(client).to.not.be(undefined)      pool.query('select now()', (err, result) => {        expect(err).to.be.an(Error)        expect(result).to.be(undefined)        release()        pool.end(done)      })    })  })  it('should recover from timeout errors', (done) => {    const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 })    pool.connect((err, client, release) => {      expect(err).to.be(undefined)      expect(client).to.not.be(undefined)      pool.query('select now()', (err, result) => {        expect(err).to.be.an(Error)        expect(result).to.be(undefined)        release()        pool.query('select $1::text as name', ['brianc'], (err, res) => {          expect(err).to.be(undefined)          expect(res.rows).to.have.length(1)          pool.end(done)        })      })    })  })  it('continues processing after a connection failure', (done) => {    const Client = require('pg').Client    const orgConnect = Client.prototype.connect    let called = false    Client.prototype.connect = function (cb) {      // Simulate a failure on first call      if (!called) {        called = true        return setTimeout(() => {          cb(connectionFailure)        }, 100)      }      // And pass-through the second call      orgConnect.call(this, cb)    }    const pool = new Pool({      Client: Client,      connectionTimeoutMillis: 1000,      max: 1,    })    pool.connect((err, client, release) => {      expect(err).to.be(connectionFailure)      pool.query('select $1::text as name', ['brianc'], (err, res) => {        expect(err).to.be(undefined)        expect(res.rows).to.have.length(1)        pool.end(done)      })    })  })  it('releases newly connected clients if the queued already timed out', (done) => {    const Client = require('pg').Client    const orgConnect = Client.prototype.connect    let connection = 0    Client.prototype.connect = function (cb) {      // Simulate a failure on first call      if (connection === 0) {        connection++        return setTimeout(() => {          cb(connectionFailure)        }, 300)      }      // And second connect taking > connection timeout      if (connection === 1) {        connection++        return setTimeout(() => {          orgConnect.call(this, cb)        }, 1000)      }      orgConnect.call(this, cb)    }    const pool = new Pool({      Client: Client,      connectionTimeoutMillis: 1000,      max: 1,    })    // Direct connect    pool.connect((err, client, release) => {      expect(err).to.be(connectionFailure)    })    // Queued    let called = 0    pool.connect((err, client, release) => {      // Verify the callback is only called once      expect(called++).to.be(0)      expect(err).to.be.an(Error)      pool.query('select $1::text as name', ['brianc'], (err, res) => {        expect(err).to.be(undefined)        expect(res.rows).to.have.length(1)        pool.end(done)      })    })  })})
 |