'use strict'; var should = require('chai').should(); var sinon = require('sinon'); var proxyquire = require('proxyquire'); var fs = require('fs'); var sinon = require('sinon'); var readFileSync = sinon.stub().returns(fs.readFileSync(__dirname + '/../data/bitcoin.conf')); var BitcoinService = proxyquire('../../lib/services/bitcoind', { fs: { readFileSync: readFileSync } }); describe('Bitcoin Service', function() { var baseConfig = { node: { datadir: 'testdir', network: { name: 'regtest' } } }; describe('@constructor', function() { it('will create an instance', function() { var bitcoind = new BitcoinService(baseConfig); should.exist(bitcoind); }); it('will create an instance without `new`', function() { var bitcoind = BitcoinService(baseConfig); should.exist(bitcoind); }); }); describe('@dependencies', function() { it('will have no dependencies', function() { BitcoinService.dependencies.should.deep.equal([]); }); }); describe('#_loadConfiguration', function() { it('will parse a bitcoin.conf file', function() { var TestBitcoin = proxyquire('../../lib/services/bitcoind', { fs: { readFileSync: readFileSync, existsSync: sinon.stub().returns(true) }, mkdirp: { sync: sinon.stub() } }); var bitcoind = new TestBitcoin(baseConfig); bitcoind._loadConfiguration({datadir: process.env.HOME + '/.bitcoin'}); should.exist(bitcoind.configuration); bitcoind.configuration.should.deep.equal({ server: 1, whitelist: '127.0.0.1', txindex: 1, port: 20000, rpcallowip: '127.0.0.1', rpcuser: 'bitcoin', rpcpassword: 'local321' }); }); it('should throw an exception if txindex isn\'t enabled in the configuration', function() { var TestBitcoin = proxyquire('../../lib/services/bitcoind', { fs: { readFileSync: sinon.stub().returns(fs.readFileSync(__dirname + '/../data/badbitcoin.conf')), existsSync: sinon.stub().returns(true), }, mkdirp: { sync: sinon.stub() } }); var bitcoind = new TestBitcoin(baseConfig); (function() { bitcoind._loadConfiguration({datadir: './test'}); }).should.throw('Txindex option'); }); }); describe('#_registerEventHandlers', function() { it('will emit tx with transactions from bindings', function(done) { var transaction = {}; var TestBitcoin = proxyquire('../../lib/services/bitcoind', { fs: { readFileSync: readFileSync }, bindings: function(name) { name.should.equal('bitcoind.node'); return { onTipUpdate: sinon.stub(), startTxMon: sinon.stub().callsArgWith(0, [transaction]) }; } }); var bitcoind = new TestBitcoin(baseConfig); bitcoind.on('tx', function(tx) { tx.should.equal(transaction); done(); }); bitcoind._registerEventHandlers(); }); it('will emit tip from bindings', function(done) { var height = 1; var TestBitcoin = proxyquire('../../lib/services/bitcoind', { fs: { readFileSync: readFileSync }, bindings: function(name) { name.should.equal('bitcoind.node'); return { syncPercentage: function() { return height * 10; }, onTipUpdate: function(callback) { if (height >= 10) { return callback(undefined); } setImmediate(function() { callback(height++); }); }, startTxMon: sinon.stub() }; } }); var bitcoind = new TestBitcoin(baseConfig); var tipCallCount = 0; bitcoind.on('tip', function(height) { should.exist(height); tipCallCount++; if (height === 9) { tipCallCount.should.equal(9); done(); } }); bitcoind._registerEventHandlers(); }); }); describe('#_onReady', function(done) { var genesisBuffer = new Buffer('0100000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000bac8b0fa927c0ac8234287e33c5f74d38d354820e24756ad709d7038fc5f31f020e7494dffff001d03e4b6720101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0420e7494d017f062f503253482fffffffff0100f2052a010000002321021aeaf2f8638a129a3156fbe7e5ef635226b0bafd495ff03afe2c843d7e3a4b51ac00000000', 'hex'); it('will emit ready and set the height and genesisBuffer', function(done) { var TestBitcoin = proxyquire('../../lib/services/bitcoind', { fs: { readFileSync: readFileSync }, bindings: function(name) { name.should.equal('bitcoind.node'); return { onTipUpdate: sinon.stub(), startTxMon: sinon.stub(), getInfo: sinon.stub().returns({ blocks: 101 }), getBlock: sinon.stub().callsArgWith(1, null, genesisBuffer) }; } }); var bitcoind = new TestBitcoin(baseConfig); bitcoind._registerEventHandlers = sinon.stub(); var result = {}; var readyCallCount = 0; bitcoind.on('ready', function() { readyCallCount++; }); bitcoind._onReady(result, function(err) { if (err) { throw err; } bitcoind._registerEventHandlers.callCount.should.equal(1); readyCallCount.should.equal(1); bitcoind.genesisBuffer.should.equal(genesisBuffer); bitcoind.height.should.equal(101); done(); }); }); }); describe('#start', function() { it('call bindings start with the correct arguments', function(done) { var startCallCount = 0; var start = function(obj, cb) { startCallCount++; obj.datadir.should.equal('testdir'); obj.network.should.equal('regtest'); cb(); }; var onBlocksReady = sinon.stub().callsArg(0); var TestBitcoin = proxyquire('../../lib/services/bitcoind', { fs: { readFileSync: readFileSync }, bindings: function(name) { name.should.equal('bitcoind.node'); return { start: start, onBlocksReady: onBlocksReady }; } }); var bitcoind = new TestBitcoin(baseConfig); bitcoind._loadConfiguration = sinon.stub(); bitcoind._onReady = sinon.stub().callsArg(1); bitcoind.start(function(err) { should.not.exist(err); bitcoind._loadConfiguration.callCount.should.equal(1); startCallCount.should.equal(1); onBlocksReady.callCount.should.equal(1); bitcoind._onReady.callCount.should.equal(1); done(); }); }); it('will give an error from bindings.start', function(done) { var start = sinon.stub().callsArgWith(1, new Error('test')); var TestBitcoin = proxyquire('../../lib/services/bitcoind', { fs: { readFileSync: readFileSync }, bindings: function(name) { name.should.equal('bitcoind.node'); return { start: start }; } }); var bitcoind = new TestBitcoin(baseConfig); bitcoind._loadConfiguration = sinon.stub(); bitcoind.start(function(err) { should.exist(err); err.message.should.equal('test'); done(); }); }); it('will give an error from bindings.onBlocksReady', function(done) { var start = sinon.stub().callsArgWith(1, null); var onBlocksReady = sinon.stub().callsArgWith(0, new Error('test')); var TestBitcoin = proxyquire('../../lib/services/bitcoind', { fs: { readFileSync: readFileSync }, bindings: function(name) { name.should.equal('bitcoind.node'); return { start: start, onBlocksReady: onBlocksReady }; } }); var bitcoind = new TestBitcoin(baseConfig); bitcoind._onReady = sinon.stub().callsArg(1); bitcoind._loadConfiguration = sinon.stub(); bitcoind.start(function(err) { should.exist(err); err.message.should.equal('test'); done(); }); }); }); describe('#stop', function() { it('will call bindings stop', function() { var stop = sinon.stub().callsArgWith(0, null, 'status'); var TestBitcoin = proxyquire('../../lib/services/bitcoind', { fs: { readFileSync: readFileSync }, bindings: function(name) { name.should.equal('bitcoind.node'); return { stop: stop }; } }); var bitcoind = new TestBitcoin(baseConfig); bitcoind.stop(function(err, status) { stop.callCount.should.equal(1); should.not.exist(err); }); }); it('will give an error from bindings stop', function() { var stop = sinon.stub().callsArgWith(0, new Error('test')); var TestBitcoin = proxyquire('../../lib/services/bitcoind', { fs: { readFileSync: readFileSync }, bindings: function(name) { name.should.equal('bitcoind.node'); return { stop: stop }; } }); var bitcoind = new TestBitcoin(baseConfig); bitcoind.stop(function(err) { stop.callCount.should.equal(1); should.exist(err); err.message.should.equal('test'); }); }); }); describe('proxy methods', function() { var proxyMethods = [ ['isSynced', 0], ['syncPercentage', 0], ['getBlock', 2], ['isSpent', 2], ['getBlockIndex', 1], ['estimateFee', 1], ['sendTransaction', 2], ['getTransaction', 3], ['getTransactionWithBlockInfo', 3], ['getMempoolOutputs', 1], ['addMempoolUncheckedTransaction', 1], ['getInfo', 0] ]; proxyMethods.forEach(function(x) { it('pass ' + x[1] + ' argument(s) to ' + x[0], function() { var stub = sinon.stub(); var TestBitcoin = proxyquire('../../lib/services/bitcoind', { fs: { readFileSync: readFileSync }, bindings: function(name) { name.should.equal('bitcoind.node'); var methods = {}; methods[x[0]] = stub; return methods; } }); var bitcoind = new TestBitcoin(baseConfig); var args = []; for (var i = 0; i < x[1]; i++) { args.push(i); } bitcoind[x[0]].apply(bitcoind, args); stub.callCount.should.equal(1); stub.args[0].length.should.equal(x[1]); }); }); }); });