bot18
Version:
A high-frequency cryptocurrency trading bot by Zenbot creator @carlos8f
620 lines (542 loc) • 19.9 kB
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(err, obj) {
return function(err, obj) {
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 borrowCount = 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();
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, factory.max);
assert.equal(0, factory.min);
});
},
'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, factory.max);
assert.equal(0, factory.min);
});
},
'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();
beforeExit(function() {
assert.equal(3, factory.max);
assert.equal(2, factory.min);
});
},
'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() {
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.equal(typeof client.id, 'number');
// should be removed second
setTimeout(function() { pool.release(client); }, 5);
});
pool.acquire(function(err, client) {
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) {
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){
if (err) {throw 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.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){
pool.destroy(obj);
});
assert.equal(destroyCalled, true);
assert.equal(pool.availableObjectsCount(), 0);
},
'removes from available objects on validation failure': function(beforeExit){
var destroyCalled = false,
validateCalled = false,
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){
pool.release(obj);
assert.equal(obj.count, 0);
pool.acquire(function(err, obj){
pool.release(obj);
assert.equal(obj.count, 1);
});
});
assert.equal(validateCalled, true);
assert.equal(destroyCalled, 0);
assert.equal(pool.availableObjectsCount(), 1);
},
'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);
});
}
};