UNPKG

@copperjs/copper

Version:
241 lines 11.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const chai = require("chai"); const uuid = require("uuid"); const sinon = require("sinon"); const sinonChai = require("sinon-chai"); const proxyquire = require("proxyquire"); const chaiAsPromised = require("chai-as-promised"); const chaiShallowDeepEqual = require("chai-shallow-deep-equal"); const chai_1 = require("chai"); const errors_1 = require("../common/errors"); const config_1 = require("../standalone/config"); const utils_1 = require("../common/utils"); chai.use(sinonChai); chai.use(chaiAsPromised); chai.use(chaiShallowDeepEqual); const nodeHost = '127'; const nodePort = '127'; const nodeConfig = { hubHost: '', hubPort: 1, nodePolling: 1, port: nodePort, host: nodeHost }; const fetchStub = sinon.stub(); const { Grid, Node } = proxyquire.noCallThru()('./grid', { 'node-fetch': { default: fetchStub } }); describe('node', () => { let node; let checkIsAliveStub; beforeEach(() => { checkIsAliveStub = sinon.stub(Node.prototype, 'checkIsAlive').returns(undefined); node = new Node(Object.assign({}, nodeConfig)); }); afterEach(() => { config_1.copperConfig.reset(); sinon.restore(); fetchStub.reset(); }); it('should set default urlPrefix', () => { config_1.copperConfig.value.routesPrefix = 'abcd'; node = new Node(Object.assign({}, nodeConfig)); chai_1.expect(node.urlPrefix).to.equal('abcd'); }); it('should set default maxSession', () => { chai_1.expect(node.freeSlots).to.equal(Number.POSITIVE_INFINITY); }); it('should set default maxSession when set to number smaller than 1', () => { node = new Node(Object.assign(Object.assign({}, nodeConfig), { maxSession: -1 })); chai_1.expect(node.freeSlots).to.equal(Number.POSITIVE_INFINITY); }); it('should set default nodePolling', () => { node = new Node(Object.assign(Object.assign({}, nodeConfig), { nodePolling: undefined })); chai_1.expect(node.config.nodePolling).to.equal(10000); }); it('should calculate a node id using its host and port', () => { chai_1.expect(Node.getId(nodeConfig)).to.equal(`${nodeHost}:${nodePort}`); chai_1.expect(node.id).to.equal(`${nodeHost}:${nodePort}`); }); it('should calculate node urls', () => { chai_1.expect(node.URL).to.equal(`http://${nodeHost}:${nodePort}`); chai_1.expect(node.webSocketURL).to.equal(`ws://${nodeHost}:${nodePort}`); }); describe('sessions', () => { it('should register session', () => { chai_1.expect(node.getSessions().length).to.equal(0); node.registerSession('1'); chai_1.expect(node.getSessions().length).to.equal(1); }); it('should deregister session', () => { chai_1.expect(node.getSessions().length).to.equal(0); node.registerSession('1'); chai_1.expect(node.getSessions().length).to.equal(1); node.deregisterSession('1'); chai_1.expect(node.getSessions().length).to.equal(0); }); it('should count node free slots', () => { chai_1.expect(node.freeSlots).to.equal(Number.POSITIVE_INFINITY); node.registerSession('1'); chai_1.expect(node.freeSlots).to.equal(Number.POSITIVE_INFINITY - 1); node.deregisterSession('1'); chai_1.expect(node.freeSlots).to.equal(Number.POSITIVE_INFINITY); }); }); describe('checkIsAlive', () => { let processNextTickStub; beforeEach(() => { checkIsAliveStub.restore(); sinon.restore(); processNextTickStub = sinon.stub(process, 'nextTick').callsFake((cb) => cb()); node = new Node(Object.assign(Object.assign({}, nodeConfig), { nodePolling: 1 })); }); afterEach(() => { processNextTickStub.restore(); }); it('should mark node is alive', async () => { fetchStub.returns(undefined); node = new Node(nodeConfig); await utils_1.delay(1); //wait for nextTick chai_1.expect(fetchStub).to.have.been.called; chai_1.expect(node.canCreateSession).to.equal(true); }); it('should mark node as not alive when keep alive fails', async () => { fetchStub.throws(new Error('error')); node = new Node(nodeConfig); await utils_1.delay(1); //wait for nextTick chai_1.expect(node.canCreateSession).to.equal(false); chai_1.expect(fetchStub).to.have.been.called; }); it('should mark node as not alive when deregistring node', async () => { fetchStub.returns(undefined); node = new Node(nodeConfig); await utils_1.delay(1); //wait for nextTick chai_1.expect(node.canCreateSession).to.equal(true); node.deregister(); chai_1.expect(node.canCreateSession).to.equal(false); }); it('should loop using nextTick', async () => { fetchStub.returns(undefined); const stub = sinon.stub(Node.prototype, 'checkIsAlive').callThrough(); node = new Node(Object.assign(Object.assign({}, nodeConfig), { nodePolling: 1 })); chai_1.expect(stub).to.have.been.calledOnce; chai_1.expect(node.shouldCheckIsAlive).to.equal(true); await utils_1.delay(2); node.deregister(); chai_1.expect(stub.callCount).to.be.greaterThanOrEqual(2); chai_1.expect(processNextTickStub.callCount).to.equal(stub.callCount - 1); chai_1.expect(node.shouldCheckIsAlive).to.equal(false); }); }); }); describe('grid', () => { let grid; beforeEach(() => { grid = new Grid(); sinon.stub(Node.prototype, 'checkIsAlive').callsFake(function () { this.isAlive = true; }); fetchStub.resolves({ json: () => { const sessionId = uuid.v4(); return { sessionId, value: { id: sessionId } }; }, }); }); afterEach(() => { sinon.restore(); fetchStub.reset(); }); function assertGridNodesCount(count) { chai_1.expect(grid.nodes.size).to.equal(count); } describe('register node', () => { it('should register node', () => { const node = grid.registerNode(nodeConfig); assertGridNodesCount(1); chai_1.expect(grid.nodes.get(node.id)).to.equal(node); }); }); describe('deregister node', () => { it('should deregister node', () => { grid.registerNode(nodeConfig); assertGridNodesCount(1); grid.deregisterNode(nodeHost, nodePort); assertGridNodesCount(0); }); it('should throw when deregestring unregistered node', () => { chai_1.expect(() => grid.deregisterNode(nodeHost, 2)).to.throw(errors_1.NoMatchingNode); }); it('should mark node as deregisterd for stopping alive checks', () => { const node = grid.registerNode(nodeConfig); const stub = sinon.stub(node, 'deregister').callThrough(); grid.deregisterNode(nodeHost, nodePort); chai_1.expect(stub).to.have.been.calledOnce; }); it('should remove all nodes sessions', async () => { grid.registerNode(nodeConfig).isAlive = true; const stub = sinon.stub(Grid.prototype, '_removeSession').callThrough(); await grid.createSession(); chai_1.expect(grid.listSessions().length).to.equal(1); grid.deregisterNode(nodeHost, nodePort); chai_1.expect(grid.listSessions().length).to.equal(0); chai_1.expect(stub).to.have.been.calledOnce; }); }); describe('sessions', () => { describe('createSession', () => { it('should throw if no nodes to create session on', () => { chai_1.expect(grid.createSession()).to.be.rejectedWith(errors_1.NoMatchingNode); }); it('should create session on node and register it', async () => { grid.registerNode(nodeConfig); await grid.createSession(); chai_1.expect(grid.listSessions().length).to.equal(1); chai_1.expect(fetchStub).to.have.been.calledOnce; }); it('should create session on node with biggest number of free slots', async () => { const mediumNode = grid.registerNode(Object.assign(Object.assign({}, nodeConfig), { port: 3, maxSession: 7 })); const bigNode = grid.registerNode(Object.assign(Object.assign({}, nodeConfig), { maxSession: 10 })); const smallNode = grid.registerNode(Object.assign(Object.assign({}, nodeConfig), { port: 2, maxSession: 5 })); const smallNodeRegister = sinon.stub(smallNode, 'registerSession').callThrough(); const mediumNodeRegister = sinon.stub(mediumNode, 'registerSession').callThrough(); const bigNodeRegister = sinon.stub(bigNode, 'registerSession').callThrough(); await grid.createSession(); chai_1.expect(smallNodeRegister).not.to.have.been.called; chai_1.expect(mediumNodeRegister).not.to.have.been.called; chai_1.expect(bigNodeRegister).to.have.been.calledOnce; }); it('should pass create session options to node', async () => { grid.registerNode(nodeConfig); await grid.createSession({ foo: 'bar' }); chai_1.expect(fetchStub).to.have.been.calledOnce; chai_1.expect(fetchStub.getCall(0).args[1].body).to.equal(JSON.stringify({ foo: 'bar' })); }); }); it('should get node by session id', async () => { const node = grid.registerNode(nodeConfig); const session = await grid.createSession(); chai_1.expect(grid.getNode(session.id)).to.equal(node); }); it('getNode should throw when no matching session', () => { chai_1.expect(() => grid.getNode(uuid.v4())).to.throw(errors_1.SessionNotFound); }); it('should get session by session id', async () => { grid.registerNode(nodeConfig); const session = await grid.createSession(); chai_1.expect(grid.getSession(session.id)).to.shallowDeepEqual(session); }); it('getSession should throw when no matching session', () => { chai_1.expect(() => grid.getSession(uuid.v4())).to.throw(errors_1.SessionNotFound); }); it('should get a session websocket url', async () => { grid.registerNode(nodeConfig); const session = await grid.createSession(); chai_1.expect(grid.getWebSocketUrl(session.id)).to.equal(`ws://${nodeHost}:${nodePort}/ws/${session.id}`); }); it('should remove session from node', async () => { const node = grid.registerNode(nodeConfig); const stub = sinon.stub(node, 'deregisterSession').callThrough(); const session = await grid.createSession(); await grid.removeSession(session.id); chai_1.expect(stub).to.have.been.calledOnce; chai_1.expect(fetchStub).to.have.been.calledTwice; }); }); }); //# sourceMappingURL=grid.test.js.map