@copperjs/copper
Version:
A lightweight chromium grid
241 lines • 11.5 kB
JavaScript
;
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