Add tests for event bus.
This commit is contained in:
parent
097fd6e3ba
commit
725120a24e
36
lib/bus.js
36
lib/bus.js
@ -11,30 +11,30 @@ function Bus(params) {
|
||||
util.inherits(Bus, events.EventEmitter);
|
||||
|
||||
Bus.prototype.subscribe = function(name) {
|
||||
for (var i = 0; i < this.db.modules; i++) {
|
||||
var module = this.db.modules[i];
|
||||
var events = module.getEvents();
|
||||
for (var j = 0; i < events.length; j++) {
|
||||
var eventName = events[0];
|
||||
var subscribeHandler = events[2];
|
||||
var params = arguments.slice(1);
|
||||
if (name === eventName) {
|
||||
subscribeHandler.apply(events[1], params);
|
||||
for (var i = 0; i < this.db.modules.length; i++) {
|
||||
var mod = this.db.modules[i];
|
||||
var events = mod.getPublishEvents();
|
||||
for (var j = 0; j < events.length; j++) {
|
||||
var event = events[j];
|
||||
var params = Array.prototype.slice.call(arguments).slice(1);
|
||||
params.unshift(this);
|
||||
if (name === event.name) {
|
||||
event.subscribe.apply(event.scope, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Bus.prototype.unsubscribe = function(name) {
|
||||
for (var i = 0; i < this.db.modules; i++) {
|
||||
var module = this.db.modules[i];
|
||||
var events = module.getEvents();
|
||||
for (var j = 0; i < events.length; j++) {
|
||||
var eventName = events[0];
|
||||
var unsubscribeHandler = events[3];
|
||||
var params = arguments.slice(1);
|
||||
if (name === eventName) {
|
||||
unsubscribeHandler.apply(events[1], params);
|
||||
for (var i = 0; i < this.db.modules.length; i++) {
|
||||
var mod = this.db.modules[i];
|
||||
var events = mod.getPublishEvents();
|
||||
for (var j = 0; j < events.length; j++) {
|
||||
var event = events[j];
|
||||
var params = Array.prototype.slice.call(arguments).slice(1);
|
||||
params.unshift(this);
|
||||
if (name === event.name) {
|
||||
event.unsubscribe.apply(event.scope, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,18 @@ Module.prototype.blockHandler = function(block, add, callback) {
|
||||
setImmediate(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* the bus events available for subscription
|
||||
* @return {Array} an array of event info
|
||||
*/
|
||||
Module.prototype.getPublishEvents = function() {
|
||||
// Example:
|
||||
// return [
|
||||
// ['eventname', this, this.subscribeEvent, this.unsubscribeEvent],
|
||||
// ];
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* the API methods to expose
|
||||
* @return {Array} return array of methods
|
||||
@ -33,4 +45,4 @@ Module.prototype.getAPIMethods = function() {
|
||||
//
|
||||
// };
|
||||
|
||||
module.exports = Module;
|
||||
module.exports = Module;
|
||||
|
||||
@ -7,14 +7,17 @@ var chainlib = require('chainlib');
|
||||
var log = chainlib.log;
|
||||
var errors = chainlib.errors;
|
||||
var bitcore = require('bitcore');
|
||||
var $ = bitcore.util.preconditions;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var PublicKey = bitcore.PublicKey;
|
||||
var Address = bitcore.Address;
|
||||
|
||||
var AddressModule = function(options) {
|
||||
BaseModule.call(this, options);
|
||||
|
||||
this.txSubscriptions = {};
|
||||
this.balanceSubscriptions = {};
|
||||
this.subscriptions = {};
|
||||
this.subscriptions.transaction = {};
|
||||
this.subscriptions.balance = {};
|
||||
};
|
||||
|
||||
inherits(AddressModule, BaseModule);
|
||||
@ -32,10 +35,20 @@ AddressModule.prototype.getAPIMethods = function() {
|
||||
];
|
||||
};
|
||||
|
||||
AddressModule.prototype.getEvents = function() {
|
||||
AddressModule.prototype.getPublishEvents = function() {
|
||||
return [
|
||||
['transaction', this, this.subscribeTransaction, this.unsubscribeTransaction],
|
||||
['balance', this, this.subscribeBalance, this.unsubscribeBalance]
|
||||
{
|
||||
name: 'transaction',
|
||||
scope: this,
|
||||
subscribe: this.subscribe.bind(this, 'transaction'),
|
||||
unsubscribe: this.unsubscribe.bind(this, 'transaction')
|
||||
},
|
||||
{
|
||||
name: 'balance',
|
||||
scope: this,
|
||||
subscribe: this.subscribe.bind(this, 'balance'),
|
||||
unsubscribe: this.unsubscribe.bind(this, 'balance')
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
@ -93,25 +106,10 @@ AddressModule.prototype.blockHandler = function(block, addOutput, callback) {
|
||||
value: [output.satoshis, script, height].join(':')
|
||||
});
|
||||
|
||||
if(self.txSubscriptions[address]) {
|
||||
var subscriptions = self.txSubscriptions[address];
|
||||
for(var i = 0; i < subscriptions.length; i++) {
|
||||
subscriptions[i].emit('transaction', tx);
|
||||
}
|
||||
}
|
||||
// publish events to any subscribers
|
||||
this.transactionEventHandler(block, address, tx);
|
||||
this.balanceEventHandler(block, address);
|
||||
|
||||
if(self.balanceSubscriptions[address]) {
|
||||
var emitters = self.balanceSubsriptions[address];
|
||||
self.getBalance(address, true, function(err, balance) {
|
||||
if(err) {
|
||||
return self.emit(err);
|
||||
}
|
||||
|
||||
for(var i = 0; i < subscriptions.length; i++) {
|
||||
emitters[i].emit('balance', address, balance, block);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if(tx.isCoinbase()) {
|
||||
@ -125,42 +123,49 @@ AddressModule.prototype.blockHandler = function(block, addOutput, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
AddressModule.prototype.subscribeTransaction = function(emitter, addresses) {
|
||||
for(var i = 0; i < addresses.length; i++) {
|
||||
if(!this.txSubscriptions[addresses[i]]) {
|
||||
this.txSubscriptions[addresses[i]] = [];
|
||||
AddressModule.prototype.transactionEventHandler = function(block, address, tx) {
|
||||
if(this.subscriptions.transaction[address]) {
|
||||
var emitters = this.subscriptions.transaction[address];
|
||||
for(var k = 0; k < emitters.length; k++) {
|
||||
emitters[k].emit('transaction', address, tx, block);
|
||||
}
|
||||
|
||||
this.txSubscriptions[addresses[i]].push(emitter);
|
||||
}
|
||||
};
|
||||
|
||||
AddressModule.prototype.subscribeBalance = function(emitter, addresses) {
|
||||
for(var i = 0; i < addresses.length; i++) {
|
||||
if(!this.balanceSubscriptions[addresses[i]]) {
|
||||
this.balanceSubscriptions[addresses[i]] = [];
|
||||
}
|
||||
|
||||
this.balanceSubscriptions[addresses[i]].push(emitter);
|
||||
}
|
||||
};
|
||||
|
||||
AddressModule.prototype.unsubscribeTransaction = function(emitter, addresses) {
|
||||
for(var i = 0; i < addresses.length; i++) {
|
||||
if(this.txSubscriptions[addresses[i]]) {
|
||||
var emitters = self.txSubscriptions[addresses[i]];
|
||||
var index = emitters.indexOf(emitter);
|
||||
if(index > -1) {
|
||||
emitters.splice(index, 1);
|
||||
AddressModule.prototype.balanceEventHandler = function(block, address) {
|
||||
if(this.subscriptions.balance[address]) {
|
||||
var emitters = this.subscriptions.balance[address];
|
||||
this.getBalance(address, true, function(err, balance) {
|
||||
if(err) {
|
||||
return this.emit(err);
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < emitters.length; i++) {
|
||||
emitters[i].emit('balance', address, balance, block);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
AddressModule.prototype.unsubscribeBalance = function(emitter, addresses) {
|
||||
AddressModule.prototype.subscribe = function(name, emitter, addresses) {
|
||||
$.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter');
|
||||
$.checkArgument(Array.isArray(addresses), 'Second argument is expected to be an Array of addresses');
|
||||
|
||||
for(var i = 0; i < addresses.length; i++) {
|
||||
if(this.balanceSubscriptions[addresses[i]]) {
|
||||
var emitters = self.balanceSubscriptions[addresses[i]];
|
||||
if(!this.subscriptions[name][addresses[i]]) {
|
||||
this.subscriptions[name][addresses[i]] = [];
|
||||
}
|
||||
this.subscriptions[name][addresses[i]].push(emitter);
|
||||
}
|
||||
};
|
||||
|
||||
AddressModule.prototype.unsubscribe = function(name, emitter, addresses) {
|
||||
$.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter');
|
||||
$.checkArgument(Array.isArray(addresses), 'Second argument is expected to be an Array of addresses');
|
||||
|
||||
for(var i = 0; i < addresses.length; i++) {
|
||||
if(this.subscriptions[name][addresses[i]]) {
|
||||
var emitters = this.subscriptions[name][addresses[i]];
|
||||
var index = emitters.indexOf(emitter);
|
||||
if(index > -1) {
|
||||
emitters.splice(index, 1);
|
||||
@ -276,4 +281,4 @@ AddressModule.prototype.isSpent = function(output, queryMempool, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = AddressModule;
|
||||
module.exports = AddressModule;
|
||||
|
||||
61
test/bus.unit.js
Normal file
61
test/bus.unit.js
Normal file
@ -0,0 +1,61 @@
|
||||
'use strict';
|
||||
|
||||
var should = require('chai').should();
|
||||
var sinon = require('sinon');
|
||||
var Bus = require('../lib/bus');
|
||||
|
||||
describe('Bus', function() {
|
||||
|
||||
describe('#subscribe', function() {
|
||||
it('will call modules subscribe function with the correct arguments', function() {
|
||||
var subscribe = sinon.spy();
|
||||
var db = {
|
||||
modules: [
|
||||
{
|
||||
getPublishEvents: sinon.stub().returns([
|
||||
{
|
||||
name: 'test',
|
||||
scope: this,
|
||||
subscribe: subscribe,
|
||||
}
|
||||
])
|
||||
}
|
||||
]
|
||||
};
|
||||
var bus = new Bus({db: db});
|
||||
bus.subscribe('test', 'a', 'b', 'c');
|
||||
subscribe.callCount.should.equal(1);
|
||||
subscribe.args[0][0].should.equal(bus);
|
||||
subscribe.args[0][1].should.equal('a');
|
||||
subscribe.args[0][2].should.equal('b');
|
||||
subscribe.args[0][3].should.equal('c');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#unsubscribe', function() {
|
||||
it('will call modules unsubscribe function with the correct arguments', function() {
|
||||
var unsubscribe = sinon.spy();
|
||||
var db = {
|
||||
modules: [
|
||||
{
|
||||
getPublishEvents: sinon.stub().returns([
|
||||
{
|
||||
name: 'test',
|
||||
scope: this,
|
||||
unsubscribe: unsubscribe
|
||||
}
|
||||
])
|
||||
}
|
||||
]
|
||||
};
|
||||
var bus = new Bus({db: db});
|
||||
bus.unsubscribe('test', 'a', 'b', 'c');
|
||||
unsubscribe.callCount.should.equal(1);
|
||||
unsubscribe.args[0][0].should.equal(bus);
|
||||
unsubscribe.args[0][1].should.equal('a');
|
||||
unsubscribe.args[0][2].should.equal('b');
|
||||
unsubscribe.args[0][3].should.equal('c');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@ -2,8 +2,6 @@
|
||||
|
||||
var should = require('chai').should();
|
||||
var sinon = require('sinon');
|
||||
var chainlib = require('chainlib');
|
||||
var levelup = chainlib.deps.levelup;
|
||||
var bitcoindjs = require('../../');
|
||||
var AddressModule = bitcoindjs.modules.AddressModule;
|
||||
var blockData = require('../data/livenet-345003.json');
|
||||
@ -21,6 +19,40 @@ describe('AddressModule', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getPublishEvents', function() {
|
||||
it('will return an array of publish event objects', function() {
|
||||
var am = new AddressModule({});
|
||||
am.subscribe = sinon.spy();
|
||||
am.unsubscribe = sinon.spy();
|
||||
var events = am.getPublishEvents();
|
||||
|
||||
var callCount = 0;
|
||||
function testName(event, name) {
|
||||
event.name.should.equal(name);
|
||||
event.scope.should.equal(am);
|
||||
var emitter = new EventEmitter();
|
||||
var addresses = [];
|
||||
event.subscribe(emitter, addresses);
|
||||
am.subscribe.callCount.should.equal(callCount + 1);
|
||||
am.subscribe.args[callCount][0].should.equal(name);
|
||||
am.subscribe.args[callCount][1].should.equal(emitter);
|
||||
am.subscribe.args[callCount][2].should.equal(addresses);
|
||||
am.subscribe.thisValues[callCount].should.equal(am);
|
||||
event.unsubscribe(emitter, addresses);
|
||||
am.unsubscribe.callCount.should.equal(callCount + 1);
|
||||
am.unsubscribe.args[callCount][0].should.equal(name);
|
||||
am.unsubscribe.args[callCount][1].should.equal(emitter);
|
||||
am.unsubscribe.args[callCount][2].should.equal(addresses);
|
||||
am.unsubscribe.thisValues[callCount].should.equal(am);
|
||||
callCount++;
|
||||
}
|
||||
events.forEach(function(event) {
|
||||
testName(event, event.name);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('#blockHandler', function() {
|
||||
var block = bitcore.Block.fromString(blockData);
|
||||
var db = {
|
||||
@ -124,6 +156,129 @@ describe('AddressModule', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('will call event handlers', function() {
|
||||
var block = bitcore.Block.fromString(blockData);
|
||||
var db = {
|
||||
getTransactionsFromBlock: function() {
|
||||
return block.transactions.slice(0, 8);
|
||||
}
|
||||
};
|
||||
var am = new AddressModule({db: db, network: 'livenet'});
|
||||
am.transactionEventHandler = sinon.spy();
|
||||
am.balanceEventHandler = sinon.spy();
|
||||
am.blockHandler(
|
||||
{
|
||||
__height: 345003,
|
||||
timestamp: new Date(1424836934000)
|
||||
},
|
||||
true,
|
||||
function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
am.transactionEventHandler.callCount.should.equal(11);
|
||||
am.balanceEventHandler.callCount.should.equal(11);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#transactionEventHandler', function() {
|
||||
it('will emit a transaction if there is a subscriber', function(done) {
|
||||
var am = new AddressModule({});
|
||||
var emitter = new EventEmitter();
|
||||
am.subscriptions.transaction = {
|
||||
'1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter]
|
||||
};
|
||||
var block = {};
|
||||
var tx = {};
|
||||
emitter.on('transaction', function(address, t, b) {
|
||||
address.should.equal('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N');
|
||||
t.should.equal(tx);
|
||||
b.should.equal(block);
|
||||
done();
|
||||
});
|
||||
am.transactionEventHandler(block, '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N', tx);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#balanceEventHandler', function() {
|
||||
it('will emit a balance if there is a subscriber', function(done) {
|
||||
var am = new AddressModule({});
|
||||
var emitter = new EventEmitter();
|
||||
am.subscriptions.balance = {
|
||||
'1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter]
|
||||
};
|
||||
var block = {};
|
||||
var balance = 1000;
|
||||
am.getBalance = sinon.stub().callsArgWith(2, null, balance);
|
||||
emitter.on('balance', function(address, bal, b) {
|
||||
address.should.equal('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N');
|
||||
bal.should.equal(balance);
|
||||
b.should.equal(block);
|
||||
done();
|
||||
});
|
||||
am.balanceEventHandler(block, '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#subscribe', function() {
|
||||
it('will add emitters to the subscribers array (transaction)', function() {
|
||||
var am = new AddressModule({});
|
||||
var emitter = new EventEmitter();
|
||||
|
||||
var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N';
|
||||
var name = 'transaction';
|
||||
am.subscribe(name, emitter, [address]);
|
||||
am.subscriptions.transaction[address].should.deep.equal([emitter]);
|
||||
|
||||
var address2 = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W';
|
||||
am.subscribe(name, emitter, [address2]);
|
||||
am.subscriptions.transaction[address2].should.deep.equal([emitter]);
|
||||
|
||||
var emitter2 = new EventEmitter();
|
||||
am.subscribe(name, emitter2, [address]);
|
||||
am.subscriptions.transaction[address].should.deep.equal([emitter, emitter2]);
|
||||
});
|
||||
it('will add an emitter to the subscribers array (balance)', function() {
|
||||
var am = new AddressModule({});
|
||||
var emitter = new EventEmitter();
|
||||
var name = 'balance';
|
||||
var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N';
|
||||
am.subscribe(name, emitter, [address]);
|
||||
am.subscriptions.balance[address].should.deep.equal([emitter]);
|
||||
|
||||
var address2 = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W';
|
||||
am.subscribe(name, emitter, [address2]);
|
||||
am.subscriptions.balance[address2].should.deep.equal([emitter]);
|
||||
|
||||
var emitter2 = new EventEmitter();
|
||||
am.subscribe(name, emitter2, [address]);
|
||||
am.subscriptions.balance[address].should.deep.equal([emitter, emitter2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#unsubscribe', function() {
|
||||
it('will remove emitter from subscribers array (transaction)', function() {
|
||||
var am = new AddressModule({});
|
||||
var emitter = new EventEmitter();
|
||||
var emitter2 = new EventEmitter();
|
||||
var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N';
|
||||
am.subscriptions.transaction[address] = [emitter, emitter2];
|
||||
var name = 'transaction';
|
||||
am.unsubscribe(name, emitter, [address]);
|
||||
am.subscriptions.transaction[address].should.deep.equal([emitter2]);
|
||||
});
|
||||
it('will remove emitter from subscribers array (balance)', function() {
|
||||
var am = new AddressModule({});
|
||||
var emitter = new EventEmitter();
|
||||
var emitter2 = new EventEmitter();
|
||||
var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N';
|
||||
var name = 'balance';
|
||||
am.subscriptions.balance[address] = [emitter, emitter2];
|
||||
am.unsubscribe(name, emitter, [address]);
|
||||
am.subscriptions.balance[address].should.deep.equal([emitter2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getBalance', function() {
|
||||
|
||||
@ -31,6 +31,15 @@ var Node = proxyquire('../lib/node', {
|
||||
chainlib.Node = OriginalNode;
|
||||
|
||||
describe('Bitcoind Node', function() {
|
||||
describe('#openBus', function() {
|
||||
it('will create a new bus', function() {
|
||||
var node = new Node({});
|
||||
var db = {};
|
||||
node.db = db;
|
||||
var bus = node.openBus();
|
||||
bus.db.should.equal(db);
|
||||
});
|
||||
});
|
||||
describe('#_loadConfiguration', function() {
|
||||
it('should call the necessary methods', function() {
|
||||
var node = new Node({});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user