update tests
This commit is contained in:
parent
8d39d02ee6
commit
14e21fadb0
3
index.js
3
index.js
@ -9,5 +9,8 @@ module.exports.DB = require('./lib/db');
|
||||
module.exports.Transaction = require('./lib/transaction');
|
||||
module.exports.errors = require('./lib/errors');
|
||||
|
||||
module.exports.modules = {};
|
||||
module.exports.modules.AddressModule = require('./lib/modules/address');
|
||||
|
||||
module.exports.deps = {};
|
||||
module.exports.deps.chainlib = require('chainlib');
|
||||
@ -5,6 +5,7 @@ var inherits = require('util').inherits;
|
||||
var async = require('async');
|
||||
var chainlib = require('chainlib');
|
||||
var log = chainlib.log;
|
||||
var errors = chainlib.errors;
|
||||
var bitcore = require('bitcore');
|
||||
var PublicKey = bitcore.PublicKey;
|
||||
var Address = bitcore.Address;
|
||||
|
||||
435
test/db.unit.js
435
test/db.unit.js
@ -11,6 +11,8 @@ var bitcore = require('bitcore');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var errors = bitcoindjs.errors;
|
||||
var memdown = require('memdown');
|
||||
var inherits = require('util').inherits;
|
||||
var BaseModule = require('../lib/module');
|
||||
|
||||
describe('Bitcoin DB', function() {
|
||||
var coinbaseAmount = 50 * 1e8;
|
||||
@ -196,395 +198,128 @@ describe('Bitcoin DB', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_updateOutputs', function() {
|
||||
var block = bitcore.Block.fromString(blockData);
|
||||
var db = new DB({path: 'path', store: memdown, network: 'livenet'});
|
||||
db.getTransactionsFromBlock = function() {
|
||||
return block.transactions.slice(0, 8);
|
||||
};
|
||||
|
||||
var data = [
|
||||
{
|
||||
key: {
|
||||
address: '1F1MAvhTKg2VG29w8cXsiSN2PJ8gSsrJw',
|
||||
timestamp: 1424836934000,
|
||||
txid: 'fdbefe0d064729d85556bd3ab13c3a889b685d042499c02b4aa2064fb1e16923',
|
||||
outputIndex: 0
|
||||
},
|
||||
value: {
|
||||
satoshis: 2502227470,
|
||||
script: 'OP_DUP OP_HASH160 20 0x02a61d2066d19e9e2fd348a8320b7ebd4dd3ca2b OP_EQUALVERIFY OP_CHECKSIG',
|
||||
blockHeight: 345003
|
||||
}
|
||||
},
|
||||
{
|
||||
key: {
|
||||
prevTxId: '3d7d5d98df753ef2a4f82438513c509e3b11f3e738e94a7234967b03a03123a9',
|
||||
prevOutputIndex: 32
|
||||
},
|
||||
value: {
|
||||
txid: '5780f3ee54889a0717152a01abee9a32cec1b0cdf8d5537a08c7bd9eeb6bfbca',
|
||||
inputIndex: 0,
|
||||
timestamp: 1424836934000
|
||||
}
|
||||
},
|
||||
{
|
||||
key: {
|
||||
address: '1Ep5LA4T6Y7zaBPiwruUJurjGFvCJHzJhm',
|
||||
timestamp: 1424836934000,
|
||||
txid: 'e66f3b989c790178de2fc1a5329f94c0d8905d0d3df4e7ecf0115e7f90a6283d',
|
||||
outputIndex: 1
|
||||
},
|
||||
value: {
|
||||
satoshis: 3100000,
|
||||
script: 'OP_DUP OP_HASH160 20 0x9780ccd5356e2acc0ee439ee04e0fe69426c7528 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
blockHeight: 345003
|
||||
}
|
||||
}
|
||||
];
|
||||
var key0 = data[0].key;
|
||||
var value0 = data[0].value;
|
||||
var key3 = data[1].key;
|
||||
var value3 = data[1].value;
|
||||
var key64 = data[2].key;
|
||||
var value64 = data[2].value;
|
||||
|
||||
it('should create the correct operations when updating/adding outputs', function(done) {
|
||||
db._updateOutputs({height: 345003, timestamp: new Date(1424836934000)}, true, function(err, operations) {
|
||||
should.not.exist(err);
|
||||
operations.length.should.equal(11);
|
||||
operations[0].type.should.equal('put');
|
||||
var expected0 = ['outs', key0.address, key0.timestamp, key0.txid, key0.outputIndex].join('-');
|
||||
operations[0].key.should.equal(expected0);
|
||||
operations[0].value.should.equal([value0.satoshis, value0.script, value0.blockHeight].join(':'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should create the correct operations when removing outputs', function(done) {
|
||||
db._updateOutputs({height: 345003, timestamp: new Date(1424836934000)}, false, function(err, operations) {
|
||||
should.not.exist(err);
|
||||
operations.length.should.equal(11);
|
||||
operations[0].type.should.equal('del');
|
||||
operations[0].key.should.equal(['outs', key0.address, key0.timestamp, key0.txid, key0.outputIndex].join('-'));
|
||||
operations[0].value.should.equal([value0.satoshis, value0.script, value0.blockHeight].join(':'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should continue if output script is null', function(done) {
|
||||
var db = new DB({path: 'path', store: memdown, network: 'livenet'});
|
||||
var transactions = [
|
||||
{
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{
|
||||
script: null,
|
||||
satoshis: 1000,
|
||||
}
|
||||
],
|
||||
isCoinbase: sinon.stub().returns(false)
|
||||
}
|
||||
];
|
||||
db.getTransactionsFromBlock = function() {
|
||||
return transactions;
|
||||
};
|
||||
|
||||
db._updateOutputs({height: 345003, timestamp: new Date(1424836934000)}, false, function(err, operations) {
|
||||
should.not.exist(err);
|
||||
operations.length.should.equal(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('#_onChainAddBlock', function() {
|
||||
var db = new DB({path: 'path', store: memdown});
|
||||
db._updateOutputs = sinon.stub().callsArgWith(2, null, ['1a', '1b']);
|
||||
db.store = {
|
||||
batch: sinon.stub().callsArg(1)
|
||||
};
|
||||
|
||||
it('should give error when there is a failure to write', function() {
|
||||
var errordb = new DB({path: 'path', store: memdown});
|
||||
errordb._updateOutputs = sinon.stub().callsArgWith(2, null, ['1a', '1b']);
|
||||
errordb.store = {
|
||||
batch: sinon.stub().callsArgWith(1, new Error('error'))
|
||||
it('should remove block from mempool and call blockHandler with true', function(done) {
|
||||
var db = new DB({store: memdown});
|
||||
db.mempool = {
|
||||
removeBlock: sinon.stub()
|
||||
};
|
||||
errordb._onChainAddBlock('block', function(err) {
|
||||
should.exist(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('should call block processing functions and write to database', function(done) {
|
||||
db._onChainAddBlock('block', function(err) {
|
||||
db.blockHandler = sinon.stub().callsArg(2);
|
||||
db._onChainAddBlock({hash: 'hash'}, function(err) {
|
||||
should.not.exist(err);
|
||||
db._updateOutputs.calledOnce.should.equal(true);
|
||||
db._updateOutputs.calledWith('block', true).should.equal(true);
|
||||
db.store.batch.args[0][0].should.deep.equal(['1a', '1b']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should halt on an error and not write to database', function(done) {
|
||||
db._updateOutputs.reset();
|
||||
db.store.batch.reset();
|
||||
db._updateOutputs = sinon.stub().callsArgWith(2, new Error('error'));
|
||||
db._onChainAddBlock('block', function(err) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('error');
|
||||
db._updateOutputs.calledOnce.should.equal(true);
|
||||
db._updateOutputs.calledWith('block', true).should.equal(true);
|
||||
db.store.batch.called.should.equal(false);
|
||||
db.mempool.removeBlock.args[0][0].should.equal('hash');
|
||||
db.blockHandler.args[0][1].should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_onChainRemoveBlock', function() {
|
||||
var db = new DB({path: 'path', store: memdown});
|
||||
db._updateOutputs = sinon.stub().callsArgWith(2, null, ['1a', '1b']);
|
||||
it('should call blockHandler with false', function(done) {
|
||||
var db = new DB({store: memdown});
|
||||
db.blockHandler = sinon.stub().callsArg(2);
|
||||
db._onChainRemoveBlock({hash: 'hash'}, function(err) {
|
||||
should.not.exist(err);
|
||||
db.blockHandler.args[0][1].should.equal(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#blockHandler', function() {
|
||||
var db = new DB({store: memdown});
|
||||
var Module1 = function() {};
|
||||
Module1.prototype.blockHandler = sinon.stub().callsArgWith(2, null, ['op1', 'op2', 'op3']);
|
||||
var Module2 = function() {};
|
||||
Module2.prototype.blockHandler = sinon.stub().callsArgWith(2, null, ['op4', 'op5']);
|
||||
db.modules = [
|
||||
new Module1(),
|
||||
new Module2()
|
||||
];
|
||||
db.store = {
|
||||
batch: sinon.stub().callsArg(1)
|
||||
};
|
||||
|
||||
it('should give error when there is a failure to write', function() {
|
||||
var errordb = new DB({path: 'path', store: memdown});
|
||||
errordb._updateOutputs = sinon.stub().callsArgWith(2, null, ['1a', '1b']);
|
||||
errordb.store = {
|
||||
batch: sinon.stub().callsArgWith(1, new Error('error'))
|
||||
};
|
||||
errordb._onChainRemoveBlock('block', function(err) {
|
||||
should.exist(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('should call block processing functions and write to database', function(done) {
|
||||
db._onChainRemoveBlock('block', function(err) {
|
||||
it('should call blockHandler in all modules and perform operations', function(done) {
|
||||
db.blockHandler('block', true, function(err) {
|
||||
should.not.exist(err);
|
||||
db._updateOutputs.calledOnce.should.equal(true);
|
||||
db._updateOutputs.calledWith('block', false).should.equal(true);
|
||||
db.store.batch.args[0][0].should.deep.equal(['1a', '1b']);
|
||||
db.store.batch.args[0][0].should.deep.equal(['op1', 'op2', 'op3', 'op4', 'op5']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should halt on an error and not write to database', function(done) {
|
||||
db._updateOutputs.reset();
|
||||
db.store.batch.reset();
|
||||
db._updateOutputs = sinon.stub().callsArgWith(2, new Error('error'));
|
||||
db._onChainRemoveBlock('block', function(err) {
|
||||
|
||||
it('should give an error if one of the modules gives an error', function(done) {
|
||||
var Module3 = function() {};
|
||||
Module3.prototype.blockHandler = sinon.stub().callsArgWith(2, new Error('error'));
|
||||
db.modules.push(new Module3());
|
||||
|
||||
db.blockHandler('block', true, function(err) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('error');
|
||||
db._updateOutputs.calledOnce.should.equal(true);
|
||||
db._updateOutputs.calledWith('block', false).should.equal(true);
|
||||
db.store.batch.called.should.equal(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getAPIMethods', function() {
|
||||
it('should return the correct methods', function() {
|
||||
var db = new DB({path: 'path', store: memdown});
|
||||
it('should return the correct db methods', function() {
|
||||
var db = new DB({store: memdown});
|
||||
db.modules = [];
|
||||
var methods = db.getAPIMethods();
|
||||
methods.length.should.equal(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getBalance', function() {
|
||||
it('should sum up the unspent outputs', function(done) {
|
||||
var db = new DB({path: 'path', store: memdown});
|
||||
var outputs = [
|
||||
{satoshis: 1000}, {satoshis: 2000}, {satoshis: 3000}
|
||||
];
|
||||
db.getUnspentOutputs = sinon.stub().callsArgWith(2, null, outputs);
|
||||
db.getBalance('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N', false, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.should.equal(6000);
|
||||
done();
|
||||
});
|
||||
methods.length.should.equal(1);
|
||||
});
|
||||
|
||||
it('will handle error from unspent outputs', function(done) {
|
||||
var db = new DB({path: 'path', store: memdown});
|
||||
db.getUnspentOutputs = sinon.stub().callsArgWith(2, new Error('error'));
|
||||
db.getBalance('someaddress', false, function(err) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('error');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#getOutputs', function() {
|
||||
var db = new DB({path: 'path', store: memdown});
|
||||
var address = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W';
|
||||
|
||||
it('should get outputs for an address', function(done) {
|
||||
var readStream1 = new EventEmitter();
|
||||
db.store = {
|
||||
createReadStream: sinon.stub().returns(readStream1)
|
||||
};
|
||||
var mempoolOutputs = [
|
||||
{
|
||||
address: '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W',
|
||||
txid: 'aa2db23f670596e96ed94c405fd11848c8f236d266ee96da37ecd919e53b4371',
|
||||
satoshis: 307627737,
|
||||
script: 'OP_DUP OP_HASH160 f6db95c81dea3d10f0ff8d890927751bf7b203c1 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
blockHeight: 352532
|
||||
it('should also return modules methods', function() {
|
||||
var module1 = {
|
||||
methods: function() {
|
||||
return [
|
||||
['module1-one', module1, module1, 2],
|
||||
['module1-two', module1, module1, 2]
|
||||
];
|
||||
}
|
||||
];
|
||||
db.bitcoind = {
|
||||
getMempoolOutputs: sinon.stub().returns(mempoolOutputs)
|
||||
};
|
||||
|
||||
db.getOutputs(address, true, function(err, outputs) {
|
||||
should.not.exist(err);
|
||||
outputs.length.should.equal(3);
|
||||
outputs[0].address.should.equal(address);
|
||||
outputs[0].txid.should.equal('125dd0e50fc732d67c37b6c56be7f9dc00b6859cebf982ee2cc83ed2d604bf87');
|
||||
outputs[0].outputIndex.should.equal(1);
|
||||
outputs[0].satoshis.should.equal(4527773864);
|
||||
outputs[0].script.should.equal('OP_DUP OP_HASH160 038a213afdfc551fc658e9a2a58a86e98d69b687 OP_EQUALVERIFY OP_CHECKSIG');
|
||||
outputs[0].blockHeight.should.equal(345000);
|
||||
outputs[1].address.should.equal(address);
|
||||
outputs[1].txid.should.equal('3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7');
|
||||
outputs[1].outputIndex.should.equal(2);
|
||||
outputs[1].satoshis.should.equal(10000);
|
||||
outputs[1].script.should.equal('OP_DUP OP_HASH160 038a213afdfc551fc658e9a2a58a86e98d69b687 OP_EQUALVERIFY OP_CHECKSIG');
|
||||
outputs[1].blockHeight.should.equal(345004);
|
||||
outputs[2].address.should.equal(address);
|
||||
outputs[2].txid.should.equal('aa2db23f670596e96ed94c405fd11848c8f236d266ee96da37ecd919e53b4371');
|
||||
outputs[2].script.should.equal('OP_DUP OP_HASH160 f6db95c81dea3d10f0ff8d890927751bf7b203c1 OP_EQUALVERIFY OP_CHECKSIG');
|
||||
outputs[2].blockHeight.should.equal(352532);
|
||||
done();
|
||||
});
|
||||
|
||||
var data1 = {
|
||||
key: ['outs', address, '1424835319000', '125dd0e50fc732d67c37b6c56be7f9dc00b6859cebf982ee2cc83ed2d604bf87', '1'].join('-'),
|
||||
value: ['4527773864', 'OP_DUP OP_HASH160 038a213afdfc551fc658e9a2a58a86e98d69b687 OP_EQUALVERIFY OP_CHECKSIG', '345000'].join(':')
|
||||
};
|
||||
|
||||
var data2 = {
|
||||
key: ['outs', address, '1424837300000', '3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7', '2'].join('-'),
|
||||
value: ['10000', 'OP_DUP OP_HASH160 038a213afdfc551fc658e9a2a58a86e98d69b687 OP_EQUALVERIFY OP_CHECKSIG', '345004'].join(':')
|
||||
};
|
||||
|
||||
readStream1.emit('data', data1);
|
||||
readStream1.emit('data', data2);
|
||||
readStream1.emit('close');
|
||||
});
|
||||
|
||||
it('should give an error if the readstream has an error', function(done) {
|
||||
var readStream2 = new EventEmitter();
|
||||
db.store = {
|
||||
createReadStream: sinon.stub().returns(readStream2)
|
||||
};
|
||||
|
||||
db.getOutputs(address, true, function(err, outputs) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('readstreamerror');
|
||||
done();
|
||||
});
|
||||
|
||||
readStream2.emit('error', new Error('readstreamerror'));
|
||||
process.nextTick(function() {
|
||||
readStream2.emit('close');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getUnspentOutputs', function() {
|
||||
it('should filter out spent outputs', function(done) {
|
||||
var outputs = [
|
||||
{
|
||||
satoshis: 1000,
|
||||
spent: false,
|
||||
},
|
||||
{
|
||||
satoshis: 2000,
|
||||
spent: true
|
||||
},
|
||||
{
|
||||
satoshis: 3000,
|
||||
spent: false
|
||||
var module2 = {
|
||||
methods: function() {
|
||||
return [
|
||||
['moudle2-one', module2, module2, 1]
|
||||
];
|
||||
}
|
||||
];
|
||||
var i = 0;
|
||||
|
||||
var db = new DB({path: 'path', store: memdown});
|
||||
db.getOutputs = sinon.stub().callsArgWith(2, null, outputs);
|
||||
db.isUnspent = function(output, queryMempool, callback) {
|
||||
callback(!outputs[i].spent);
|
||||
i++;
|
||||
};
|
||||
|
||||
db.getUnspentOutputs('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) {
|
||||
should.not.exist(err);
|
||||
outputs.length.should.equal(2);
|
||||
outputs[0].satoshis.should.equal(1000);
|
||||
outputs[1].satoshis.should.equal(3000);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should handle an error from getOutputs', function(done) {
|
||||
var db = new DB({path: 'path', store: memdown});
|
||||
db.getOutputs = sinon.stub().callsArgWith(2, new Error('error'));
|
||||
db.getUnspentOutputs('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('error');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should handle when there are no outputs', function(done) {
|
||||
var db = new DB({path: 'path', store: memdown});
|
||||
db.getOutputs = sinon.stub().callsArgWith(2, null, []);
|
||||
db.getUnspentOutputs('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) {
|
||||
should.exist(err);
|
||||
err.should.be.instanceof(errors.NoOutputs);
|
||||
outputs.length.should.equal(0);
|
||||
done();
|
||||
});
|
||||
var db = new DB({store: memdown});
|
||||
db.modules = [module1, module2];
|
||||
|
||||
var methods = db.getAPIMethods();
|
||||
methods.length.should.equal(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isUnspent', function() {
|
||||
var db = new DB({path: 'path', store: memdown});
|
||||
describe('#addModule', function() {
|
||||
it('instantiate module and add to db.modules', function() {
|
||||
var Module1 = function(options) {
|
||||
BaseModule.call(this, options);
|
||||
};
|
||||
inherits(Module1, BaseModule);
|
||||
|
||||
it('should give true when isSpent() gives false', function(done) {
|
||||
db.isSpent = sinon.stub().callsArgWith(2, false);
|
||||
db.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(unspent) {
|
||||
unspent.should.equal(true);
|
||||
done();
|
||||
});
|
||||
var db = new DB({store: memdown});
|
||||
db.bitcoind = {};
|
||||
db.network = {};
|
||||
db.modules = [];
|
||||
db.addModule(Module1);
|
||||
|
||||
db.modules.length.should.equal(1);
|
||||
should.exist(db.modules[0].db);
|
||||
should.exist(db.modules[0].bitcoind);
|
||||
should.exist(db.modules[0].network);
|
||||
});
|
||||
|
||||
it('should give false when isSpent() gives true', function(done) {
|
||||
db.isSpent = sinon.stub().callsArgWith(2, true);
|
||||
db.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(unspent) {
|
||||
unspent.should.equal(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should throw an error if module is not an instance of BaseModule', function() {
|
||||
var Module2 = function(options) {};
|
||||
var db = new DB({store: memdown});
|
||||
db.modules = [];
|
||||
|
||||
it('should give false when isSpent() returns an error', function(done) {
|
||||
db.isSpent = sinon.stub().callsArgWith(2, new Error('error'));
|
||||
db.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(unspent) {
|
||||
unspent.should.equal(false);
|
||||
done();
|
||||
});
|
||||
(function() {
|
||||
db.addModule(Module2);
|
||||
}).should.throw('bitcore.ErrorInvalidArgumentType');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isSpent', function() {
|
||||
var db = new DB({path: 'path', store: memdown});
|
||||
db.bitcoind = {
|
||||
isSpent: sinon.stub().returns(true)
|
||||
};
|
||||
|
||||
it('should give true if bitcoind.isSpent gives true', function(done) {
|
||||
db.isSpent('output', true, function(spent) {
|
||||
spent.should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
329
test/modules/address.unit.js
Normal file
329
test/modules/address.unit.js
Normal file
@ -0,0 +1,329 @@
|
||||
'use strict';
|
||||
|
||||
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');
|
||||
var bitcore = require('bitcore');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var errors = bitcoindjs.errors;
|
||||
|
||||
describe('AddressModule', function() {
|
||||
|
||||
describe('#methods', function() {
|
||||
it('should return the correct methods', function() {
|
||||
var am = new AddressModule({});
|
||||
var methods = am.methods();
|
||||
methods.length.should.equal(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#blockHandler', 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'});
|
||||
|
||||
var data = [
|
||||
{
|
||||
key: {
|
||||
address: '1F1MAvhTKg2VG29w8cXsiSN2PJ8gSsrJw',
|
||||
timestamp: 1424836934000,
|
||||
txid: 'fdbefe0d064729d85556bd3ab13c3a889b685d042499c02b4aa2064fb1e16923',
|
||||
outputIndex: 0
|
||||
},
|
||||
value: {
|
||||
satoshis: 2502227470,
|
||||
script: 'OP_DUP OP_HASH160 20 0x02a61d2066d19e9e2fd348a8320b7ebd4dd3ca2b OP_EQUALVERIFY OP_CHECKSIG',
|
||||
blockHeight: 345003
|
||||
}
|
||||
},
|
||||
{
|
||||
key: {
|
||||
prevTxId: '3d7d5d98df753ef2a4f82438513c509e3b11f3e738e94a7234967b03a03123a9',
|
||||
prevOutputIndex: 32
|
||||
},
|
||||
value: {
|
||||
txid: '5780f3ee54889a0717152a01abee9a32cec1b0cdf8d5537a08c7bd9eeb6bfbca',
|
||||
inputIndex: 0,
|
||||
timestamp: 1424836934000
|
||||
}
|
||||
},
|
||||
{
|
||||
key: {
|
||||
address: '1Ep5LA4T6Y7zaBPiwruUJurjGFvCJHzJhm',
|
||||
timestamp: 1424836934000,
|
||||
txid: 'e66f3b989c790178de2fc1a5329f94c0d8905d0d3df4e7ecf0115e7f90a6283d',
|
||||
outputIndex: 1
|
||||
},
|
||||
value: {
|
||||
satoshis: 3100000,
|
||||
script: 'OP_DUP OP_HASH160 20 0x9780ccd5356e2acc0ee439ee04e0fe69426c7528 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
blockHeight: 345003
|
||||
}
|
||||
}
|
||||
];
|
||||
var key0 = data[0].key;
|
||||
var value0 = data[0].value;
|
||||
var key3 = data[1].key;
|
||||
var value3 = data[1].value;
|
||||
var key64 = data[2].key;
|
||||
var value64 = data[2].value;
|
||||
|
||||
it('should create the correct operations when updating/adding outputs', function(done) {
|
||||
am.blockHandler({height: 345003, timestamp: new Date(1424836934000)}, true, function(err, operations) {
|
||||
should.not.exist(err);
|
||||
operations.length.should.equal(11);
|
||||
operations[0].type.should.equal('put');
|
||||
var expected0 = ['outs', key0.address, key0.timestamp, key0.txid, key0.outputIndex].join('-');
|
||||
operations[0].key.should.equal(expected0);
|
||||
operations[0].value.should.equal([value0.satoshis, value0.script, value0.blockHeight].join(':'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should create the correct operations when removing outputs', function(done) {
|
||||
am.blockHandler({height: 345003, timestamp: new Date(1424836934000)}, false, function(err, operations) {
|
||||
should.not.exist(err);
|
||||
operations.length.should.equal(11);
|
||||
operations[0].type.should.equal('del');
|
||||
operations[0].key.should.equal(['outs', key0.address, key0.timestamp, key0.txid, key0.outputIndex].join('-'));
|
||||
operations[0].value.should.equal([value0.satoshis, value0.script, value0.blockHeight].join(':'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should continue if output script is null', function(done) {
|
||||
var transactions = [
|
||||
{
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{
|
||||
script: null,
|
||||
satoshis: 1000,
|
||||
}
|
||||
],
|
||||
isCoinbase: sinon.stub().returns(false)
|
||||
}
|
||||
];
|
||||
var db = {
|
||||
getTransactionsFromBlock: function() {
|
||||
return transactions;
|
||||
}
|
||||
};
|
||||
|
||||
var am = new AddressModule({db: db, network: 'livenet'});
|
||||
|
||||
am.blockHandler({height: 345003, timestamp: new Date(1424836934000)}, false, function(err, operations) {
|
||||
should.not.exist(err);
|
||||
operations.length.should.equal(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getBalance', function() {
|
||||
it('should sum up the unspent outputs', function(done) {
|
||||
var am = new AddressModule({});
|
||||
var outputs = [
|
||||
{satoshis: 1000}, {satoshis: 2000}, {satoshis: 3000}
|
||||
];
|
||||
am.getUnspentOutputs = sinon.stub().callsArgWith(2, null, outputs);
|
||||
am.getBalance('1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N', false, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.should.equal(6000);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('will handle error from unspent outputs', function(done) {
|
||||
var am = new AddressModule({});
|
||||
am.getUnspentOutputs = sinon.stub().callsArgWith(2, new Error('error'));
|
||||
am.getBalance('someaddress', false, function(err) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('error');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#getOutputs', function() {
|
||||
var am = new AddressModule({db: {}});
|
||||
var address = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W';
|
||||
|
||||
it('should get outputs for an address', function(done) {
|
||||
var readStream1 = new EventEmitter();
|
||||
am.db.store = {
|
||||
createReadStream: sinon.stub().returns(readStream1)
|
||||
};
|
||||
var mempoolOutputs = [
|
||||
{
|
||||
address: '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W',
|
||||
txid: 'aa2db23f670596e96ed94c405fd11848c8f236d266ee96da37ecd919e53b4371',
|
||||
satoshis: 307627737,
|
||||
script: 'OP_DUP OP_HASH160 f6db95c81dea3d10f0ff8d890927751bf7b203c1 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
blockHeight: 352532
|
||||
}
|
||||
];
|
||||
am.bitcoind = {
|
||||
getMempoolOutputs: sinon.stub().returns(mempoolOutputs)
|
||||
};
|
||||
|
||||
am.getOutputs(address, true, function(err, outputs) {
|
||||
should.not.exist(err);
|
||||
outputs.length.should.equal(3);
|
||||
outputs[0].address.should.equal(address);
|
||||
outputs[0].txid.should.equal('125dd0e50fc732d67c37b6c56be7f9dc00b6859cebf982ee2cc83ed2d604bf87');
|
||||
outputs[0].outputIndex.should.equal(1);
|
||||
outputs[0].satoshis.should.equal(4527773864);
|
||||
outputs[0].script.should.equal('OP_DUP OP_HASH160 038a213afdfc551fc658e9a2a58a86e98d69b687 OP_EQUALVERIFY OP_CHECKSIG');
|
||||
outputs[0].blockHeight.should.equal(345000);
|
||||
outputs[1].address.should.equal(address);
|
||||
outputs[1].txid.should.equal('3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7');
|
||||
outputs[1].outputIndex.should.equal(2);
|
||||
outputs[1].satoshis.should.equal(10000);
|
||||
outputs[1].script.should.equal('OP_DUP OP_HASH160 038a213afdfc551fc658e9a2a58a86e98d69b687 OP_EQUALVERIFY OP_CHECKSIG');
|
||||
outputs[1].blockHeight.should.equal(345004);
|
||||
outputs[2].address.should.equal(address);
|
||||
outputs[2].txid.should.equal('aa2db23f670596e96ed94c405fd11848c8f236d266ee96da37ecd919e53b4371');
|
||||
outputs[2].script.should.equal('OP_DUP OP_HASH160 f6db95c81dea3d10f0ff8d890927751bf7b203c1 OP_EQUALVERIFY OP_CHECKSIG');
|
||||
outputs[2].blockHeight.should.equal(352532);
|
||||
done();
|
||||
});
|
||||
|
||||
var data1 = {
|
||||
key: ['outs', address, '1424835319000', '125dd0e50fc732d67c37b6c56be7f9dc00b6859cebf982ee2cc83ed2d604bf87', '1'].join('-'),
|
||||
value: ['4527773864', 'OP_DUP OP_HASH160 038a213afdfc551fc658e9a2a58a86e98d69b687 OP_EQUALVERIFY OP_CHECKSIG', '345000'].join(':')
|
||||
};
|
||||
|
||||
var data2 = {
|
||||
key: ['outs', address, '1424837300000', '3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7', '2'].join('-'),
|
||||
value: ['10000', 'OP_DUP OP_HASH160 038a213afdfc551fc658e9a2a58a86e98d69b687 OP_EQUALVERIFY OP_CHECKSIG', '345004'].join(':')
|
||||
};
|
||||
|
||||
readStream1.emit('data', data1);
|
||||
readStream1.emit('data', data2);
|
||||
readStream1.emit('close');
|
||||
});
|
||||
|
||||
it('should give an error if the readstream has an error', function(done) {
|
||||
var readStream2 = new EventEmitter();
|
||||
am.db.store = {
|
||||
createReadStream: sinon.stub().returns(readStream2)
|
||||
};
|
||||
|
||||
am.getOutputs(address, true, function(err, outputs) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('readstreamerror');
|
||||
done();
|
||||
});
|
||||
|
||||
readStream2.emit('error', new Error('readstreamerror'));
|
||||
process.nextTick(function() {
|
||||
readStream2.emit('close');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getUnspentOutputs', function() {
|
||||
it('should filter out spent outputs', function(done) {
|
||||
var outputs = [
|
||||
{
|
||||
satoshis: 1000,
|
||||
spent: false,
|
||||
},
|
||||
{
|
||||
satoshis: 2000,
|
||||
spent: true
|
||||
},
|
||||
{
|
||||
satoshis: 3000,
|
||||
spent: false
|
||||
}
|
||||
];
|
||||
var i = 0;
|
||||
|
||||
var am = new AddressModule({});
|
||||
am.getOutputs = sinon.stub().callsArgWith(2, null, outputs);
|
||||
am.isUnspent = function(output, queryMempool, callback) {
|
||||
callback(!outputs[i].spent);
|
||||
i++;
|
||||
};
|
||||
|
||||
am.getUnspentOutputs('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) {
|
||||
should.not.exist(err);
|
||||
outputs.length.should.equal(2);
|
||||
outputs[0].satoshis.should.equal(1000);
|
||||
outputs[1].satoshis.should.equal(3000);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should handle an error from getOutputs', function(done) {
|
||||
var am = new AddressModule({});
|
||||
am.getOutputs = sinon.stub().callsArgWith(2, new Error('error'));
|
||||
am.getUnspentOutputs('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('error');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should handle when there are no outputs', function(done) {
|
||||
var am = new AddressModule({});
|
||||
am.getOutputs = sinon.stub().callsArgWith(2, null, []);
|
||||
am.getUnspentOutputs('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) {
|
||||
should.exist(err);
|
||||
err.should.be.instanceof(errors.NoOutputs);
|
||||
outputs.length.should.equal(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isUnspent', function() {
|
||||
var am = new AddressModule({});
|
||||
|
||||
it('should give true when isSpent() gives false', function(done) {
|
||||
am.isSpent = sinon.stub().callsArgWith(2, false);
|
||||
am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(unspent) {
|
||||
unspent.should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should give false when isSpent() gives true', function(done) {
|
||||
am.isSpent = sinon.stub().callsArgWith(2, true);
|
||||
am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(unspent) {
|
||||
unspent.should.equal(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should give false when isSpent() returns an error', function(done) {
|
||||
am.isSpent = sinon.stub().callsArgWith(2, new Error('error'));
|
||||
am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(unspent) {
|
||||
unspent.should.equal(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isSpent', function() {
|
||||
var am = new AddressModule({});
|
||||
am.bitcoind = {
|
||||
isSpent: sinon.stub().returns(true)
|
||||
};
|
||||
|
||||
it('should give true if bitcoind.isSpent gives true', function(done) {
|
||||
am.isSpent('output', true, function(spent) {
|
||||
spent.should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user