nas-denar/node_modules/generic-pool/test/generic-pool.test.js
2017-09-14 20:06:51 +02:00

798 lines
21 KiB
JavaScript

var assert = require('assert')
var poolModule = require('..')
module.exports = {
'expands to max limit': function (beforeExit) {
var createCount = 0
var destroyCount = 0
var borrowCount = 0
var factory = {
name: 'test1',
create: function (callback) {
callback(null, { count: ++createCount })
},
destroy: function (client) { destroyCount++ },
max: 2,
idleTimeoutMillis: 100
}
var pool = poolModule.Pool(factory)
for (var i = 0; i < 10; i++) {
var full = !pool.acquire(function () {
return function (err, obj) {
assert.ifError(err)
assert.equal(typeof obj.count, 'number')
setTimeout(function () {
borrowCount++
pool.release(obj)
}, 100)
}
}())
assert.ok((i < 1) ^ full)
}
beforeExit(function () {
assert.equal(0, factory.min)
assert.equal(2, createCount)
assert.equal(2, destroyCount)
assert.equal(10, borrowCount)
})
},
'respects min limit': function (beforeExit) {
var createCount = 0
var destroyCount = 0
var pool = poolModule.Pool({
name: 'test-min',
create: function (callback) {
callback(null, { count: ++createCount })
},
destroy: function (client) { destroyCount++ },
min: 1,
max: 2,
idleTimeoutMillis: 100
})
pool.drain(function () {
pool.destroyAllNow()
})
beforeExit(function () {
assert.equal(0, pool.availableObjectsCount())
assert.equal(1, createCount)
assert.equal(1, destroyCount)
})
},
'min and max limit defaults': function (beforeExit) {
var factory = {
name: 'test-limit-defaults',
create: function (callback) { callback(null, {}) },
destroy: function (client) {},
idleTimeoutMillis: 100
}
var pool = poolModule.Pool(factory)
beforeExit(function () {
assert.equal(1, pool.getMaxPoolSize())
assert.equal(0, pool.getMinPoolSize())
})
},
'malformed min and max limits are ignored': function (beforeExit) {
var factory = {
name: 'test-limit-defaults2',
create: function (callback) { callback(null, {}) },
destroy: function (client) {},
idleTimeoutMillis: 100,
min: 'asf',
max: []
}
var pool = poolModule.Pool(factory)
beforeExit(function () {
assert.equal(1, pool.getMaxPoolSize())
assert.equal(0, pool.getMinPoolSize())
})
},
'min greater than max sets to max minus one': function (beforeExit) {
var factory = {
name: 'test-limit-defaults3',
create: function (callback) { callback(null, {}) },
destroy: function (client) {},
idleTimeoutMillis: 100,
min: 5,
max: 3
}
var pool = poolModule.Pool(factory)
pool.drain(function () {
pool.destroyAllNow()
})
beforeExit(function () {
assert.equal(3, pool.getMaxPoolSize())
assert.equal(2, pool.getMinPoolSize())
})
},
'supports priority on borrow': function (beforeExit) {
var borrowTimeLow = 0
var borrowTimeHigh = 0
var borrowCount = 0
var i
var pool = poolModule.Pool({
name: 'test2',
create: function (callback) { callback() },
destroy: function (client) {},
max: 1,
idleTimeoutMillis: 100,
priorityRange: 2
})
for (i = 0; i < 10; i++) {
pool.acquire((function (err, obj) {
return function () {
setTimeout(function () {
assert.ifError(err)
var t = new Date().getTime()
if (t > borrowTimeLow) { borrowTimeLow = t }
borrowCount++
pool.release(obj)
}, 50)
}
}()), 1)
}
for (i = 0; i < 10; i++) {
pool.acquire((function (obj) {
return function () {
setTimeout(function () {
var t = new Date().getTime()
if (t > borrowTimeHigh) { borrowTimeHigh = t }
borrowCount++
pool.release(obj)
}, 50)
}
}()), 0)
}
beforeExit(function () {
assert.equal(20, borrowCount)
assert.equal(true, borrowTimeLow > borrowTimeHigh)
})
},
'removes correct object on reap': function (beforeExit) {
var destroyed = []
var clientCount = 0
var pool = poolModule.Pool({
name: 'test3',
create: function (callback) { callback(null, { id: ++clientCount }) },
destroy: function (client) { destroyed.push(client.id) },
max: 2,
idleTimeoutMillis: 100
})
pool.acquire(function (err, client) {
assert.ifError(err)
assert.equal(typeof client.id, 'number')
// should be removed second
setTimeout(function () { pool.release(client) }, 5)
})
pool.acquire(function (err, client) {
assert.ifError(err)
assert.equal(typeof client.id, 'number')
// should be removed first
pool.release(client)
})
setTimeout(function () {}, 102)
beforeExit(function () {
assert.equal(2, destroyed[0])
assert.equal(1, destroyed[1])
})
},
'tests drain': function (beforeExit) {
var created = 0
var destroyed = 0
var count = 5
var acquired = 0
var pool = poolModule.Pool({
name: 'test4',
create: function (callback) { callback(null, {id: ++created}) },
destroy: function (client) { destroyed += 1 },
max: 2,
idletimeoutMillis: 300000
})
for (var i = 0; i < count; i++) {
pool.acquire(function (err, client) {
assert.ifError(err)
acquired += 1
assert.equal(typeof client.id, 'number')
setTimeout(function () { pool.release(client) }, 250)
})
}
assert.notEqual(count, acquired)
pool.drain(function () {
assert.equal(count, acquired)
// short circuit the absurdly long timeouts above.
pool.destroyAllNow()
beforeExit(function () {})
})
// subsequent calls to acquire should return an error.
assert.throws(function () {
pool.acquire(function (client) {})
}, Error)
},
'handle creation errors': function (beforeExit) {
var created = 0
var pool = poolModule.Pool({
name: 'test6',
create: function (callback) {
if (created < 5) {
callback(new Error('Error occurred.'))
} else {
callback({ id: created })
}
created++
},
destroy: function (client) {},
max: 1,
idleTimeoutMillis: 1000
})
// ensure that creation errors do not populate the pool.
for (var i = 0; i < 5; i++) {
pool.acquire(function (err, client) {
assert.ok(err instanceof Error)
assert.ok(client === null)
})
}
var called = false
pool.acquire(function (err, client) {
assert.ok(err === null)
assert.equal(typeof client.id, 'number')
called = true
})
beforeExit(function () {
assert.ok(called)
assert.equal(pool.waitingClientsCount(), 0)
})
},
'handle creation errors for delayed creates': function (beforeExit) {
var created = 0
var pool = poolModule.Pool({
name: 'test6',
create: function (callback) {
if (created < 5) {
setTimeout(function () {
callback(new Error('Error occurred.'))
}, 0)
} else {
setTimeout(function () {
callback({ id: created })
}, 0)
}
created++
},
destroy: function (client) {},
max: 1,
idleTimeoutMillis: 1000
})
// ensure that creation errors do not populate the pool.
for (var i = 0; i < 5; i++) {
pool.acquire(function (err, client) {
assert.ok(err instanceof Error)
assert.ok(client === null)
})
}
var called = false
pool.acquire(function (err, client) {
assert.ok(err === null)
assert.equal(typeof client.id, 'number')
called = true
})
beforeExit(function () {
assert.ok(called)
assert.equal(pool.waitingClientsCount(), 0)
})
},
'pooled decorator should acquire and release': function (beforeExit) {
var assertion_count = 0
var destroyed_count = 0
var pool = poolModule.Pool({
name: 'test1',
create: function (callback) { callback({id: Math.floor(Math.random() * 1000)}) },
destroy: function (client) { destroyed_count += 1 },
max: 1,
idleTimeoutMillis: 100
})
var pooledFn = pool.pooled(function (client, cb) {
assert.equal(typeof client.id, 'number')
assert.equal(pool.getPoolSize(), 1)
assertion_count += 2
cb()
})
assert.equal(pool.getPoolSize(), 0)
assertion_count += 1
pooledFn(function (err) {
if (err) { throw err }
assert.ok(true)
assertion_count += 1
})
beforeExit(function () {
assert.equal(assertion_count, 4)
assert.equal(destroyed_count, 1)
})
},
'pooled decorator should pass arguments and return values': function (beforeExit) {
var assertion_count = 0
var pool = poolModule.Pool({
name: 'test1',
create: function (callback) { callback({id: Math.floor(Math.random() * 1000)}) },
destroy: function (client) {},
max: 1,
idleTimeoutMillis: 100
})
var pooledFn = pool.pooled(function (client, arg1, arg2, cb) {
assert.equal(arg1, 'First argument')
assert.equal(arg2, 'Second argument')
assertion_count += 2
cb(null, 'First return', 'Second return')
})
pooledFn('First argument', 'Second argument', function (err, retVal1, retVal2) {
if (err) { throw err }
assert.equal(retVal1, 'First return')
assert.equal(retVal2, 'Second return')
assertion_count += 2
})
beforeExit(function () {
assert.equal(assertion_count, 4)
})
},
'pooled decorator should allow undefined callback': function (beforeExit) {
var assertion_count = 0
var pool = poolModule.Pool({
name: 'test1',
create: function (callback) { callback({id: Math.floor(Math.random() * 1000)}) },
destroy: function (client) {},
max: 1,
idleTimeoutMillis: 100
})
var pooledFn = pool.pooled(function (client, arg, cb) {
assert.equal(arg, 'Arg!')
assertion_count += 1
cb()
})
pooledFn('Arg!')
beforeExit(function () {
assert.equal(pool.getPoolSize(), 0)
assert.equal(assertion_count, 1)
})
},
'pooled decorator should forward pool errors': function (beforeExit) {
var assertion_count = 0
var pool = poolModule.Pool({
name: 'test1',
create: function (callback) { callback(new Error('Pool error')) },
destroy: function (client) {},
max: 1,
idleTimeoutMillis: 100
})
var pooledFn = pool.pooled(function (cb) {
assert.ok(false, "Pooled function shouldn't be called due to a pool error")
})
pooledFn(function (err, obj) {
assert.equal(err.message, 'Pool error')
assertion_count += 1
})
beforeExit(function () {
assert.equal(assertion_count, 1)
})
},
'getPoolSize': function (beforeExit) {
var assertion_count = 0
var pool = poolModule.Pool({
name: 'test1',
create: function (callback) { callback({id: Math.floor(Math.random() * 1000)}) },
destroy: function (client) {},
max: 2,
idleTimeoutMillis: 100
})
assert.equal(pool.getPoolSize(), 0)
assertion_count += 1
pool.acquire(function (err, obj1) {
if (err) { throw err }
assert.equal(pool.getPoolSize(), 1)
assertion_count += 1
pool.acquire(function (err, obj2) {
if (err) { throw err }
assert.equal(pool.getPoolSize(), 2)
assertion_count += 1
pool.release(obj1)
pool.release(obj2)
pool.acquire(function (err, obj3) {
if (err) { throw err }
// should still be 2
assert.equal(pool.getPoolSize(), 2)
assertion_count += 1
pool.release(obj3)
})
})
})
beforeExit(function () {
assert.equal(assertion_count, 4)
})
},
'availableObjectsCount': function (beforeExit) {
var assertion_count = 0
var pool = poolModule.Pool({
name: 'test1',
create: function (callback) { callback({id: Math.floor(Math.random() * 1000)}) },
destroy: function (client) {},
max: 2,
idleTimeoutMillis: 100
})
assert.equal(pool.availableObjectsCount(), 0)
assertion_count += 1
pool.acquire(function (err, obj1) {
if (err) { throw err }
assert.equal(pool.availableObjectsCount(), 0)
assertion_count += 1
pool.acquire(function (err, obj2) {
if (err) { throw err }
assert.equal(pool.availableObjectsCount(), 0)
assertion_count += 1
pool.release(obj1)
assert.equal(pool.availableObjectsCount(), 1)
assertion_count += 1
pool.release(obj2)
assert.equal(pool.availableObjectsCount(), 2)
assertion_count += 1
pool.acquire(function (err, obj3) {
if (err) { throw err }
assert.equal(pool.availableObjectsCount(), 1)
assertion_count += 1
pool.release(obj3)
assert.equal(pool.availableObjectsCount(), 2)
assertion_count += 1
})
})
})
beforeExit(function () {
assert.equal(assertion_count, 7)
})
},
'logPassesLogLevel': function (beforeExit) {
var loglevels = {'verbose': 0, 'info': 1, 'warn': 2, 'error': 3}
var logmessages = {verbose: [], info: [], warn: [], error: []}
var factory = {
name: 'test1',
create: function (callback) { callback(null, {id: Math.floor(Math.random() * 1000)}) },
destroy: function (client) {},
max: 2,
idleTimeoutMillis: 100,
log: function (msg, level) { testlog(msg, level) }
}
var testlog = function (msg, level) {
assert.ok(level in loglevels)
logmessages[level].push(msg)
}
var pool = poolModule.Pool(factory)
var pool2 = poolModule.Pool({
name: 'testNoLog',
create: function (callback) { callback(null, {id: Math.floor(Math.random() * 1000)}) },
destroy: function (client) {},
max: 2,
idleTimeoutMillis: 100
})
assert.equal(pool2.getName(), 'testNoLog')
pool.acquire(function (err, obj) {
assert.ifError(err)
assert.equal(logmessages.verbose[0], 'createResource() - creating obj - count=1 min=0 max=2')
assert.equal(logmessages.info[0], 'dispense() clients=1 available=0')
logmessages.info = []
logmessages.verbose = []
pool2.borrow(function (err, obj) {
assert.ifError(err)
assert.equal(logmessages.info.length, 0)
assert.equal(logmessages.verbose.length, 0)
assert.equal(logmessages.warn.length, 0)
})
})
},
'removes from available objects on destroy': function (beforeExit) {
var destroyCalled = false
var factory = {
name: 'test',
create: function (callback) { callback(null, {}) },
destroy: function (client) { destroyCalled = true },
max: 2,
idleTimeoutMillis: 100
}
var pool = poolModule.Pool(factory)
pool.acquire(function (err, obj) {
assert.ifError(err)
pool.destroy(obj)
})
assert.equal(destroyCalled, true)
assert.equal(pool.availableObjectsCount(), 0)
},
'removes from available objects on validation failure': function (beforeExit) {
var destroyCalled = false
var validateCalled = false
var count = 0
var factory = {
name: 'test',
create: function (callback) { callback(null, {count: count++}) },
destroy: function (client) { destroyCalled = client.count },
validate: function (client) { validateCalled = true; return client.count > 0 },
max: 2,
idleTimeoutMillis: 100
}
var pool = poolModule.Pool(factory)
pool.acquire(function (err, obj) {
assert.ifError(err)
pool.release(obj)
assert.equal(obj.count, 0)
pool.acquire(function (err, obj) {
assert.ifError(err)
pool.release(obj)
assert.equal(obj.count, 1)
})
})
assert.equal(validateCalled, true)
assert.equal(destroyCalled, 0)
assert.equal(pool.availableObjectsCount(), 1)
},
'removes from available objects on async validation failure': function (beforeExit) {
var destroyCalled = false
var validateCalled = false
var count = 0
var factory = {
name: 'test',
create: function (callback) { callback(null, {count: count++}) },
destroy: function (client) { destroyCalled = client.count },
validateAsync: function (client, callback) { validateCalled = true; callback(client.count > 0) },
max: 2,
idleTimeoutMillis: 100
}
var pool = poolModule.Pool(factory)
pool.acquire(function (err, obj) {
assert.ifError(err)
pool.release(obj)
assert.equal(obj.count, 0)
pool.acquire(function (err, obj) {
assert.ifError(err)
pool.release(obj)
assert.equal(obj.count, 1)
})
})
assert.equal(validateCalled, true)
assert.equal(destroyCalled, 0)
assert.equal(pool.availableObjectsCount(), 1)
},
'error on setting both validate functions': function (beforeExit) {
var noop = function () {}
var factory = {
name: 'test',
create: noop,
destroy: noop,
validate: noop,
validateAsync: noop
}
try {
poolModule.Pool(factory)
} catch (err) {
assert.equal(err.message, 'Only one of validate or validateAsync may be specified')
}
},
'do schedule again if error occured when creating new Objects async': function (beforeExit) {
var factory = {
name: 'test',
create: function (callback) {
process.nextTick(function () {
var err = new Error('Create Error')
callback(err)
})
},
destroy: function (client) {},
max: 1,
idleTimeoutMillis: 100
}
var getFlag = 0
var pool = poolModule.Pool(factory)
pool.acquire(function () {})
pool.acquire(function (err, obj) {
getFlag = 1
assert(err)
assert.equal(pool.availableObjectsCount(), 0)
})
beforeExit(function () {
assert.equal(getFlag, 1)
})
},
'returns only valid object to the pool': function (beforeExit) {
var pool = poolModule.Pool({
name: 'test',
create: function (callback) {
process.nextTick(function () {
callback(null, { id: 'validId' })
})
},
destroy: function (client) {},
max: 1,
idleTimeoutMillis: 100
})
pool.acquire(function (err, obj) {
assert.ifError(err)
assert.equal(pool.availableObjectsCount(), 0)
assert.equal(pool.inUseObjectsCount(), 1)
// Invalid release
pool.release({})
assert.equal(pool.availableObjectsCount(), 0)
assert.equal(pool.inUseObjectsCount(), 1)
// Valid release
pool.release(obj)
assert.equal(pool.availableObjectsCount(), 1)
assert.equal(pool.inUseObjectsCount(), 0)
})
},
'validate acquires object from the pool': function (beforeExit) {
var pool = poolModule.Pool({
name: 'test',
create: function (callback) {
process.nextTick(function () {
callback(null, { id: 'validId' })
})
},
validate: function (resource) {
return true
},
destroy: function (client) {},
max: 1,
idleTimeoutMillis: 100
})
pool.acquire(function (err, obj) {
assert.ifError(err)
assert.equal(pool.availableObjectsCount(), 0)
assert.equal(pool.inUseObjectsCount(), 1)
})
},
'validateAsync acquires object from the pool': function (beforeExit) {
var pool = poolModule.Pool({
name: 'test',
create: function (callback) {
process.nextTick(function () {
callback(null, { id: 'validId' })
})
},
validateAsync: function (resource, callback) {
callback(true)
},
destroy: function (client) {},
max: 1,
idleTimeoutMillis: 100
})
pool.acquire(function (err, obj) {
assert.ifError(err)
assert.equal(pool.availableObjectsCount(), 0)
assert.equal(pool.inUseObjectsCount(), 1)
})
},
'domain context is preserved on acquire callback': function (beforeExit) {
var assertion_count = 0
var pool = poolModule.Pool({
name: 'test',
create: function (cb) {
cb(null, {})
},
destroy: function (client) {},
max: 2,
idleTimeoutMillis: 1000
})
// bail on old node versions because domains didn't exist until v0.8
if (process.version < 'v0.8') {
return
}
var domain = require('domain')
function check (index) {
var wrapDomain = domain.create()
wrapDomain.index = index
wrapDomain.run(function () {
pool.acquire(function (err, client) {
assert.ifError(err)
assert.equal(domain.active.index, index)
assertion_count++
setTimeout(function () {
pool.release(client)
}, 50)
})
})
}
// first two will work even without domain binding
check(1)
check(2)
// third and on will fail without domain binding
check(3)
beforeExit(function () {
assert.equal(assertion_count, 3)
})
}
}