diff --git a/test/common/helpers.js b/test/common/helpers.js new file mode 100644 index 00000000000..a609d6d3a73 --- /dev/null +++ b/test/common/helpers.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = { + + randomInt: function (min, max) { + min = Math.ceil(min); + max = Math.floor(max); + + return Math.floor(Math.random() * (max - min + 1)) + min; + } +}; diff --git a/test/common/modulesLoader.js b/test/common/modulesLoader.js index bf659f5cfd9..f9ae6b59491 100644 --- a/test/common/modulesLoader.js +++ b/test/common/modulesLoader.js @@ -133,7 +133,7 @@ var modulesLoader = new function () { * @param {function} cb */ this.initModules = function (modules, logic, scope, cb) { - scope = _.assign({}, this.scope, scope); + scope = _.merge({}, this.scope, scope); async.waterfall([ function (waterCb) { async.reduce(logic, {}, function (memo, logicObj, mapCb) { @@ -145,7 +145,7 @@ var modulesLoader = new function () { }.bind(this), waterCb); }.bind(this), function (logic, waterCb) { - scope = _.assign({}, this.scope, scope, {logic: logic}); + scope = _.merge({}, this.scope, scope, {logic: logic}); async.reduce(modules, {}, function (memo, moduleObj, mapCb) { var name = _.keys(moduleObj)[0]; return this.initModule(moduleObj[name], scope, function (err, module) { diff --git a/test/common/objectStubs.js b/test/common/objectStubs.js index c5e29fd0669..9cb70e87fd6 100644 --- a/test/common/objectStubs.js +++ b/test/common/objectStubs.js @@ -1,19 +1,38 @@ 'use strict'; -var sinon = require('sinon'); +var randomstring = require('randomstring'); +var _ = require('lodash'); -var randomPeer = { - broadhash: '198f2b61a8eb95fbeed58b8216780b68f697f26b849acf00c8c93bb9b24f783d', - height: 1, - ip: '40.40.40.40', - os: 'unknown', - port: 5000, - httpPort: 4000, - state: 2, - version: '0.0.0', - nonce: 'randomnonce' +var randomNormalizedPeer = { + 'broadhash': '198f2b61a8eb95fbeed58b8216780b68f697f26b849acf00c8c93bb9b24f783d', + 'height': 1, + 'ip': '40.40.40.40', + 'os': 'unknown', + 'port': 5000, + 'httpPort': 4000, + 'state': 2, + 'version': '0.0.0', + 'nonce': randomstring.generate(16) }; +function generateRandomActivePeer () { + var randomDigits = function (length) { + return randomstring.generate({charset: 'numeric', length: length}); + }; + return { + 'broadhash': randomstring.generate(64), + 'dappid': null, + 'height': randomDigits(4), + 'ip': randomDigits(3) + '.' + randomDigits(3) + '.' + randomDigits(3) + '.' + randomDigits(3), + 'os': randomstring.generate(10), + 'port': randomstring.generate({charset: 'numeric', length: 4}), + 'state': 2, + 'nonce': randomstring.generate(16), + 'version': randomDigits(1) + '.' + randomDigits(1) + '.' + randomDigits(1) + }; +} + module.exports = { - randomPeer: randomPeer + randomNormalizedPeer: randomNormalizedPeer, + generateRandomActivePeer: generateRandomActivePeer }; diff --git a/test/functional/ws/transport.js b/test/functional/ws/transport.js index cc59c47cae2..03cd9f7d71b 100644 --- a/test/functional/ws/transport.js +++ b/test/functional/ws/transport.js @@ -6,7 +6,7 @@ var WAMPClient = require('wamp-socket-cluster/WAMPClient'); var scClient = require('socketcluster-client'); var node = require('../../node'); -var randomPeer = require('../../common/objectStubs').randomPeer; +var randomPeer = require('../../common/objectStubs').randomNormalizedPeer; var Rules = require('../../../api/ws/workers/rules'); var testConfig = require('../../config.json'); var wsServer = require('../../common/wsServer'); diff --git a/test/unit/api/ws/workers/peersUpdateRules.js b/test/unit/api/ws/workers/peersUpdateRules.js index 09a0d39c20c..3c82f7d3382 100644 --- a/test/unit/api/ws/workers/peersUpdateRules.js +++ b/test/unit/api/ws/workers/peersUpdateRules.js @@ -6,7 +6,7 @@ var expect = require('chai').expect; var sinon = require('sinon'); var failureCodes = require('../../../../../api/ws/rpc/failureCodes'); var PeerUpdateError = require('../../../../../api/ws/rpc/failureCodes').PeerUpdateError; -var randomPeer = require('../../../../common/objectStubs').randomPeer; +var randomPeer = require('../../../../common/objectStubs').randomNormalizedPeer; var connectionsTable = require('../../../../../api/ws/workers/connectionsTable'); var PeersUpdateRules = require('../../../../../api/ws/workers/peersUpdateRules'); var Rules = require('../../../../../api/ws/workers/rules'); diff --git a/test/unit/common/helpers/peers.js b/test/unit/common/helpers/peers.js new file mode 100644 index 00000000000..9d1c738575e --- /dev/null +++ b/test/unit/common/helpers/peers.js @@ -0,0 +1,25 @@ +'use strict'; + +var _ = require('lodash'); +var randomstring = require('randomstring'); + +module.exports = { + + generateMatchedAndUnmatchedBroadhashes: function (unmatchedAmount) { + var characterNotPresentInValidBroadhash = '@'; + var validBroadhash = randomstring.generate({ + length: 64, + custom: 'abcdefghijklmnopqrstuvwxyz0123456789!$&_.' + }); + return _.range(unmatchedAmount).reduce(function (result) { + result.unmatchedBroadhashes.push(randomstring.generate({ + length: 63, + custom: 'abcdefghijklmnopqrstuvwxyz0123456789!$&_.' + }) + characterNotPresentInValidBroadhash); + return result; + }, { + matchedBroadhash: validBroadhash, + unmatchedBroadhashes: [] + }); + } +}; diff --git a/test/unit/helpers/peersManager.js b/test/unit/helpers/peersManager.js index 2ff06bd9c98..9426bbbe1e4 100644 --- a/test/unit/helpers/peersManager.js +++ b/test/unit/helpers/peersManager.js @@ -4,7 +4,7 @@ var _ = require('lodash'); var chai = require('chai'); var expect = require('chai').expect; var sinon = require('sinon'); -var randomPeer = require('../../common/objectStubs').randomPeer; +var randomPeer = require('../../common/objectStubs').randomNormalizedPeer; var Peer = require('../../../logic/peer'); var peersManager = require('../../../helpers/peersManager'); diff --git a/test/unit/logic/peer.js b/test/unit/logic/peer.js index 624225e6256..8baa45f92f8 100644 --- a/test/unit/logic/peer.js +++ b/test/unit/logic/peer.js @@ -7,7 +7,7 @@ var express = require('express'); var ip = require('ip'); var _ = require('lodash'); var sinon = require('sinon'); -var randomPeer = require('../../common/objectStubs').randomPeer; +var randomPeer = require('../../common/objectStubs').randomNormalizedPeer; var Peer = require('../../../logic/peer.js'); describe('peer', function () { diff --git a/test/unit/logic/peers.js b/test/unit/logic/peers.js index f3d75d2e239..95651bec000 100644 --- a/test/unit/logic/peers.js +++ b/test/unit/logic/peers.js @@ -8,7 +8,7 @@ var sinon = require('sinon'); var failureCodes = require('../../../api/ws/rpc/failureCodes'); var modulesLoader = require('../../common/modulesLoader'); -var randomPeer = require('../../common/objectStubs').randomPeer; +var randomPeer = require('../../common/objectStubs').randomNormalizedPeer; var Peers = require('../../../logic/peers.js'); var Peer = require('../../../logic/peer.js'); diff --git a/test/unit/modules/peers.js b/test/unit/modules/peers.js index a6e5d193c11..dff21758640 100644 --- a/test/unit/modules/peers.js +++ b/test/unit/modules/peers.js @@ -1,220 +1,631 @@ 'use strict'; -var _ = require('lodash'); -var chai = require('chai'); +var _ = require('lodash'); var expect = require('chai').expect; -var express = require('express'); +var rewire = require('rewire'); var randomstring = require('randomstring'); var sinon = require('sinon'); -var MasterWAMPServer = require('wamp-socket-cluster/MasterWAMPServer'); -var config = require('../../config.json'); -var Peer = require('../../../logic/peer'); -var PeersLogic = require('../../../logic/peers'); -var PeersModule = require('../../../modules/peers'); +var constants = require('../../../helpers/constants'); +var generateMatchedAndUnmatchedBroadhashes = require('../common/helpers/peers').generateMatchedAndUnmatchedBroadhashes; +var generateRandomActivePeer = require('../../common/objectStubs').generateRandomActivePeer; var modulesLoader = require('../../common/modulesLoader'); -var randomPeer = require('../../common/objectStubs').randomPeer; -var wsRPC = require('../../../api/ws/rpc/wsRPC').wsRPC; +var randomInt = require('../../common/helpers').randomInt; +var randomPeer = require('../../common/objectStubs').randomNormalizedPeer; describe('peers', function () { + var dbMock; var peers; - var systemNonce; + var PeersRewired; + var modules; - function getPeers (cb) { - peers.list({normalized: false}, function (err, __peers) { - expect(err).to.not.exist; - expect(__peers).to.be.an('array'); - return cb(err, __peers); - }); - } + var peersLogicMock; + var systemModuleMock; + var transportModuleMock; - function removeAll (done) { - peers.list({normalized: false}, function (err, __peers) { - __peers.forEach(function (peer) { - peers.remove(peer); - }); - getPeers(function (err, __peers) { - expect(__peers).to.have.a.lengthOf(0); - done(); - }); - }); - } + var NONCE = randomstring.generate(16); + + before(function () { + dbMock = { + any: sinon.stub().resolves() + }; + PeersRewired = rewire('../../../modules/peers'); + peersLogicMock = { + create: sinon.spy(), + exists: sinon.stub(), + get: sinon.stub(), + list: sinon.stub(), + upsert: sinon.stub(), + remove: sinon.stub() + }; + systemModuleMock = {}; + transportModuleMock = {}; + modules = { + system: systemModuleMock, + transport: transportModuleMock + }; + }); before(function (done) { + modulesLoader.scope.nonce = NONCE; + new PeersRewired(function (err, peersModule) { + peers = peersModule; + peers.onBind(modules); + done(); + }, _.assign({}, modulesLoader.scope, {logic: {peers: peersLogicMock}, db: dbMock})); + }); - process.env['NODE_ENV'] = 'TEST'; - systemNonce = randomstring.generate(16); + describe('list', function () { - var systemModuleMock = { - getNonce: sinon.stub().returns(systemNonce), - getBroadhash: sinon.stub().returns(config.nethash) - }; + var listError; + var listResult; + var validOptions; + var randomPeers; - new PeersLogic(modulesLoader.scope.logger, function (err, __peersLogic) { - modulesLoader.scope.logic = { - peers: __peersLogic - }; - new PeersModule(function (err, __peers) { - peers = __peers; - peers.onBind({system: systemModuleMock}); - __peersLogic.bindModules({peers: peers}); + before(function () { + validOptions = {}; + // Set TEST variable in case public ip address gets generated + process.env['NODE_ENV'] = 'TEST'; + }); + + after(function () { + process.env['NODE_ENV'] = ''; + }); + + beforeEach(function (done) { + peers.list(validOptions, function (err, peersResult) { + listError = err; + listResult = peersResult; done(); - }, modulesLoader.scope); + }); }); - }); - beforeEach(function (done) { - removeAll(done); - }); + describe('when logic.peers.list returns no records', function () { - describe('update', function () { + before(function () { + systemModuleMock.getBroadhash = sinon.stub().returns(); + peersLogicMock.list = sinon.stub().returns([]); + }); - it('should insert new peer', function (done) { - peers.update(randomPeer); - getPeers(function (err, __peers) { - expect(__peers).to.be.an('array').and.to.have.lengthOf(1); - expect(__peers[0]).to.have.property('string').equal(randomPeer.ip + ':' + randomPeer.port); - done(); + it('should return an empty array', function () { + expect(listResult).to.be.an('array').and.to.be.empty; }); }); - it('should insert new peer with only ip, port and state', function (done) { + describe('when logic.peers.list returns 1000 random connected peers', function () { - var ipAndPortPeer = { - ip: '40.40.40.43', - port: 4000, - state: 2 - }; + before(function () { + randomPeers = _.range(1000).map(function () { + return generateRandomActivePeer(); + }); + peersLogicMock.list = sinon.stub().returns(randomPeers); + }); - peers.update(ipAndPortPeer); + it('should return all 1000 peers', function () { + expect(listResult).be.an('array').and.have.lengthOf(100); + }); - getPeers(function (err, __peers) { - expect(__peers).to.be.an('array').and.to.have.lengthOf(1); - expect(__peers[0]).to.have.property('string').equal(ipAndPortPeer.ip + ':' + ipAndPortPeer.port); - done(); + describe('options.limit', function () { + + describe('when options.limit < 1000', function () { + + var validLimit; + + before(function () { + validLimit = randomInt(1, (1000 - 1)); + validOptions.limit = validLimit; + }); + + after(function () { + delete validOptions.limit; + }); + + it('should return up to [options.limit] results', function () { + expect(listResult).be.an('array').and.have.lengthOf(validLimit); + }); + }); + + describe('when no options.limit passed', function () { + + it('should return [constants.maxPeers] results', function () { + expect(listResult).be.an('array').and.have.lengthOf(constants.maxPeers); + }); + }); + }); + + describe('options.broadhash', function () { + + describe('when 250 peers matching and 750 not matching broadhash', function () { + + var validBroadhash; + var validLimit; + + before(function () { + // Ensure that different than checking broadhashes will be generated + var broadhashes = generateMatchedAndUnmatchedBroadhashes(750); + validBroadhash = broadhashes.matchedBroadhash; + validOptions.broadhash = validBroadhash; + // 250 peers matching broadhash, next 750 with different one + _.range(1000).forEach(function (i) { + randomPeers[i].broadhash = i < 250 ? validBroadhash : broadhashes.unmatchedBroadhashes[i]; + }); + }); + + after(function () { + delete validOptions.broadhash; + delete validOptions.limit; + }); + + describe('when options.limit = 100', function () { + + before(function () { + validLimit = 100; + validOptions.limit = validLimit; + }); + + it('should return 100 results', function () { + expect(listResult).be.an('array').and.have.lengthOf(100); + }); + + it('should return 100 results with the same broadhash', function () { + expect(listResult.filter(function (peer) { + return peer.broadhash === validBroadhash; + })).be.an('array').and.have.lengthOf(100); + }); + }); + + describe('when options.limit = 500', function () { + + before(function () { + validLimit = 500; + validOptions.limit = validLimit; + }); + + it('should return 500 results', function () { + expect(listResult).be.an('array').and.have.lengthOf(500); + }); + + it('should return 250 results with the same broadhash', function () { + expect(listResult.filter(function (peer) { + return peer.broadhash === validBroadhash; + })).be.an('array').and.have.lengthOf(250); + }); + + it('should return 250 results with different broadhash', function () { + expect(listResult.filter(function (peer) { + return peer.broadhash !== validBroadhash; + })).be.an('array').and.have.lengthOf(250); + }); + + describe('options.attempt', function () { + + after(function () { + delete validOptions.attempt; + }); + + describe('when options.attempt = 0', function () { + + before(function () { + validOptions.attempt = 0; + }); + + it('should return 250 results', function () { + expect(listResult).to.have.lengthOf(250); + }); + + it('should return only peers matching broadhash', function () { + listResult.forEach(function (peer) { + expect(peer.broadhash).eql(validBroadhash); + }); + }); + }); + + describe('when options.attempt = 1', function () { + + before(function () { + validOptions.attempt = 1; + }); + + it('should return 500 results', function () { + expect(listResult).to.have.lengthOf(500); + }); + + it('should return only peers not matching broadhash', function () { + listResult.forEach(function (peer) { + expect(peer.broadhash).not.eql(validBroadhash); + }); + }); + }); + }); + }); + }); + + describe('when no options.limit passed', function () { + + it('should return [constants.maxPeers] results', function () { + expect(listResult).be.an('array').and.have.lengthOf(constants.maxPeers); + }); + }); }); }); + describe('when logic.peers.list returns 1000 random state peers and limit = 1000', function () { - it('should update existing peer', function (done) { + describe('options.allowedStates', function () { - peers.update(randomPeer); + var CONNECTED_STATE = 2; + var BANNED_STATE = 1; + var DISCONNECTED_STATE = 0; + + before(function () { + validOptions.limit = 1000; + randomPeers = _.range(1000).map(function () { + var peer = generateRandomActivePeer(); + peer.state = randomInt(DISCONNECTED_STATE, CONNECTED_STATE); + return peer; + }); + peersLogicMock.list = sinon.stub().returns(randomPeers); + }); + + after(function () { + delete validOptions.limit; + }); + + it('should return only connected peers', function () { + expect(_.uniqBy(listResult, 'state')).be.an('array').and.have.lengthOf(1); + expect(listResult[0].state).equal(CONNECTED_STATE); + }); - getPeers(function (err, __peers) { - expect(__peers[0]).to.have.property('height').equal(randomPeer.height); - var toUpdate = _.clone(randomPeer); - toUpdate.height += 1; - peers.update(toUpdate); - getPeers(function (err, __peers) { - expect(__peers[0]).to.have.property('height').equal(toUpdate.height); - done(); + describe('when options.allowedStates = [1]', function () { + + before(function () { + validOptions.allowedStates = [1]; + }); + + after(function () { + delete validOptions.allowedStates; + }); + + it('should return only banned peers', function () { + expect(_.uniqBy(listResult, 'state')).be.an('array').and.have.lengthOf(1); + expect(listResult[0].state).equal(BANNED_STATE); + }); + }); + + describe('when options.allowedStates = [0]', function () { + + before(function () { + validOptions.allowedStates = [0]; + }); + + after(function () { + delete validOptions.allowedStates; + }); + + it('should return only disconnected peers', function () { + expect(_.uniqBy(listResult, 'state')).be.an('array').and.have.lengthOf(1); + expect(listResult[0].state).equal(DISCONNECTED_STATE); + }); + }); + + describe('when options.allowedStates = [0, 1]', function () { + + before(function () { + validOptions.allowedStates = [0, 1]; + }); + + after(function () { + delete validOptions.allowedStates; + }); + + it('should return disconnected and banned peers', function () { + expect(_.uniqBy(listResult, 'state')).be.an('array').and.have.length.at.least(1); + listResult.forEach(function (state) { + expect(state).not.to.equal(CONNECTED_STATE); + }); + }); }); }); + }); + }); + + describe('update', function () { + + var validPeer; + var updateResult; + var validUpsertResult; + + before(function () { + validUpsertResult = true; + validPeer = generateRandomActivePeer(); + }); + beforeEach(function () { + peersLogicMock.upsert = sinon.stub().returns(validUpsertResult); + updateResult = peers.update(validPeer); }); - it('should not insert new peer if address changed but nonce is the same', function (done) { + it('should call logic.peers.upsert', function () { + expect(peersLogicMock.upsert.calledOnce).to.be.true; + }); - peers.update(randomPeer); + it('should call logic.peers.upsert with peer', function () { + expect(peersLogicMock.upsert.calledWith(validPeer)).to.be.true; + }); - getPeers(function (err, __peers) { - expect(__peers[0]).to.have.property('string').equal(randomPeer.ip + ':' + randomPeer.port); - var toUpdate = _.clone(randomPeer); - toUpdate.port += 1; - peers.update(toUpdate); - getPeers(function (err, __peers) { - expect(__peers[0]).to.have.property('string').equal(randomPeer.ip + ':' + randomPeer.port); - done(); - }); + it('should return library.logic.peers.upsert result', function () { + expect(updateResult).equal(validUpsertResult); + }); + }); + + describe('remove', function () { + + var validPeer; + var removeResult; + var validLogicRemoveResult; + + before(function () { + validLogicRemoveResult = true; + validPeer = generateRandomActivePeer(); + }); + + beforeEach(function () { + peersLogicMock.remove = sinon.stub().returns(validLogicRemoveResult); + removeResult = peers.remove(validPeer); + }); + + describe('when removable peer is frozen', function () { + + var originalFrozenPeersList; + var loggerDebugSpy; + + before(function () { + originalFrozenPeersList = _.assign({}, modulesLoader.scope.config.peers.list); + modulesLoader.scope.config.peers.list = [{ + ip: validPeer.ip, + port: validPeer.port + }]; + loggerDebugSpy = sinon.spy(modulesLoader.scope.logger, 'debug'); + }); + + after(function () { + modulesLoader.scope.config.peers.list = originalFrozenPeersList; + loggerDebugSpy.restore(); + }); + + it('should not call logic.peers.remove', function () { + expect(peersLogicMock.remove.called).to.be.false; }); + it('should call logger.debug with message = "Cannot remove frozen peer"', function () { + expect(loggerDebugSpy.calledWith('Cannot remove frozen peer')).to.be.true; + }); + + it('should call logger.debug with message = [ip:port]', function () { + expect(loggerDebugSpy.args[0][1]).eql(validPeer.ip + ':' + validPeer.port); + }); }); - it('should insert new peer if address and nonce changed', function (done) { + describe('when removable peer is not frozen', function () { - peers.update(randomPeer); + it('should call logic.peers.remove', function () { + expect(peersLogicMock.remove.calledOnce).to.be.true; + }); - getPeers(function (err, __peers) { - expect(__peers[0]).to.have.property('string').equal(randomPeer.ip + ':' + randomPeer.port); - var secondPeer = _.clone(randomPeer); - secondPeer.port += 1; - secondPeer.nonce = randomstring.generate(16); - peers.update(secondPeer); - getPeers(function (err, __peers) { - expect(__peers).to.have.a.lengthOf(2); - var peersAddresses = __peers.map(function (p) { - return p.string; - }); - expect(peersAddresses.indexOf(randomPeer.ip + ':' + randomPeer.port) !== -1).to.be.ok; - expect(peersAddresses.indexOf(secondPeer.ip + ':' + secondPeer.port) !== -1).to.be.ok; - done(); - }); + it('should call logic.peers.remove with object containing expected ip', function () { + expect(peersLogicMock.remove.calledWith(sinon.match({ip: validPeer.ip}))).to.be.true; + }); + + it('should call logic.peers.remove with object containing expected port', function () { + expect(peersLogicMock.remove.calledWith(sinon.match({port: validPeer.port}))).to.be.true; + }); + + it('should return library.logic.peers.remove result', function () { + expect(removeResult).equal(validLogicRemoveResult); }); }); }); - describe('list', function () { + describe('getConsensus', function () { - beforeEach(function (done) { - removeAll(done); + var validActive; + var validMatched; + var getConsensusResult; + var originalForgingForce; + + before(function () { + validActive = null; + validMatched = null; + getConsensusResult = null; + originalForgingForce = PeersRewired.__get__('library.config.forging.force'); + systemModuleMock.getBroadhash = sinon.stub().returns(); + peersLogicMock.list = sinon.stub().returns([]); }); - it('should list empty peers list when no peers were inserted before', function (done) { - peers.list({}, function (err, __peers) { - expect(__peers).to.be.an('array').and.to.have.lengthOf(0); - done(); + after(function () { + PeersRewired.__set__('library.config.forging.force', originalForgingForce); + }); + + beforeEach(function () { + getConsensusResult = peers.getConsensus(validActive, validMatched); + }); + + afterEach(function () { + peersLogicMock.list.resetHistory(); + }); + + describe('when config.forging.force = true', function () { + + before(function () { + PeersRewired.__set__('library.config.forging.force', true); + }); + + it('should return undefined', function () { + expect(getConsensusResult).to.be.undefined; }); }); - describe('when inserted', function () { + describe('when config.forging.force = false', function () { - beforeEach(function () { - peers.update(randomPeer); + before(function () { + PeersRewired.__set__('library.config.forging.force', false); + peersLogicMock.list = sinon.stub().returns([]); }); - it('should list the inserted peer after insert', function (done) { - peers.list({}, function (err, __peers) { - expect(__peers).to.be.an('array').and.to.have.lengthOf(1); - done(); - }); + afterEach(function () { + peersLogicMock.list.resetHistory(); }); - it('should list the inserted peer as Peer instance with normalized parameter set to false', function (done) { - peers.list({normalized: false}, function (err, __peers) { - expect(__peers[0]).to.be.an.instanceof(Peer); - done(); + describe('when active peers not passed', function () { + + it('should call logic.peers.list', function () { + expect(peersLogicMock.list.called).to.be.true; }); - }); - it('should list the inserted peer as object with normalized parameter set to true', function (done) { - peers.list({normalized: true}, function (err, __peers) { - expect(__peers[0]).to.be.an.instanceof(Object); - done(); + it('should call logic.peers.list with true', function () { + expect(peersLogicMock.list.calledWith(true)).to.be.true; + }); + + it('should return consensus as a number', function () { + expect(getConsensusResult).to.be.a('number'); }); }); - it('should list the inserted peer as object without set normalized parameter', function (done) { - peers.list({}, function (err, __peers) { - expect(__peers[0]).to.be.an.instanceof(Object); - done(); + describe('when matched peers not passed and there are 100 active peers', function () { + + var oneHundredActivePeers; + var broadhashes; + + before(function () { + oneHundredActivePeers = _.range(100).map(function () { + return generateRandomActivePeer(); + }); + broadhashes = generateMatchedAndUnmatchedBroadhashes(100); + systemModuleMock.getBroadhash = sinon.stub().returns(broadhashes.matchedBroadhash); + validActive = oneHundredActivePeers; + }); + + afterEach(function () { + peersLogicMock.list.resetHistory(); + }); + + after(function () { + validActive = null; + }); + + describe('when non of active peers matches broadhash', function () { + + before(function () { + oneHundredActivePeers.forEach(function (peer, index) { + peer.broadhash = broadhashes.unmatchedBroadhashes[index]; + }); + }); + + it('should return consensus = 0', function () { + expect(getConsensusResult).to.equal(0); + }); + }); + + describe('when all of active peers matches broadhash', function () { + + before(function () { + oneHundredActivePeers.forEach(function (peer) { + peer.broadhash = broadhashes.matchedBroadhash; + }); + }); + + it('should return consensus = 100', function () { + expect(getConsensusResult).equal(100); + }); + }); + + describe('when half of active peers matches broadhash', function () { + + before(function () { + oneHundredActivePeers.forEach(function (peer, i) { + peer.broadhash = i < 50 ? broadhashes.matchedBroadhash : broadhashes.unmatchedBroadhashes[i]; + }); + }); + + it('should return consensus = 50', function () { + expect(getConsensusResult).equal(50); + }); }); }); - }); - }); - describe('remove', function () { + describe('when called with active and matched arguments', function () { - it('should remove added peer', function (done) { + describe('when there are 10 active and 10 matched peers', function () { - peers.update(randomPeer); + before(function () { + validActive = _.range(10).map(generateRandomActivePeer); + validMatched = _.range(10).map(generateRandomActivePeer); + }); - getPeers(function (err, __peers) { - expect(__peers).to.be.an('array').and.to.have.lengthOf(1); - expect(peers.remove(randomPeer)).to.be.ok; - getPeers(function (err, __peers) { - expect(__peers).to.be.an('array').and.to.have.lengthOf(0); - done(); + it('should return consensus = 100', function () { + expect(getConsensusResult).equal(100); + }); + }); + + describe('when there are [constants.maxPeers] active and [constants.maxPeers] matched peers', function () { + + before(function () { + validActive = _.range(constants.maxPeers).map(generateRandomActivePeer); + validMatched = _.range(constants.maxPeers).map(generateRandomActivePeer); + }); + + it('should return consensus = 100', function () { + expect(getConsensusResult).equal(100); + }); + }); + + describe('when there are [constants.maxPeers] x 10 active and [constants.maxPeers] matched peers', function () { + + before(function () { + validActive = _.range(10 * constants.maxPeers).map(generateRandomActivePeer); + validMatched = _.range(constants.maxPeers).map(generateRandomActivePeer); + }); + + it('should return consensus = 100', function () { + expect(getConsensusResult).equal(100); + }); + }); + + describe('when there are [constants.maxPeers] active and [constants.maxPeers] x 10 matched peers', function () { + + before(function () { + validActive = _.range(constants.maxPeers).map(generateRandomActivePeer); + validMatched = _.range(10 * constants.maxPeers).map(generateRandomActivePeer); + }); + + it('should return consensus = 100', function () { + expect(getConsensusResult).equal(100); + }); + }); + + describe('when there are 50 active and 100 matched peers', function () { + + before(function () { + validActive = _.range(50).map(generateRandomActivePeer); + validMatched = _.range(100).map(generateRandomActivePeer); + }); + + it('should return consensus = 100', function () { + expect(getConsensusResult).equal(100); + }); + }); + + describe('when there are 100 active and 50 matched peers', function () { + + before(function () { + validActive = _.range(100).map(generateRandomActivePeer); + validMatched = _.range(50).map(generateRandomActivePeer); + }); + + it('should return consensus = 50', function () { + expect(getConsensusResult).equal(50); + }); }); }); }); @@ -223,11 +634,10 @@ describe('peers', function () { describe('acceptable', function () { before(function () { + systemModuleMock.getNonce = sinon.stub().returns(NONCE); process.env['NODE_ENV'] = 'DEV'; }); - var ip = require('ip'); - it('should accept peer with public ip', function () { expect(peers.acceptable([randomPeer])).that.is.an('array').and.to.deep.equal([randomPeer]); }); @@ -240,7 +650,7 @@ describe('peers', function () { it('should not accept peer with host\'s nonce', function () { var peer = _.clone(randomPeer); - peer.nonce = systemNonce; + peer.nonce = NONCE; expect(peers.acceptable([peer])).that.is.an('array').and.to.be.empty; }); @@ -249,7 +659,7 @@ describe('peers', function () { var meAsPeer = { ip: '40.00.40.40', port: 4001, - nonce: systemNonce + nonce: NONCE }; expect(peers.acceptable([meAsPeer])).that.is.an('array').and.to.be.empty; }); @@ -259,35 +669,48 @@ describe('peers', function () { }); }); - describe('events', function () { - before(function () { - var testWampServer = new MasterWAMPServer({on: sinon.spy()}, {}); + describe('onBlockchainReady', function () { - var usedRPCEndpoints = { - status: function () {} - }; + var originalPeersList; - sinon.stub(usedRPCEndpoints, 'status').callsArgWith(0, null, { - success: true, - broadhash: '123456789broadhash', - nethash: '123456789nethash' - }); + before(function () { + originalPeersList = PeersRewired.__get__('library.config.peers.list'); + PeersRewired.__set__('library.config.peers.list', []); + peersLogicMock.create = sinon.stub().returnsArg(0); + }); + + after(function () { + PeersRewired.__set__('library.config.peers.list', originalPeersList); + }); - testWampServer.registerRPCEndpoints(usedRPCEndpoints); - wsRPC.setServer(testWampServer); + it('should update peers during onBlockchainReady', function (done) { + sinon.stub(peers, 'discover').callsArgWith(0, null); + var config = require('../../config.json'); + var initialPeers = _.clone(config.peers.list); + if (initialPeers.length === 0) { + config.peers.list.push(randomPeer); + } + peers.onBlockchainReady(); + setTimeout(function () { + expect(peers.discover.calledOnce).to.be.ok; + done(); + }, 100); }); + }); - describe('onPeersReady', function () { + describe('onPeersReady', function () { - it('should update peers during onBlockchainReady', function (done) { - sinon.stub(peers, 'discover').callsArgWith(0, null); - peers.onPeersReady(); - setTimeout(function () { - expect(peers.discover.called).to.be.ok; - peers.discover.restore(); - done(); - }, 500); - }); + before(function () { + peersLogicMock.list = sinon.stub().returns([]); + peers.discover = sinon.stub().callsArgWith(0, null); + }); + + it('should update peers during onBlockchainReady', function (done) { + peers.onPeersReady(); + setTimeout(function () { + expect(peers.discover.calledOnce).to.be.ok; + done(); + }, 100); }); }); });