mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-15 05:06:30 +00:00
prettier: convert test/unit decaffeinated files to Prettier format
This commit is contained in:
parent
68e2adebf5
commit
3eceb8a5f6
14 changed files with 4344 additions and 3063 deletions
|
@ -9,214 +9,312 @@
|
|||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const chai = require("chai");
|
||||
chai.should();
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
const sinon = require("sinon");
|
||||
const SandboxedModule = require('sandboxed-module');
|
||||
const path = require("path");
|
||||
const modulePath = '../../../app/js/AuthorizationManager';
|
||||
const chai = require('chai')
|
||||
chai.should()
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const path = require('path')
|
||||
const modulePath = '../../../app/js/AuthorizationManager'
|
||||
|
||||
describe('AuthorizationManager', function() {
|
||||
beforeEach(function() {
|
||||
this.client =
|
||||
{ol_context: {}};
|
||||
describe('AuthorizationManager', function () {
|
||||
beforeEach(function () {
|
||||
this.client = { ol_context: {} }
|
||||
|
||||
return this.AuthorizationManager = SandboxedModule.require(modulePath, {requires: {}});});
|
||||
return (this.AuthorizationManager = SandboxedModule.require(modulePath, {
|
||||
requires: {}
|
||||
}))
|
||||
})
|
||||
|
||||
describe("assertClientCanViewProject", function() {
|
||||
it("should allow the readOnly privilegeLevel", function(done) {
|
||||
this.client.ol_context.privilege_level = "readOnly";
|
||||
return this.AuthorizationManager.assertClientCanViewProject(this.client, (error) => {
|
||||
expect(error).to.be.null;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
describe('assertClientCanViewProject', function () {
|
||||
it('should allow the readOnly privilegeLevel', function (done) {
|
||||
this.client.ol_context.privilege_level = 'readOnly'
|
||||
return this.AuthorizationManager.assertClientCanViewProject(
|
||||
this.client,
|
||||
(error) => {
|
||||
expect(error).to.be.null
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should allow the readAndWrite privilegeLevel", function(done) {
|
||||
this.client.ol_context.privilege_level = "readAndWrite";
|
||||
return this.AuthorizationManager.assertClientCanViewProject(this.client, (error) => {
|
||||
expect(error).to.be.null;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should allow the readAndWrite privilegeLevel', function (done) {
|
||||
this.client.ol_context.privilege_level = 'readAndWrite'
|
||||
return this.AuthorizationManager.assertClientCanViewProject(
|
||||
this.client,
|
||||
(error) => {
|
||||
expect(error).to.be.null
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should allow the owner privilegeLevel", function(done) {
|
||||
this.client.ol_context.privilege_level = "owner";
|
||||
return this.AuthorizationManager.assertClientCanViewProject(this.client, (error) => {
|
||||
expect(error).to.be.null;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should allow the owner privilegeLevel', function (done) {
|
||||
this.client.ol_context.privilege_level = 'owner'
|
||||
return this.AuthorizationManager.assertClientCanViewProject(
|
||||
this.client,
|
||||
(error) => {
|
||||
expect(error).to.be.null
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return it("should return an error with any other privilegeLevel", function(done) {
|
||||
this.client.ol_context.privilege_level = "unknown";
|
||||
return this.AuthorizationManager.assertClientCanViewProject(this.client, (error) => {
|
||||
error.message.should.equal("not authorized");
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should return an error with any other privilegeLevel', function (done) {
|
||||
this.client.ol_context.privilege_level = 'unknown'
|
||||
return this.AuthorizationManager.assertClientCanViewProject(
|
||||
this.client,
|
||||
(error) => {
|
||||
error.message.should.equal('not authorized')
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("assertClientCanEditProject", function() {
|
||||
it("should not allow the readOnly privilegeLevel", function(done) {
|
||||
this.client.ol_context.privilege_level = "readOnly";
|
||||
return this.AuthorizationManager.assertClientCanEditProject(this.client, (error) => {
|
||||
error.message.should.equal("not authorized");
|
||||
return done();
|
||||
});
|
||||
});
|
||||
describe('assertClientCanEditProject', function () {
|
||||
it('should not allow the readOnly privilegeLevel', function (done) {
|
||||
this.client.ol_context.privilege_level = 'readOnly'
|
||||
return this.AuthorizationManager.assertClientCanEditProject(
|
||||
this.client,
|
||||
(error) => {
|
||||
error.message.should.equal('not authorized')
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should allow the readAndWrite privilegeLevel", function(done) {
|
||||
this.client.ol_context.privilege_level = "readAndWrite";
|
||||
return this.AuthorizationManager.assertClientCanEditProject(this.client, (error) => {
|
||||
expect(error).to.be.null;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should allow the readAndWrite privilegeLevel', function (done) {
|
||||
this.client.ol_context.privilege_level = 'readAndWrite'
|
||||
return this.AuthorizationManager.assertClientCanEditProject(
|
||||
this.client,
|
||||
(error) => {
|
||||
expect(error).to.be.null
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should allow the owner privilegeLevel", function(done) {
|
||||
this.client.ol_context.privilege_level = "owner";
|
||||
return this.AuthorizationManager.assertClientCanEditProject(this.client, (error) => {
|
||||
expect(error).to.be.null;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should allow the owner privilegeLevel', function (done) {
|
||||
this.client.ol_context.privilege_level = 'owner'
|
||||
return this.AuthorizationManager.assertClientCanEditProject(
|
||||
this.client,
|
||||
(error) => {
|
||||
expect(error).to.be.null
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return it("should return an error with any other privilegeLevel", function(done) {
|
||||
this.client.ol_context.privilege_level = "unknown";
|
||||
return this.AuthorizationManager.assertClientCanEditProject(this.client, (error) => {
|
||||
error.message.should.equal("not authorized");
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should return an error with any other privilegeLevel', function (done) {
|
||||
this.client.ol_context.privilege_level = 'unknown'
|
||||
return this.AuthorizationManager.assertClientCanEditProject(
|
||||
this.client,
|
||||
(error) => {
|
||||
error.message.should.equal('not authorized')
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// check doc access for project
|
||||
// check doc access for project
|
||||
|
||||
describe("assertClientCanViewProjectAndDoc", function() {
|
||||
beforeEach(function() {
|
||||
this.doc_id = "12345";
|
||||
this.callback = sinon.stub();
|
||||
return this.client.ol_context = {};});
|
||||
describe('assertClientCanViewProjectAndDoc', function () {
|
||||
beforeEach(function () {
|
||||
this.doc_id = '12345'
|
||||
this.callback = sinon.stub()
|
||||
return (this.client.ol_context = {})
|
||||
})
|
||||
|
||||
describe("when not authorised at the project level", function() {
|
||||
beforeEach(function() {
|
||||
return this.client.ol_context.privilege_level = "unknown";
|
||||
});
|
||||
describe('when not authorised at the project level', function () {
|
||||
beforeEach(function () {
|
||||
return (this.client.ol_context.privilege_level = 'unknown')
|
||||
})
|
||||
|
||||
it("should not allow access", function() {
|
||||
return this.AuthorizationManager.assertClientCanViewProjectAndDoc(this.client, this.doc_id, err => err.message.should.equal("not authorized"));
|
||||
});
|
||||
it('should not allow access', function () {
|
||||
return this.AuthorizationManager.assertClientCanViewProjectAndDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
(err) => err.message.should.equal('not authorized')
|
||||
)
|
||||
})
|
||||
|
||||
return describe("even when authorised at the doc level", function() {
|
||||
beforeEach(function(done) {
|
||||
return this.AuthorizationManager.addAccessToDoc(this.client, this.doc_id, done);
|
||||
});
|
||||
return describe('even when authorised at the doc level', function () {
|
||||
beforeEach(function (done) {
|
||||
return this.AuthorizationManager.addAccessToDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
done
|
||||
)
|
||||
})
|
||||
|
||||
return it("should not allow access", function() {
|
||||
return this.AuthorizationManager.assertClientCanViewProjectAndDoc(this.client, this.doc_id, err => err.message.should.equal("not authorized"));
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should not allow access', function () {
|
||||
return this.AuthorizationManager.assertClientCanViewProjectAndDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
(err) => err.message.should.equal('not authorized')
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return describe("when authorised at the project level", function() {
|
||||
beforeEach(function() {
|
||||
return this.client.ol_context.privilege_level = "readOnly";
|
||||
});
|
||||
return describe('when authorised at the project level', function () {
|
||||
beforeEach(function () {
|
||||
return (this.client.ol_context.privilege_level = 'readOnly')
|
||||
})
|
||||
|
||||
describe("and not authorised at the document level", function() { return it("should not allow access", function() {
|
||||
return this.AuthorizationManager.assertClientCanViewProjectAndDoc(this.client, this.doc_id, err => err.message.should.equal("not authorized"));
|
||||
}); });
|
||||
describe('and not authorised at the document level', function () {
|
||||
return it('should not allow access', function () {
|
||||
return this.AuthorizationManager.assertClientCanViewProjectAndDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
(err) => err.message.should.equal('not authorized')
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("and authorised at the document level", function() {
|
||||
beforeEach(function(done) {
|
||||
return this.AuthorizationManager.addAccessToDoc(this.client, this.doc_id, done);
|
||||
});
|
||||
describe('and authorised at the document level', function () {
|
||||
beforeEach(function (done) {
|
||||
return this.AuthorizationManager.addAccessToDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
done
|
||||
)
|
||||
})
|
||||
|
||||
return it("should allow access", function() {
|
||||
this.AuthorizationManager.assertClientCanViewProjectAndDoc(this.client, this.doc_id, this.callback);
|
||||
return this.callback
|
||||
.calledWith(null)
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should allow access', function () {
|
||||
this.AuthorizationManager.assertClientCanViewProjectAndDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
this.callback
|
||||
)
|
||||
return this.callback.calledWith(null).should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return describe("when document authorisation is added and then removed", function() {
|
||||
beforeEach(function(done) {
|
||||
return this.AuthorizationManager.addAccessToDoc(this.client, this.doc_id, () => {
|
||||
return this.AuthorizationManager.removeAccessToDoc(this.client, this.doc_id, done);
|
||||
});
|
||||
});
|
||||
return describe('when document authorisation is added and then removed', function () {
|
||||
beforeEach(function (done) {
|
||||
return this.AuthorizationManager.addAccessToDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
() => {
|
||||
return this.AuthorizationManager.removeAccessToDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
done
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return it("should deny access", function() {
|
||||
return this.AuthorizationManager.assertClientCanViewProjectAndDoc(this.client, this.doc_id, err => err.message.should.equal("not authorized"));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should deny access', function () {
|
||||
return this.AuthorizationManager.assertClientCanViewProjectAndDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
(err) => err.message.should.equal('not authorized')
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return describe("assertClientCanEditProjectAndDoc", function() {
|
||||
beforeEach(function() {
|
||||
this.doc_id = "12345";
|
||||
this.callback = sinon.stub();
|
||||
return this.client.ol_context = {};});
|
||||
return describe('assertClientCanEditProjectAndDoc', function () {
|
||||
beforeEach(function () {
|
||||
this.doc_id = '12345'
|
||||
this.callback = sinon.stub()
|
||||
return (this.client.ol_context = {})
|
||||
})
|
||||
|
||||
describe("when not authorised at the project level", function() {
|
||||
beforeEach(function() {
|
||||
return this.client.ol_context.privilege_level = "readOnly";
|
||||
});
|
||||
describe('when not authorised at the project level', function () {
|
||||
beforeEach(function () {
|
||||
return (this.client.ol_context.privilege_level = 'readOnly')
|
||||
})
|
||||
|
||||
it("should not allow access", function() {
|
||||
return this.AuthorizationManager.assertClientCanEditProjectAndDoc(this.client, this.doc_id, err => err.message.should.equal("not authorized"));
|
||||
});
|
||||
it('should not allow access', function () {
|
||||
return this.AuthorizationManager.assertClientCanEditProjectAndDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
(err) => err.message.should.equal('not authorized')
|
||||
)
|
||||
})
|
||||
|
||||
return describe("even when authorised at the doc level", function() {
|
||||
beforeEach(function(done) {
|
||||
return this.AuthorizationManager.addAccessToDoc(this.client, this.doc_id, done);
|
||||
});
|
||||
return describe('even when authorised at the doc level', function () {
|
||||
beforeEach(function (done) {
|
||||
return this.AuthorizationManager.addAccessToDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
done
|
||||
)
|
||||
})
|
||||
|
||||
return it("should not allow access", function() {
|
||||
return this.AuthorizationManager.assertClientCanEditProjectAndDoc(this.client, this.doc_id, err => err.message.should.equal("not authorized"));
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should not allow access', function () {
|
||||
return this.AuthorizationManager.assertClientCanEditProjectAndDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
(err) => err.message.should.equal('not authorized')
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return describe("when authorised at the project level", function() {
|
||||
beforeEach(function() {
|
||||
return this.client.ol_context.privilege_level = "readAndWrite";
|
||||
});
|
||||
return describe('when authorised at the project level', function () {
|
||||
beforeEach(function () {
|
||||
return (this.client.ol_context.privilege_level = 'readAndWrite')
|
||||
})
|
||||
|
||||
describe("and not authorised at the document level", function() { return it("should not allow access", function() {
|
||||
return this.AuthorizationManager.assertClientCanEditProjectAndDoc(this.client, this.doc_id, err => err.message.should.equal("not authorized"));
|
||||
}); });
|
||||
describe('and not authorised at the document level', function () {
|
||||
return it('should not allow access', function () {
|
||||
return this.AuthorizationManager.assertClientCanEditProjectAndDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
(err) => err.message.should.equal('not authorized')
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("and authorised at the document level", function() {
|
||||
beforeEach(function(done) {
|
||||
return this.AuthorizationManager.addAccessToDoc(this.client, this.doc_id, done);
|
||||
});
|
||||
describe('and authorised at the document level', function () {
|
||||
beforeEach(function (done) {
|
||||
return this.AuthorizationManager.addAccessToDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
done
|
||||
)
|
||||
})
|
||||
|
||||
return it("should allow access", function() {
|
||||
this.AuthorizationManager.assertClientCanEditProjectAndDoc(this.client, this.doc_id, this.callback);
|
||||
return this.callback
|
||||
.calledWith(null)
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should allow access', function () {
|
||||
this.AuthorizationManager.assertClientCanEditProjectAndDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
this.callback
|
||||
)
|
||||
return this.callback.calledWith(null).should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return describe("when document authorisation is added and then removed", function() {
|
||||
beforeEach(function(done) {
|
||||
return this.AuthorizationManager.addAccessToDoc(this.client, this.doc_id, () => {
|
||||
return this.AuthorizationManager.removeAccessToDoc(this.client, this.doc_id, done);
|
||||
});
|
||||
});
|
||||
return describe('when document authorisation is added and then removed', function () {
|
||||
beforeEach(function (done) {
|
||||
return this.AuthorizationManager.addAccessToDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
() => {
|
||||
return this.AuthorizationManager.removeAccessToDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
done
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return it("should deny access", function() {
|
||||
return this.AuthorizationManager.assertClientCanEditProjectAndDoc(this.client, this.doc_id, err => err.message.should.equal("not authorized"));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should deny access', function () {
|
||||
return this.AuthorizationManager.assertClientCanEditProjectAndDoc(
|
||||
this.client,
|
||||
this.doc_id,
|
||||
(err) => err.message.should.equal('not authorized')
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -9,272 +9,430 @@
|
|||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const chai = require('chai');
|
||||
const should = chai.should();
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
const sinon = require("sinon");
|
||||
const modulePath = "../../../app/js/ChannelManager.js";
|
||||
const SandboxedModule = require('sandboxed-module');
|
||||
const chai = require('chai')
|
||||
const should = chai.should()
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
const modulePath = '../../../app/js/ChannelManager.js'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
|
||||
describe('ChannelManager', function() {
|
||||
beforeEach(function() {
|
||||
this.rclient = {};
|
||||
this.other_rclient = {};
|
||||
return this.ChannelManager = SandboxedModule.require(modulePath, { requires: {
|
||||
"settings-sharelatex": (this.settings = {}),
|
||||
"metrics-sharelatex": (this.metrics = {inc: sinon.stub(), summary: sinon.stub()}),
|
||||
"logger-sharelatex": (this.logger = { log: sinon.stub(), warn: sinon.stub(), error: sinon.stub() })
|
||||
}
|
||||
});});
|
||||
describe('ChannelManager', function () {
|
||||
beforeEach(function () {
|
||||
this.rclient = {}
|
||||
this.other_rclient = {}
|
||||
return (this.ChannelManager = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'settings-sharelatex': (this.settings = {}),
|
||||
'metrics-sharelatex': (this.metrics = {
|
||||
inc: sinon.stub(),
|
||||
summary: sinon.stub()
|
||||
}),
|
||||
'logger-sharelatex': (this.logger = {
|
||||
log: sinon.stub(),
|
||||
warn: sinon.stub(),
|
||||
error: sinon.stub()
|
||||
})
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
describe("subscribe", function() {
|
||||
describe('subscribe', function () {
|
||||
describe('when there is no existing subscription for this redis client', function () {
|
||||
beforeEach(function (done) {
|
||||
this.rclient.subscribe = sinon.stub().resolves()
|
||||
this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
return setTimeout(done)
|
||||
})
|
||||
|
||||
describe("when there is no existing subscription for this redis client", function() {
|
||||
beforeEach(function(done) {
|
||||
this.rclient.subscribe = sinon.stub().resolves();
|
||||
this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
return setTimeout(done);
|
||||
});
|
||||
return it('should subscribe to the redis channel', function () {
|
||||
return this.rclient.subscribe
|
||||
.calledWithExactly('applied-ops:1234567890abcdef')
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should subscribe to the redis channel", function() {
|
||||
return this.rclient.subscribe.calledWithExactly("applied-ops:1234567890abcdef").should.equal(true);
|
||||
});
|
||||
});
|
||||
describe('when there is an existing subscription for this redis client', function () {
|
||||
beforeEach(function (done) {
|
||||
this.rclient.subscribe = sinon.stub().resolves()
|
||||
this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
return setTimeout(done)
|
||||
})
|
||||
|
||||
describe("when there is an existing subscription for this redis client", function() {
|
||||
beforeEach(function(done) {
|
||||
this.rclient.subscribe = sinon.stub().resolves();
|
||||
this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
return setTimeout(done);
|
||||
});
|
||||
return it('should subscribe to the redis channel again', function () {
|
||||
return this.rclient.subscribe.callCount.should.equal(2)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should subscribe to the redis channel again", function() {
|
||||
return this.rclient.subscribe.callCount.should.equal(2);
|
||||
});
|
||||
});
|
||||
describe('when subscribe errors', function () {
|
||||
beforeEach(function (done) {
|
||||
this.rclient.subscribe = sinon
|
||||
.stub()
|
||||
.onFirstCall()
|
||||
.rejects(new Error('some redis error'))
|
||||
.onSecondCall()
|
||||
.resolves()
|
||||
const p = this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
p.then(() => done(new Error('should not subscribe but fail'))).catch(
|
||||
(err) => {
|
||||
err.message.should.equal('some redis error')
|
||||
this.ChannelManager.getClientMapEntry(this.rclient)
|
||||
.has('applied-ops:1234567890abcdef')
|
||||
.should.equal(false)
|
||||
this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
// subscribe is wrapped in Promise, delay other assertions
|
||||
return setTimeout(done)
|
||||
}
|
||||
)
|
||||
return null
|
||||
})
|
||||
|
||||
describe("when subscribe errors", function() {
|
||||
beforeEach(function(done) {
|
||||
this.rclient.subscribe = sinon.stub()
|
||||
.onFirstCall().rejects(new Error("some redis error"))
|
||||
.onSecondCall().resolves();
|
||||
const p = this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
p.then(() => done(new Error('should not subscribe but fail'))).catch(err => {
|
||||
err.message.should.equal("some redis error");
|
||||
this.ChannelManager.getClientMapEntry(this.rclient).has("applied-ops:1234567890abcdef").should.equal(false);
|
||||
this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
// subscribe is wrapped in Promise, delay other assertions
|
||||
return setTimeout(done);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
it('should have recorded the error', function () {
|
||||
return expect(
|
||||
this.metrics.inc.calledWithExactly('subscribe.failed.applied-ops')
|
||||
).to.equal(true)
|
||||
})
|
||||
|
||||
it("should have recorded the error", function() {
|
||||
return expect(this.metrics.inc.calledWithExactly("subscribe.failed.applied-ops")).to.equal(true);
|
||||
});
|
||||
it('should subscribe again', function () {
|
||||
return this.rclient.subscribe.callCount.should.equal(2)
|
||||
})
|
||||
|
||||
it("should subscribe again", function() {
|
||||
return this.rclient.subscribe.callCount.should.equal(2);
|
||||
});
|
||||
return it('should cleanup', function () {
|
||||
return this.ChannelManager.getClientMapEntry(this.rclient)
|
||||
.has('applied-ops:1234567890abcdef')
|
||||
.should.equal(false)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should cleanup", function() {
|
||||
return this.ChannelManager.getClientMapEntry(this.rclient).has("applied-ops:1234567890abcdef").should.equal(false);
|
||||
});
|
||||
});
|
||||
describe('when subscribe errors and the clientChannelMap entry was replaced', function () {
|
||||
beforeEach(function (done) {
|
||||
this.rclient.subscribe = sinon
|
||||
.stub()
|
||||
.onFirstCall()
|
||||
.rejects(new Error('some redis error'))
|
||||
.onSecondCall()
|
||||
.resolves()
|
||||
this.first = this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
// ignore error
|
||||
this.first.catch(() => {})
|
||||
expect(
|
||||
this.ChannelManager.getClientMapEntry(this.rclient).get(
|
||||
'applied-ops:1234567890abcdef'
|
||||
)
|
||||
).to.equal(this.first)
|
||||
|
||||
describe("when subscribe errors and the clientChannelMap entry was replaced", function() {
|
||||
beforeEach(function(done) {
|
||||
this.rclient.subscribe = sinon.stub()
|
||||
.onFirstCall().rejects(new Error("some redis error"))
|
||||
.onSecondCall().resolves();
|
||||
this.first = this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
// ignore error
|
||||
this.first.catch((() => {}));
|
||||
expect(this.ChannelManager.getClientMapEntry(this.rclient).get("applied-ops:1234567890abcdef")).to.equal(this.first);
|
||||
this.rclient.unsubscribe = sinon.stub().resolves()
|
||||
this.ChannelManager.unsubscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
this.second = this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
// should get replaced immediately
|
||||
expect(
|
||||
this.ChannelManager.getClientMapEntry(this.rclient).get(
|
||||
'applied-ops:1234567890abcdef'
|
||||
)
|
||||
).to.equal(this.second)
|
||||
|
||||
this.rclient.unsubscribe = sinon.stub().resolves();
|
||||
this.ChannelManager.unsubscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
this.second = this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
// should get replaced immediately
|
||||
expect(this.ChannelManager.getClientMapEntry(this.rclient).get("applied-ops:1234567890abcdef")).to.equal(this.second);
|
||||
// let the first subscribe error -> unsubscribe -> subscribe
|
||||
return setTimeout(done)
|
||||
})
|
||||
|
||||
// let the first subscribe error -> unsubscribe -> subscribe
|
||||
return setTimeout(done);
|
||||
});
|
||||
return it('should cleanup the second subscribePromise', function () {
|
||||
return expect(
|
||||
this.ChannelManager.getClientMapEntry(this.rclient).has(
|
||||
'applied-ops:1234567890abcdef'
|
||||
)
|
||||
).to.equal(false)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should cleanup the second subscribePromise", function() {
|
||||
return expect(this.ChannelManager.getClientMapEntry(this.rclient).has("applied-ops:1234567890abcdef")).to.equal(false);
|
||||
});
|
||||
});
|
||||
return describe('when there is an existing subscription for another redis client but not this one', function () {
|
||||
beforeEach(function (done) {
|
||||
this.other_rclient.subscribe = sinon.stub().resolves()
|
||||
this.ChannelManager.subscribe(
|
||||
this.other_rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
this.rclient.subscribe = sinon.stub().resolves() // discard the original stub
|
||||
this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
return setTimeout(done)
|
||||
})
|
||||
|
||||
return describe("when there is an existing subscription for another redis client but not this one", function() {
|
||||
beforeEach(function(done) {
|
||||
this.other_rclient.subscribe = sinon.stub().resolves();
|
||||
this.ChannelManager.subscribe(this.other_rclient, "applied-ops", "1234567890abcdef");
|
||||
this.rclient.subscribe = sinon.stub().resolves(); // discard the original stub
|
||||
this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
return setTimeout(done);
|
||||
});
|
||||
return it('should subscribe to the redis channel on this redis client', function () {
|
||||
return this.rclient.subscribe
|
||||
.calledWithExactly('applied-ops:1234567890abcdef')
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return it("should subscribe to the redis channel on this redis client", function() {
|
||||
return this.rclient.subscribe.calledWithExactly("applied-ops:1234567890abcdef").should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('unsubscribe', function () {
|
||||
describe('when there is no existing subscription for this redis client', function () {
|
||||
beforeEach(function (done) {
|
||||
this.rclient.unsubscribe = sinon.stub().resolves()
|
||||
this.ChannelManager.unsubscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
return setTimeout(done)
|
||||
})
|
||||
|
||||
describe("unsubscribe", function() {
|
||||
return it('should unsubscribe from the redis channel', function () {
|
||||
return this.rclient.unsubscribe.called.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("when there is no existing subscription for this redis client", function() {
|
||||
beforeEach(function(done) {
|
||||
this.rclient.unsubscribe = sinon.stub().resolves();
|
||||
this.ChannelManager.unsubscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
return setTimeout(done);
|
||||
});
|
||||
describe('when there is an existing subscription for this another redis client but not this one', function () {
|
||||
beforeEach(function (done) {
|
||||
this.other_rclient.subscribe = sinon.stub().resolves()
|
||||
this.rclient.unsubscribe = sinon.stub().resolves()
|
||||
this.ChannelManager.subscribe(
|
||||
this.other_rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
this.ChannelManager.unsubscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
return setTimeout(done)
|
||||
})
|
||||
|
||||
return it("should unsubscribe from the redis channel", function() {
|
||||
return this.rclient.unsubscribe.called.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should still unsubscribe from the redis channel on this client', function () {
|
||||
return this.rclient.unsubscribe.called.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when unsubscribe errors and completes', function () {
|
||||
beforeEach(function (done) {
|
||||
this.rclient.subscribe = sinon.stub().resolves()
|
||||
this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
this.rclient.unsubscribe = sinon
|
||||
.stub()
|
||||
.rejects(new Error('some redis error'))
|
||||
this.ChannelManager.unsubscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
setTimeout(done)
|
||||
return null
|
||||
})
|
||||
|
||||
describe("when there is an existing subscription for this another redis client but not this one", function() {
|
||||
beforeEach(function(done) {
|
||||
this.other_rclient.subscribe = sinon.stub().resolves();
|
||||
this.rclient.unsubscribe = sinon.stub().resolves();
|
||||
this.ChannelManager.subscribe(this.other_rclient, "applied-ops", "1234567890abcdef");
|
||||
this.ChannelManager.unsubscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
return setTimeout(done);
|
||||
});
|
||||
it('should have cleaned up', function () {
|
||||
return this.ChannelManager.getClientMapEntry(this.rclient)
|
||||
.has('applied-ops:1234567890abcdef')
|
||||
.should.equal(false)
|
||||
})
|
||||
|
||||
return it("should still unsubscribe from the redis channel on this client", function() {
|
||||
return this.rclient.unsubscribe.called.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should not error out when subscribing again', function (done) {
|
||||
const p = this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
p.then(() => done()).catch(done)
|
||||
return null
|
||||
})
|
||||
})
|
||||
|
||||
describe("when unsubscribe errors and completes", function() {
|
||||
beforeEach(function(done) {
|
||||
this.rclient.subscribe = sinon.stub().resolves();
|
||||
this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
this.rclient.unsubscribe = sinon.stub().rejects(new Error("some redis error"));
|
||||
this.ChannelManager.unsubscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
setTimeout(done);
|
||||
return null;
|
||||
});
|
||||
describe('when unsubscribe errors and another client subscribes at the same time', function () {
|
||||
beforeEach(function (done) {
|
||||
this.rclient.subscribe = sinon.stub().resolves()
|
||||
this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
let rejectSubscribe
|
||||
this.rclient.unsubscribe = () =>
|
||||
new Promise((resolve, reject) => (rejectSubscribe = reject))
|
||||
this.ChannelManager.unsubscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
|
||||
it("should have cleaned up", function() {
|
||||
return this.ChannelManager.getClientMapEntry(this.rclient).has("applied-ops:1234567890abcdef").should.equal(false);
|
||||
});
|
||||
setTimeout(() => {
|
||||
// delay, actualUnsubscribe should not see the new subscribe request
|
||||
this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
.then(() => setTimeout(done))
|
||||
.catch(done)
|
||||
return setTimeout(() =>
|
||||
// delay, rejectSubscribe is not defined immediately
|
||||
rejectSubscribe(new Error('redis error'))
|
||||
)
|
||||
})
|
||||
return null
|
||||
})
|
||||
|
||||
return it("should not error out when subscribing again", function(done) {
|
||||
const p = this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
p.then(() => done()).catch(done);
|
||||
return null;
|
||||
});
|
||||
});
|
||||
it('should have recorded the error', function () {
|
||||
return expect(
|
||||
this.metrics.inc.calledWithExactly('unsubscribe.failed.applied-ops')
|
||||
).to.equal(true)
|
||||
})
|
||||
|
||||
describe("when unsubscribe errors and another client subscribes at the same time", function() {
|
||||
beforeEach(function(done) {
|
||||
this.rclient.subscribe = sinon.stub().resolves();
|
||||
this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
let rejectSubscribe;
|
||||
this.rclient.unsubscribe = () => new Promise((resolve, reject) => rejectSubscribe = reject);
|
||||
this.ChannelManager.unsubscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
it('should have subscribed', function () {
|
||||
return this.rclient.subscribe.called.should.equal(true)
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
// delay, actualUnsubscribe should not see the new subscribe request
|
||||
this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef")
|
||||
.then(() => setTimeout(done)).catch(done);
|
||||
return setTimeout(() => // delay, rejectSubscribe is not defined immediately
|
||||
rejectSubscribe(new Error("redis error")));
|
||||
});
|
||||
return null;
|
||||
});
|
||||
return it('should have discarded the finished Promise', function () {
|
||||
return this.ChannelManager.getClientMapEntry(this.rclient)
|
||||
.has('applied-ops:1234567890abcdef')
|
||||
.should.equal(false)
|
||||
})
|
||||
})
|
||||
|
||||
it("should have recorded the error", function() {
|
||||
return expect(this.metrics.inc.calledWithExactly("unsubscribe.failed.applied-ops")).to.equal(true);
|
||||
});
|
||||
return describe('when there is an existing subscription for this redis client', function () {
|
||||
beforeEach(function (done) {
|
||||
this.rclient.subscribe = sinon.stub().resolves()
|
||||
this.rclient.unsubscribe = sinon.stub().resolves()
|
||||
this.ChannelManager.subscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
this.ChannelManager.unsubscribe(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef'
|
||||
)
|
||||
return setTimeout(done)
|
||||
})
|
||||
|
||||
it("should have subscribed", function() {
|
||||
return this.rclient.subscribe.called.should.equal(true);
|
||||
});
|
||||
return it('should unsubscribe from the redis channel', function () {
|
||||
return this.rclient.unsubscribe
|
||||
.calledWithExactly('applied-ops:1234567890abcdef')
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return it("should have discarded the finished Promise", function() {
|
||||
return this.ChannelManager.getClientMapEntry(this.rclient).has("applied-ops:1234567890abcdef").should.equal(false);
|
||||
});
|
||||
});
|
||||
return describe('publish', function () {
|
||||
describe("when the channel is 'all'", function () {
|
||||
beforeEach(function () {
|
||||
this.rclient.publish = sinon.stub()
|
||||
return this.ChannelManager.publish(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'all',
|
||||
'random-message'
|
||||
)
|
||||
})
|
||||
|
||||
return describe("when there is an existing subscription for this redis client", function() {
|
||||
beforeEach(function(done) {
|
||||
this.rclient.subscribe = sinon.stub().resolves();
|
||||
this.rclient.unsubscribe = sinon.stub().resolves();
|
||||
this.ChannelManager.subscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
this.ChannelManager.unsubscribe(this.rclient, "applied-ops", "1234567890abcdef");
|
||||
return setTimeout(done);
|
||||
});
|
||||
return it('should publish on the base channel', function () {
|
||||
return this.rclient.publish
|
||||
.calledWithExactly('applied-ops', 'random-message')
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should unsubscribe from the redis channel", function() {
|
||||
return this.rclient.unsubscribe.calledWithExactly("applied-ops:1234567890abcdef").should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('when the channel has an specific id', function () {
|
||||
describe('when the individual channel setting is false', function () {
|
||||
beforeEach(function () {
|
||||
this.rclient.publish = sinon.stub()
|
||||
this.settings.publishOnIndividualChannels = false
|
||||
return this.ChannelManager.publish(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef',
|
||||
'random-message'
|
||||
)
|
||||
})
|
||||
|
||||
return describe("publish", function() {
|
||||
return it('should publish on the per-id channel', function () {
|
||||
this.rclient.publish
|
||||
.calledWithExactly('applied-ops', 'random-message')
|
||||
.should.equal(true)
|
||||
return this.rclient.publish.calledOnce.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("when the channel is 'all'", function() {
|
||||
beforeEach(function() {
|
||||
this.rclient.publish = sinon.stub();
|
||||
return this.ChannelManager.publish(this.rclient, "applied-ops", "all", "random-message");
|
||||
});
|
||||
return describe('when the individual channel setting is true', function () {
|
||||
beforeEach(function () {
|
||||
this.rclient.publish = sinon.stub()
|
||||
this.settings.publishOnIndividualChannels = true
|
||||
return this.ChannelManager.publish(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'1234567890abcdef',
|
||||
'random-message'
|
||||
)
|
||||
})
|
||||
|
||||
return it("should publish on the base channel", function() {
|
||||
return this.rclient.publish.calledWithExactly("applied-ops", "random-message").should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should publish on the per-id channel', function () {
|
||||
this.rclient.publish
|
||||
.calledWithExactly('applied-ops:1234567890abcdef', 'random-message')
|
||||
.should.equal(true)
|
||||
return this.rclient.publish.calledOnce.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("when the channel has an specific id", function() {
|
||||
return describe('metrics', function () {
|
||||
beforeEach(function () {
|
||||
this.rclient.publish = sinon.stub()
|
||||
return this.ChannelManager.publish(
|
||||
this.rclient,
|
||||
'applied-ops',
|
||||
'all',
|
||||
'random-message'
|
||||
)
|
||||
})
|
||||
|
||||
describe("when the individual channel setting is false", function() {
|
||||
beforeEach(function() {
|
||||
this.rclient.publish = sinon.stub();
|
||||
this.settings.publishOnIndividualChannels = false;
|
||||
return this.ChannelManager.publish(this.rclient, "applied-ops", "1234567890abcdef", "random-message");
|
||||
});
|
||||
|
||||
return it("should publish on the per-id channel", function() {
|
||||
this.rclient.publish.calledWithExactly("applied-ops", "random-message").should.equal(true);
|
||||
return this.rclient.publish.calledOnce.should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
return describe("when the individual channel setting is true", function() {
|
||||
beforeEach(function() {
|
||||
this.rclient.publish = sinon.stub();
|
||||
this.settings.publishOnIndividualChannels = true;
|
||||
return this.ChannelManager.publish(this.rclient, "applied-ops", "1234567890abcdef", "random-message");
|
||||
});
|
||||
|
||||
return it("should publish on the per-id channel", function() {
|
||||
this.rclient.publish.calledWithExactly("applied-ops:1234567890abcdef", "random-message").should.equal(true);
|
||||
return this.rclient.publish.calledOnce.should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return describe("metrics", function() {
|
||||
beforeEach(function() {
|
||||
this.rclient.publish = sinon.stub();
|
||||
return this.ChannelManager.publish(this.rclient, "applied-ops", "all", "random-message");
|
||||
});
|
||||
|
||||
return it("should track the payload size", function() {
|
||||
return this.metrics.summary.calledWithExactly(
|
||||
"redis.publish.applied-ops",
|
||||
"random-message".length
|
||||
).should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should track the payload size', function () {
|
||||
return this.metrics.summary
|
||||
.calledWithExactly(
|
||||
'redis.publish.applied-ops',
|
||||
'random-message'.length
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -12,218 +12,398 @@
|
|||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
|
||||
const should = require('chai').should();
|
||||
const SandboxedModule = require('sandboxed-module');
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const sinon = require('sinon');
|
||||
const modulePath = path.join(__dirname, "../../../app/js/ConnectedUsersManager");
|
||||
const {
|
||||
expect
|
||||
} = require("chai");
|
||||
const tk = require("timekeeper");
|
||||
const should = require('chai').should()
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const assert = require('assert')
|
||||
const path = require('path')
|
||||
const sinon = require('sinon')
|
||||
const modulePath = path.join(__dirname, '../../../app/js/ConnectedUsersManager')
|
||||
const { expect } = require('chai')
|
||||
const tk = require('timekeeper')
|
||||
|
||||
describe('ConnectedUsersManager', function () {
|
||||
beforeEach(function () {
|
||||
this.settings = {
|
||||
redis: {
|
||||
realtime: {
|
||||
key_schema: {
|
||||
clientsInProject({ project_id }) {
|
||||
return `clients_in_project:${project_id}`
|
||||
},
|
||||
connectedUser({ project_id, client_id }) {
|
||||
return `connected_user:${project_id}:${client_id}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.rClient = {
|
||||
auth() {},
|
||||
setex: sinon.stub(),
|
||||
sadd: sinon.stub(),
|
||||
get: sinon.stub(),
|
||||
srem: sinon.stub(),
|
||||
del: sinon.stub(),
|
||||
smembers: sinon.stub(),
|
||||
expire: sinon.stub(),
|
||||
hset: sinon.stub(),
|
||||
hgetall: sinon.stub(),
|
||||
exec: sinon.stub(),
|
||||
multi: () => {
|
||||
return this.rClient
|
||||
}
|
||||
}
|
||||
tk.freeze(new Date())
|
||||
|
||||
describe("ConnectedUsersManager", function() {
|
||||
this.ConnectedUsersManager = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'settings-sharelatex': this.settings,
|
||||
'logger-sharelatex': { log() {} },
|
||||
'redis-sharelatex': {
|
||||
createClient: () => {
|
||||
return this.rClient
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
this.client_id = '32132132'
|
||||
this.project_id = 'dskjh2u21321'
|
||||
this.user = {
|
||||
_id: 'user-id-123',
|
||||
first_name: 'Joe',
|
||||
last_name: 'Bloggs',
|
||||
email: 'joe@example.com'
|
||||
}
|
||||
return (this.cursorData = {
|
||||
row: 12,
|
||||
column: 9,
|
||||
doc_id: '53c3b8c85fee64000023dc6e'
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
afterEach(function () {
|
||||
return tk.reset()
|
||||
})
|
||||
|
||||
this.settings = {
|
||||
redis: {
|
||||
realtime: {
|
||||
key_schema: {
|
||||
clientsInProject({project_id}) { return `clients_in_project:${project_id}`; },
|
||||
connectedUser({project_id, client_id}){ return `connected_user:${project_id}:${client_id}`; }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
this.rClient = {
|
||||
auth() {},
|
||||
setex:sinon.stub(),
|
||||
sadd:sinon.stub(),
|
||||
get: sinon.stub(),
|
||||
srem:sinon.stub(),
|
||||
del:sinon.stub(),
|
||||
smembers:sinon.stub(),
|
||||
expire:sinon.stub(),
|
||||
hset:sinon.stub(),
|
||||
hgetall:sinon.stub(),
|
||||
exec:sinon.stub(),
|
||||
multi: () => { return this.rClient; }
|
||||
};
|
||||
tk.freeze(new Date());
|
||||
describe('updateUserPosition', function () {
|
||||
beforeEach(function () {
|
||||
return this.rClient.exec.callsArgWith(0)
|
||||
})
|
||||
|
||||
this.ConnectedUsersManager = SandboxedModule.require(modulePath, { requires: {
|
||||
"settings-sharelatex":this.settings,
|
||||
"logger-sharelatex": { log() {}
|
||||
},
|
||||
"redis-sharelatex": { createClient:() => {
|
||||
return this.rClient;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
this.client_id = "32132132";
|
||||
this.project_id = "dskjh2u21321";
|
||||
this.user = {
|
||||
_id: "user-id-123",
|
||||
first_name: "Joe",
|
||||
last_name: "Bloggs",
|
||||
email: "joe@example.com"
|
||||
};
|
||||
return this.cursorData = { row: 12, column: 9, doc_id: '53c3b8c85fee64000023dc6e' };});
|
||||
it('should set a key with the date and give it a ttl', function (done) {
|
||||
return this.ConnectedUsersManager.updateUserPosition(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
this.user,
|
||||
null,
|
||||
(err) => {
|
||||
this.rClient.hset
|
||||
.calledWith(
|
||||
`connected_user:${this.project_id}:${this.client_id}`,
|
||||
'last_updated_at',
|
||||
Date.now()
|
||||
)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(function() { return tk.reset(); });
|
||||
it('should set a key with the user_id', function (done) {
|
||||
return this.ConnectedUsersManager.updateUserPosition(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
this.user,
|
||||
null,
|
||||
(err) => {
|
||||
this.rClient.hset
|
||||
.calledWith(
|
||||
`connected_user:${this.project_id}:${this.client_id}`,
|
||||
'user_id',
|
||||
this.user._id
|
||||
)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
describe("updateUserPosition", function() {
|
||||
beforeEach(function() {
|
||||
return this.rClient.exec.callsArgWith(0);
|
||||
});
|
||||
it('should set a key with the first_name', function (done) {
|
||||
return this.ConnectedUsersManager.updateUserPosition(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
this.user,
|
||||
null,
|
||||
(err) => {
|
||||
this.rClient.hset
|
||||
.calledWith(
|
||||
`connected_user:${this.project_id}:${this.client_id}`,
|
||||
'first_name',
|
||||
this.user.first_name
|
||||
)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should set a key with the date and give it a ttl", function(done){
|
||||
return this.ConnectedUsersManager.updateUserPosition(this.project_id, this.client_id, this.user, null, err=> {
|
||||
this.rClient.hset.calledWith(`connected_user:${this.project_id}:${this.client_id}`, "last_updated_at", Date.now()).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should set a key with the last_name', function (done) {
|
||||
return this.ConnectedUsersManager.updateUserPosition(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
this.user,
|
||||
null,
|
||||
(err) => {
|
||||
this.rClient.hset
|
||||
.calledWith(
|
||||
`connected_user:${this.project_id}:${this.client_id}`,
|
||||
'last_name',
|
||||
this.user.last_name
|
||||
)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should set a key with the user_id", function(done){
|
||||
return this.ConnectedUsersManager.updateUserPosition(this.project_id, this.client_id, this.user, null, err=> {
|
||||
this.rClient.hset.calledWith(`connected_user:${this.project_id}:${this.client_id}`, "user_id", this.user._id).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should set a key with the email', function (done) {
|
||||
return this.ConnectedUsersManager.updateUserPosition(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
this.user,
|
||||
null,
|
||||
(err) => {
|
||||
this.rClient.hset
|
||||
.calledWith(
|
||||
`connected_user:${this.project_id}:${this.client_id}`,
|
||||
'email',
|
||||
this.user.email
|
||||
)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should set a key with the first_name", function(done){
|
||||
return this.ConnectedUsersManager.updateUserPosition(this.project_id, this.client_id, this.user, null, err=> {
|
||||
this.rClient.hset.calledWith(`connected_user:${this.project_id}:${this.client_id}`, "first_name", this.user.first_name).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should push the client_id on to the project list', function (done) {
|
||||
return this.ConnectedUsersManager.updateUserPosition(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
this.user,
|
||||
null,
|
||||
(err) => {
|
||||
this.rClient.sadd
|
||||
.calledWith(`clients_in_project:${this.project_id}`, this.client_id)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should set a key with the last_name", function(done){
|
||||
return this.ConnectedUsersManager.updateUserPosition(this.project_id, this.client_id, this.user, null, err=> {
|
||||
this.rClient.hset.calledWith(`connected_user:${this.project_id}:${this.client_id}`, "last_name", this.user.last_name).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should add a ttl to the project set so it stays clean', function (done) {
|
||||
return this.ConnectedUsersManager.updateUserPosition(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
this.user,
|
||||
null,
|
||||
(err) => {
|
||||
this.rClient.expire
|
||||
.calledWith(
|
||||
`clients_in_project:${this.project_id}`,
|
||||
24 * 4 * 60 * 60
|
||||
)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should set a key with the email", function(done){
|
||||
return this.ConnectedUsersManager.updateUserPosition(this.project_id, this.client_id, this.user, null, err=> {
|
||||
this.rClient.hset.calledWith(`connected_user:${this.project_id}:${this.client_id}`, "email", this.user.email).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should add a ttl to the connected user so it stays clean', function (done) {
|
||||
return this.ConnectedUsersManager.updateUserPosition(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
this.user,
|
||||
null,
|
||||
(err) => {
|
||||
this.rClient.expire
|
||||
.calledWith(
|
||||
`connected_user:${this.project_id}:${this.client_id}`,
|
||||
60 * 15
|
||||
)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should push the client_id on to the project list", function(done){
|
||||
return this.ConnectedUsersManager.updateUserPosition(this.project_id, this.client_id, this.user, null, err=> {
|
||||
this.rClient.sadd.calledWith(`clients_in_project:${this.project_id}`, this.client_id).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
return it('should set the cursor position when provided', function (done) {
|
||||
return this.ConnectedUsersManager.updateUserPosition(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
this.user,
|
||||
this.cursorData,
|
||||
(err) => {
|
||||
this.rClient.hset
|
||||
.calledWith(
|
||||
`connected_user:${this.project_id}:${this.client_id}`,
|
||||
'cursorData',
|
||||
JSON.stringify(this.cursorData)
|
||||
)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it("should add a ttl to the project set so it stays clean", function(done){
|
||||
return this.ConnectedUsersManager.updateUserPosition(this.project_id, this.client_id, this.user, null, err=> {
|
||||
this.rClient.expire.calledWith(`clients_in_project:${this.project_id}`, 24 * 4 * 60 * 60).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
describe('markUserAsDisconnected', function () {
|
||||
beforeEach(function () {
|
||||
return this.rClient.exec.callsArgWith(0)
|
||||
})
|
||||
|
||||
it("should add a ttl to the connected user so it stays clean", function(done) {
|
||||
return this.ConnectedUsersManager.updateUserPosition(this.project_id, this.client_id, this.user, null, err=> {
|
||||
this.rClient.expire.calledWith(`connected_user:${this.project_id}:${this.client_id}`, 60 * 15).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should remove the user from the set', function (done) {
|
||||
return this.ConnectedUsersManager.markUserAsDisconnected(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
(err) => {
|
||||
this.rClient.srem
|
||||
.calledWith(`clients_in_project:${this.project_id}`, this.client_id)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return it("should set the cursor position when provided", function(done){
|
||||
return this.ConnectedUsersManager.updateUserPosition(this.project_id, this.client_id, this.user, this.cursorData, err=> {
|
||||
this.rClient.hset.calledWith(`connected_user:${this.project_id}:${this.client_id}`, "cursorData", JSON.stringify(this.cursorData)).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should delete the connected_user string', function (done) {
|
||||
return this.ConnectedUsersManager.markUserAsDisconnected(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
(err) => {
|
||||
this.rClient.del
|
||||
.calledWith(`connected_user:${this.project_id}:${this.client_id}`)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
describe("markUserAsDisconnected", function() {
|
||||
beforeEach(function() {
|
||||
return this.rClient.exec.callsArgWith(0);
|
||||
});
|
||||
return it('should add a ttl to the connected user set so it stays clean', function (done) {
|
||||
return this.ConnectedUsersManager.markUserAsDisconnected(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
(err) => {
|
||||
this.rClient.expire
|
||||
.calledWith(
|
||||
`clients_in_project:${this.project_id}`,
|
||||
24 * 4 * 60 * 60
|
||||
)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it("should remove the user from the set", function(done){
|
||||
return this.ConnectedUsersManager.markUserAsDisconnected(this.project_id, this.client_id, err=> {
|
||||
this.rClient.srem.calledWith(`clients_in_project:${this.project_id}`, this.client_id).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
describe('_getConnectedUser', function () {
|
||||
it('should return a connected user if there is a user object', function (done) {
|
||||
const cursorData = JSON.stringify({ cursorData: { row: 1 } })
|
||||
this.rClient.hgetall.callsArgWith(1, null, {
|
||||
connected_at: new Date(),
|
||||
user_id: this.user._id,
|
||||
last_updated_at: `${Date.now()}`,
|
||||
cursorData
|
||||
})
|
||||
return this.ConnectedUsersManager._getConnectedUser(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
(err, result) => {
|
||||
result.connected.should.equal(true)
|
||||
result.client_id.should.equal(this.client_id)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should delete the connected_user string", function(done){
|
||||
return this.ConnectedUsersManager.markUserAsDisconnected(this.project_id, this.client_id, err=> {
|
||||
this.rClient.del.calledWith(`connected_user:${this.project_id}:${this.client_id}`).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should return a not connected user if there is no object', function (done) {
|
||||
this.rClient.hgetall.callsArgWith(1, null, null)
|
||||
return this.ConnectedUsersManager._getConnectedUser(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
(err, result) => {
|
||||
result.connected.should.equal(false)
|
||||
result.client_id.should.equal(this.client_id)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return it("should add a ttl to the connected user set so it stays clean", function(done){
|
||||
return this.ConnectedUsersManager.markUserAsDisconnected(this.project_id, this.client_id, err=> {
|
||||
this.rClient.expire.calledWith(`clients_in_project:${this.project_id}`, 24 * 4 * 60 * 60).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should return a not connected user if there is an empty object', function (done) {
|
||||
this.rClient.hgetall.callsArgWith(1, null, {})
|
||||
return this.ConnectedUsersManager._getConnectedUser(
|
||||
this.project_id,
|
||||
this.client_id,
|
||||
(err, result) => {
|
||||
result.connected.should.equal(false)
|
||||
result.client_id.should.equal(this.client_id)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("_getConnectedUser", function() {
|
||||
|
||||
it("should return a connected user if there is a user object", function(done){
|
||||
const cursorData = JSON.stringify({cursorData:{row:1}});
|
||||
this.rClient.hgetall.callsArgWith(1, null, {connected_at:new Date(), user_id: this.user._id, last_updated_at: `${Date.now()}`, cursorData});
|
||||
return this.ConnectedUsersManager._getConnectedUser(this.project_id, this.client_id, (err, result)=> {
|
||||
result.connected.should.equal(true);
|
||||
result.client_id.should.equal(this.client_id);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return a not connected user if there is no object", function(done){
|
||||
this.rClient.hgetall.callsArgWith(1, null, null);
|
||||
return this.ConnectedUsersManager._getConnectedUser(this.project_id, this.client_id, (err, result)=> {
|
||||
result.connected.should.equal(false);
|
||||
result.client_id.should.equal(this.client_id);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
return it("should return a not connected user if there is an empty object", function(done){
|
||||
this.rClient.hgetall.callsArgWith(1, null, {});
|
||||
return this.ConnectedUsersManager._getConnectedUser(this.project_id, this.client_id, (err, result)=> {
|
||||
result.connected.should.equal(false);
|
||||
result.client_id.should.equal(this.client_id);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return describe("getConnectedUsers", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.users = ["1234", "5678", "9123", "8234"];
|
||||
this.rClient.smembers.callsArgWith(1, null, this.users);
|
||||
this.ConnectedUsersManager._getConnectedUser = sinon.stub();
|
||||
this.ConnectedUsersManager._getConnectedUser.withArgs(this.project_id, this.users[0]).callsArgWith(2, null, {connected:true, client_age: 2, client_id:this.users[0]});
|
||||
this.ConnectedUsersManager._getConnectedUser.withArgs(this.project_id, this.users[1]).callsArgWith(2, null, {connected:false, client_age: 1, client_id:this.users[1]});
|
||||
this.ConnectedUsersManager._getConnectedUser.withArgs(this.project_id, this.users[2]).callsArgWith(2, null, {connected:true, client_age: 3, client_id:this.users[2]});
|
||||
return this.ConnectedUsersManager._getConnectedUser.withArgs(this.project_id, this.users[3]).callsArgWith(2, null, {connected:true, client_age: 11, client_id:this.users[3]});
|
||||
}); // connected but old
|
||||
|
||||
return it("should only return the users in the list which are still in redis and recently updated", function(done){
|
||||
return this.ConnectedUsersManager.getConnectedUsers(this.project_id, (err, users)=> {
|
||||
users.length.should.equal(2);
|
||||
users[0].should.deep.equal({client_id:this.users[0], client_age: 2, connected:true});
|
||||
users[1].should.deep.equal({client_id:this.users[2], client_age: 3, connected:true});
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return describe('getConnectedUsers', function () {
|
||||
beforeEach(function () {
|
||||
this.users = ['1234', '5678', '9123', '8234']
|
||||
this.rClient.smembers.callsArgWith(1, null, this.users)
|
||||
this.ConnectedUsersManager._getConnectedUser = sinon.stub()
|
||||
this.ConnectedUsersManager._getConnectedUser
|
||||
.withArgs(this.project_id, this.users[0])
|
||||
.callsArgWith(2, null, {
|
||||
connected: true,
|
||||
client_age: 2,
|
||||
client_id: this.users[0]
|
||||
})
|
||||
this.ConnectedUsersManager._getConnectedUser
|
||||
.withArgs(this.project_id, this.users[1])
|
||||
.callsArgWith(2, null, {
|
||||
connected: false,
|
||||
client_age: 1,
|
||||
client_id: this.users[1]
|
||||
})
|
||||
this.ConnectedUsersManager._getConnectedUser
|
||||
.withArgs(this.project_id, this.users[2])
|
||||
.callsArgWith(2, null, {
|
||||
connected: true,
|
||||
client_age: 3,
|
||||
client_id: this.users[2]
|
||||
})
|
||||
return this.ConnectedUsersManager._getConnectedUser
|
||||
.withArgs(this.project_id, this.users[3])
|
||||
.callsArgWith(2, null, {
|
||||
connected: true,
|
||||
client_age: 11,
|
||||
client_id: this.users[3]
|
||||
})
|
||||
}) // connected but old
|
||||
|
||||
return it('should only return the users in the list which are still in redis and recently updated', function (done) {
|
||||
return this.ConnectedUsersManager.getConnectedUsers(
|
||||
this.project_id,
|
||||
(err, users) => {
|
||||
users.length.should.equal(2)
|
||||
users[0].should.deep.equal({
|
||||
client_id: this.users[0],
|
||||
client_age: 2,
|
||||
connected: true
|
||||
})
|
||||
users[1].should.deep.equal({
|
||||
client_id: this.users[2],
|
||||
client_age: 3,
|
||||
connected: true
|
||||
})
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -10,200 +10,250 @@
|
|||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const SandboxedModule = require('sandboxed-module');
|
||||
const sinon = require('sinon');
|
||||
require('chai').should();
|
||||
const modulePath = require('path').join(__dirname, '../../../app/js/DocumentUpdaterController');
|
||||
const MockClient = require("./helpers/MockClient");
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const sinon = require('sinon')
|
||||
require('chai').should()
|
||||
const modulePath = require('path').join(
|
||||
__dirname,
|
||||
'../../../app/js/DocumentUpdaterController'
|
||||
)
|
||||
const MockClient = require('./helpers/MockClient')
|
||||
|
||||
describe("DocumentUpdaterController", function() {
|
||||
beforeEach(function() {
|
||||
this.project_id = "project-id-123";
|
||||
this.doc_id = "doc-id-123";
|
||||
this.callback = sinon.stub();
|
||||
this.io = { "mock": "socket.io" };
|
||||
this.rclient = [];
|
||||
this.RoomEvents = { on: sinon.stub() };
|
||||
return this.EditorUpdatesController = SandboxedModule.require(modulePath, { requires: {
|
||||
"logger-sharelatex": (this.logger = { error: sinon.stub(), log: sinon.stub(), warn: sinon.stub() }),
|
||||
"settings-sharelatex": (this.settings = {
|
||||
redis: {
|
||||
documentupdater: {
|
||||
key_schema: {
|
||||
pendingUpdates({doc_id}) { return `PendingUpdates:${doc_id}`; }
|
||||
}
|
||||
},
|
||||
pubsub: null
|
||||
}
|
||||
}),
|
||||
"redis-sharelatex" : (this.redis = {
|
||||
createClient: name => {
|
||||
let rclientStub;
|
||||
this.rclient.push(rclientStub = {name});
|
||||
return rclientStub;
|
||||
}
|
||||
}),
|
||||
"./SafeJsonParse": (this.SafeJsonParse =
|
||||
{parse: (data, cb) => cb(null, JSON.parse(data))}),
|
||||
"./EventLogger": (this.EventLogger = {checkEventOrder: sinon.stub()}),
|
||||
"./HealthCheckManager": {check: sinon.stub()},
|
||||
"metrics-sharelatex": (this.metrics = {inc: sinon.stub()}),
|
||||
"./RoomManager" : (this.RoomManager = { eventSource: sinon.stub().returns(this.RoomEvents)}),
|
||||
"./ChannelManager": (this.ChannelManager = {})
|
||||
}
|
||||
});});
|
||||
describe('DocumentUpdaterController', function () {
|
||||
beforeEach(function () {
|
||||
this.project_id = 'project-id-123'
|
||||
this.doc_id = 'doc-id-123'
|
||||
this.callback = sinon.stub()
|
||||
this.io = { mock: 'socket.io' }
|
||||
this.rclient = []
|
||||
this.RoomEvents = { on: sinon.stub() }
|
||||
return (this.EditorUpdatesController = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'logger-sharelatex': (this.logger = {
|
||||
error: sinon.stub(),
|
||||
log: sinon.stub(),
|
||||
warn: sinon.stub()
|
||||
}),
|
||||
'settings-sharelatex': (this.settings = {
|
||||
redis: {
|
||||
documentupdater: {
|
||||
key_schema: {
|
||||
pendingUpdates({ doc_id }) {
|
||||
return `PendingUpdates:${doc_id}`
|
||||
}
|
||||
}
|
||||
},
|
||||
pubsub: null
|
||||
}
|
||||
}),
|
||||
'redis-sharelatex': (this.redis = {
|
||||
createClient: (name) => {
|
||||
let rclientStub
|
||||
this.rclient.push((rclientStub = { name }))
|
||||
return rclientStub
|
||||
}
|
||||
}),
|
||||
'./SafeJsonParse': (this.SafeJsonParse = {
|
||||
parse: (data, cb) => cb(null, JSON.parse(data))
|
||||
}),
|
||||
'./EventLogger': (this.EventLogger = { checkEventOrder: sinon.stub() }),
|
||||
'./HealthCheckManager': { check: sinon.stub() },
|
||||
'metrics-sharelatex': (this.metrics = { inc: sinon.stub() }),
|
||||
'./RoomManager': (this.RoomManager = {
|
||||
eventSource: sinon.stub().returns(this.RoomEvents)
|
||||
}),
|
||||
'./ChannelManager': (this.ChannelManager = {})
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
describe("listenForUpdatesFromDocumentUpdater", function() {
|
||||
beforeEach(function() {
|
||||
this.rclient.length = 0; // clear any existing clients
|
||||
this.EditorUpdatesController.rclientList = [this.redis.createClient("first"), this.redis.createClient("second")];
|
||||
this.rclient[0].subscribe = sinon.stub();
|
||||
this.rclient[0].on = sinon.stub();
|
||||
this.rclient[1].subscribe = sinon.stub();
|
||||
this.rclient[1].on = sinon.stub();
|
||||
return this.EditorUpdatesController.listenForUpdatesFromDocumentUpdater();
|
||||
});
|
||||
|
||||
it("should subscribe to the doc-updater stream", function() {
|
||||
return this.rclient[0].subscribe.calledWith("applied-ops").should.equal(true);
|
||||
});
|
||||
describe('listenForUpdatesFromDocumentUpdater', function () {
|
||||
beforeEach(function () {
|
||||
this.rclient.length = 0 // clear any existing clients
|
||||
this.EditorUpdatesController.rclientList = [
|
||||
this.redis.createClient('first'),
|
||||
this.redis.createClient('second')
|
||||
]
|
||||
this.rclient[0].subscribe = sinon.stub()
|
||||
this.rclient[0].on = sinon.stub()
|
||||
this.rclient[1].subscribe = sinon.stub()
|
||||
this.rclient[1].on = sinon.stub()
|
||||
return this.EditorUpdatesController.listenForUpdatesFromDocumentUpdater()
|
||||
})
|
||||
|
||||
it("should register a callback to handle updates", function() {
|
||||
return this.rclient[0].on.calledWith("message").should.equal(true);
|
||||
});
|
||||
it('should subscribe to the doc-updater stream', function () {
|
||||
return this.rclient[0].subscribe
|
||||
.calledWith('applied-ops')
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
return it("should subscribe to any additional doc-updater stream", function() {
|
||||
this.rclient[1].subscribe.calledWith("applied-ops").should.equal(true);
|
||||
return this.rclient[1].on.calledWith("message").should.equal(true);
|
||||
});
|
||||
});
|
||||
it('should register a callback to handle updates', function () {
|
||||
return this.rclient[0].on.calledWith('message').should.equal(true)
|
||||
})
|
||||
|
||||
describe("_processMessageFromDocumentUpdater", function() {
|
||||
describe("with bad JSON", function() {
|
||||
beforeEach(function() {
|
||||
this.SafeJsonParse.parse = sinon.stub().callsArgWith(1, new Error("oops"));
|
||||
return this.EditorUpdatesController._processMessageFromDocumentUpdater(this.io, "applied-ops", "blah");
|
||||
});
|
||||
|
||||
return it("should log an error", function() {
|
||||
return this.logger.error.called.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should subscribe to any additional doc-updater stream', function () {
|
||||
this.rclient[1].subscribe.calledWith('applied-ops').should.equal(true)
|
||||
return this.rclient[1].on.calledWith('message').should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("with update", function() {
|
||||
beforeEach(function() {
|
||||
this.message = {
|
||||
doc_id: this.doc_id,
|
||||
op: {t: "foo", p: 12}
|
||||
};
|
||||
this.EditorUpdatesController._applyUpdateFromDocumentUpdater = sinon.stub();
|
||||
return this.EditorUpdatesController._processMessageFromDocumentUpdater(this.io, "applied-ops", JSON.stringify(this.message));
|
||||
});
|
||||
describe('_processMessageFromDocumentUpdater', function () {
|
||||
describe('with bad JSON', function () {
|
||||
beforeEach(function () {
|
||||
this.SafeJsonParse.parse = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, new Error('oops'))
|
||||
return this.EditorUpdatesController._processMessageFromDocumentUpdater(
|
||||
this.io,
|
||||
'applied-ops',
|
||||
'blah'
|
||||
)
|
||||
})
|
||||
|
||||
return it("should apply the update", function() {
|
||||
return this.EditorUpdatesController._applyUpdateFromDocumentUpdater
|
||||
.calledWith(this.io, this.doc_id, this.message.op)
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should log an error', function () {
|
||||
return this.logger.error.called.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return describe("with error", function() {
|
||||
beforeEach(function() {
|
||||
this.message = {
|
||||
doc_id: this.doc_id,
|
||||
error: "Something went wrong"
|
||||
};
|
||||
this.EditorUpdatesController._processErrorFromDocumentUpdater = sinon.stub();
|
||||
return this.EditorUpdatesController._processMessageFromDocumentUpdater(this.io, "applied-ops", JSON.stringify(this.message));
|
||||
});
|
||||
describe('with update', function () {
|
||||
beforeEach(function () {
|
||||
this.message = {
|
||||
doc_id: this.doc_id,
|
||||
op: { t: 'foo', p: 12 }
|
||||
}
|
||||
this.EditorUpdatesController._applyUpdateFromDocumentUpdater = sinon.stub()
|
||||
return this.EditorUpdatesController._processMessageFromDocumentUpdater(
|
||||
this.io,
|
||||
'applied-ops',
|
||||
JSON.stringify(this.message)
|
||||
)
|
||||
})
|
||||
|
||||
return it("should process the error", function() {
|
||||
return this.EditorUpdatesController._processErrorFromDocumentUpdater
|
||||
.calledWith(this.io, this.doc_id, this.message.error)
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should apply the update', function () {
|
||||
return this.EditorUpdatesController._applyUpdateFromDocumentUpdater
|
||||
.calledWith(this.io, this.doc_id, this.message.op)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("_applyUpdateFromDocumentUpdater", function() {
|
||||
beforeEach(function() {
|
||||
this.sourceClient = new MockClient();
|
||||
this.otherClients = [new MockClient(), new MockClient()];
|
||||
this.update = {
|
||||
op: [ {t: "foo", p: 12} ],
|
||||
meta: { source: this.sourceClient.publicId
|
||||
},
|
||||
v: (this.version = 42),
|
||||
doc: this.doc_id
|
||||
};
|
||||
return this.io.sockets =
|
||||
{clients: sinon.stub().returns([this.sourceClient, ...Array.from(this.otherClients), this.sourceClient])};
|
||||
}); // include a duplicate client
|
||||
|
||||
describe("normally", function() {
|
||||
beforeEach(function() {
|
||||
return this.EditorUpdatesController._applyUpdateFromDocumentUpdater(this.io, this.doc_id, this.update);
|
||||
});
|
||||
return describe('with error', function () {
|
||||
beforeEach(function () {
|
||||
this.message = {
|
||||
doc_id: this.doc_id,
|
||||
error: 'Something went wrong'
|
||||
}
|
||||
this.EditorUpdatesController._processErrorFromDocumentUpdater = sinon.stub()
|
||||
return this.EditorUpdatesController._processMessageFromDocumentUpdater(
|
||||
this.io,
|
||||
'applied-ops',
|
||||
JSON.stringify(this.message)
|
||||
)
|
||||
})
|
||||
|
||||
it("should send a version bump to the source client", function() {
|
||||
this.sourceClient.emit
|
||||
.calledWith("otUpdateApplied", {v: this.version, doc: this.doc_id})
|
||||
.should.equal(true);
|
||||
return this.sourceClient.emit.calledOnce.should.equal(true);
|
||||
});
|
||||
return it('should process the error', function () {
|
||||
return this.EditorUpdatesController._processErrorFromDocumentUpdater
|
||||
.calledWith(this.io, this.doc_id, this.message.error)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("should get the clients connected to the document", function() {
|
||||
return this.io.sockets.clients
|
||||
.calledWith(this.doc_id)
|
||||
.should.equal(true);
|
||||
});
|
||||
describe('_applyUpdateFromDocumentUpdater', function () {
|
||||
beforeEach(function () {
|
||||
this.sourceClient = new MockClient()
|
||||
this.otherClients = [new MockClient(), new MockClient()]
|
||||
this.update = {
|
||||
op: [{ t: 'foo', p: 12 }],
|
||||
meta: { source: this.sourceClient.publicId },
|
||||
v: (this.version = 42),
|
||||
doc: this.doc_id
|
||||
}
|
||||
return (this.io.sockets = {
|
||||
clients: sinon
|
||||
.stub()
|
||||
.returns([
|
||||
this.sourceClient,
|
||||
...Array.from(this.otherClients),
|
||||
this.sourceClient
|
||||
])
|
||||
})
|
||||
}) // include a duplicate client
|
||||
|
||||
return it("should send the full update to the other clients", function() {
|
||||
return Array.from(this.otherClients).map((client) =>
|
||||
client.emit
|
||||
.calledWith("otUpdateApplied", this.update)
|
||||
.should.equal(true));
|
||||
});
|
||||
});
|
||||
|
||||
return describe("with a duplicate op", function() {
|
||||
beforeEach(function() {
|
||||
this.update.dup = true;
|
||||
return this.EditorUpdatesController._applyUpdateFromDocumentUpdater(this.io, this.doc_id, this.update);
|
||||
});
|
||||
|
||||
it("should send a version bump to the source client as usual", function() {
|
||||
return this.sourceClient.emit
|
||||
.calledWith("otUpdateApplied", {v: this.version, doc: this.doc_id})
|
||||
.should.equal(true);
|
||||
});
|
||||
describe('normally', function () {
|
||||
beforeEach(function () {
|
||||
return this.EditorUpdatesController._applyUpdateFromDocumentUpdater(
|
||||
this.io,
|
||||
this.doc_id,
|
||||
this.update
|
||||
)
|
||||
})
|
||||
|
||||
return it("should not send anything to the other clients (they've already had the op)", function() {
|
||||
return Array.from(this.otherClients).map((client) =>
|
||||
client.emit
|
||||
.calledWith("otUpdateApplied")
|
||||
.should.equal(false));
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should send a version bump to the source client', function () {
|
||||
this.sourceClient.emit
|
||||
.calledWith('otUpdateApplied', { v: this.version, doc: this.doc_id })
|
||||
.should.equal(true)
|
||||
return this.sourceClient.emit.calledOnce.should.equal(true)
|
||||
})
|
||||
|
||||
return describe("_processErrorFromDocumentUpdater", function() {
|
||||
beforeEach(function() {
|
||||
this.clients = [new MockClient(), new MockClient()];
|
||||
this.io.sockets =
|
||||
{clients: sinon.stub().returns(this.clients)};
|
||||
return this.EditorUpdatesController._processErrorFromDocumentUpdater(this.io, this.doc_id, "Something went wrong");
|
||||
});
|
||||
it('should get the clients connected to the document', function () {
|
||||
return this.io.sockets.clients
|
||||
.calledWith(this.doc_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it("should log a warning", function() {
|
||||
return this.logger.warn.called.should.equal(true);
|
||||
});
|
||||
return it('should send the full update to the other clients', function () {
|
||||
return Array.from(this.otherClients).map((client) =>
|
||||
client.emit
|
||||
.calledWith('otUpdateApplied', this.update)
|
||||
.should.equal(true)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should disconnect all clients in that document", function() {
|
||||
this.io.sockets.clients.calledWith(this.doc_id).should.equal(true);
|
||||
return Array.from(this.clients).map((client) =>
|
||||
client.disconnect.called.should.equal(true));
|
||||
});
|
||||
});
|
||||
});
|
||||
return describe('with a duplicate op', function () {
|
||||
beforeEach(function () {
|
||||
this.update.dup = true
|
||||
return this.EditorUpdatesController._applyUpdateFromDocumentUpdater(
|
||||
this.io,
|
||||
this.doc_id,
|
||||
this.update
|
||||
)
|
||||
})
|
||||
|
||||
it('should send a version bump to the source client as usual', function () {
|
||||
return this.sourceClient.emit
|
||||
.calledWith('otUpdateApplied', { v: this.version, doc: this.doc_id })
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
return it("should not send anything to the other clients (they've already had the op)", function () {
|
||||
return Array.from(this.otherClients).map((client) =>
|
||||
client.emit.calledWith('otUpdateApplied').should.equal(false)
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return describe('_processErrorFromDocumentUpdater', function () {
|
||||
beforeEach(function () {
|
||||
this.clients = [new MockClient(), new MockClient()]
|
||||
this.io.sockets = { clients: sinon.stub().returns(this.clients) }
|
||||
return this.EditorUpdatesController._processErrorFromDocumentUpdater(
|
||||
this.io,
|
||||
this.doc_id,
|
||||
'Something went wrong'
|
||||
)
|
||||
})
|
||||
|
||||
it('should log a warning', function () {
|
||||
return this.logger.warn.called.should.equal(true)
|
||||
})
|
||||
|
||||
return it('should disconnect all clients in that document', function () {
|
||||
this.io.sockets.clients.calledWith(this.doc_id).should.equal(true)
|
||||
return Array.from(this.clients).map((client) =>
|
||||
client.disconnect.called.should.equal(true)
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -10,258 +10,372 @@
|
|||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
require('chai').should();
|
||||
const sinon = require("sinon");
|
||||
const SandboxedModule = require('sandboxed-module');
|
||||
const path = require("path");
|
||||
const modulePath = '../../../app/js/DocumentUpdaterManager';
|
||||
require('chai').should()
|
||||
const sinon = require('sinon')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const path = require('path')
|
||||
const modulePath = '../../../app/js/DocumentUpdaterManager'
|
||||
|
||||
describe('DocumentUpdaterManager', function() {
|
||||
beforeEach(function() {
|
||||
let Timer;
|
||||
this.project_id = "project-id-923";
|
||||
this.doc_id = "doc-id-394";
|
||||
this.lines = ["one", "two", "three"];
|
||||
this.version = 42;
|
||||
this.settings = {
|
||||
apis: { documentupdater: {url: "http://doc-updater.example.com"}
|
||||
},
|
||||
redis: { documentupdater: {
|
||||
key_schema: {
|
||||
pendingUpdates({doc_id}) { return `PendingUpdates:${doc_id}`; }
|
||||
}
|
||||
}
|
||||
},
|
||||
maxUpdateSize: 7 * 1024 * 1024
|
||||
};
|
||||
this.rclient = {auth() {}};
|
||||
describe('DocumentUpdaterManager', function () {
|
||||
beforeEach(function () {
|
||||
let Timer
|
||||
this.project_id = 'project-id-923'
|
||||
this.doc_id = 'doc-id-394'
|
||||
this.lines = ['one', 'two', 'three']
|
||||
this.version = 42
|
||||
this.settings = {
|
||||
apis: { documentupdater: { url: 'http://doc-updater.example.com' } },
|
||||
redis: {
|
||||
documentupdater: {
|
||||
key_schema: {
|
||||
pendingUpdates({ doc_id }) {
|
||||
return `PendingUpdates:${doc_id}`
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
maxUpdateSize: 7 * 1024 * 1024
|
||||
}
|
||||
this.rclient = { auth() {} }
|
||||
|
||||
return this.DocumentUpdaterManager = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'settings-sharelatex':this.settings,
|
||||
'logger-sharelatex': (this.logger = {log: sinon.stub(), error: sinon.stub(), warn: sinon.stub()}),
|
||||
'request': (this.request = {}),
|
||||
'redis-sharelatex' : { createClient: () => this.rclient
|
||||
},
|
||||
'metrics-sharelatex': (this.Metrics = {
|
||||
summary: sinon.stub(),
|
||||
Timer: (Timer = class Timer {
|
||||
done() {}
|
||||
})
|
||||
})
|
||||
},
|
||||
globals: {
|
||||
JSON: (this.JSON = Object.create(JSON))
|
||||
}
|
||||
}
|
||||
);
|
||||
}); // avoid modifying JSON object directly
|
||||
return (this.DocumentUpdaterManager = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'settings-sharelatex': this.settings,
|
||||
'logger-sharelatex': (this.logger = {
|
||||
log: sinon.stub(),
|
||||
error: sinon.stub(),
|
||||
warn: sinon.stub()
|
||||
}),
|
||||
request: (this.request = {}),
|
||||
'redis-sharelatex': { createClient: () => this.rclient },
|
||||
'metrics-sharelatex': (this.Metrics = {
|
||||
summary: sinon.stub(),
|
||||
Timer: (Timer = class Timer {
|
||||
done() {}
|
||||
})
|
||||
})
|
||||
},
|
||||
globals: {
|
||||
JSON: (this.JSON = Object.create(JSON))
|
||||
}
|
||||
}))
|
||||
}) // avoid modifying JSON object directly
|
||||
|
||||
describe("getDocument", function() {
|
||||
beforeEach(function() {
|
||||
return this.callback = sinon.stub();
|
||||
});
|
||||
describe('getDocument', function () {
|
||||
beforeEach(function () {
|
||||
return (this.callback = sinon.stub())
|
||||
})
|
||||
|
||||
describe("successfully", function() {
|
||||
beforeEach(function() {
|
||||
this.body = JSON.stringify({
|
||||
lines: this.lines,
|
||||
version: this.version,
|
||||
ops: (this.ops = ["mock-op-1", "mock-op-2"]),
|
||||
ranges: (this.ranges = {"mock": "ranges"})});
|
||||
this.fromVersion = 2;
|
||||
this.request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, this.body);
|
||||
return this.DocumentUpdaterManager.getDocument(this.project_id, this.doc_id, this.fromVersion, this.callback);
|
||||
});
|
||||
describe('successfully', function () {
|
||||
beforeEach(function () {
|
||||
this.body = JSON.stringify({
|
||||
lines: this.lines,
|
||||
version: this.version,
|
||||
ops: (this.ops = ['mock-op-1', 'mock-op-2']),
|
||||
ranges: (this.ranges = { mock: 'ranges' })
|
||||
})
|
||||
this.fromVersion = 2
|
||||
this.request.get = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, { statusCode: 200 }, this.body)
|
||||
return this.DocumentUpdaterManager.getDocument(
|
||||
this.project_id,
|
||||
this.doc_id,
|
||||
this.fromVersion,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
it('should get the document from the document updater', function() {
|
||||
const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}?fromVersion=${this.fromVersion}`;
|
||||
return this.request.get.calledWith(url).should.equal(true);
|
||||
});
|
||||
it('should get the document from the document updater', function () {
|
||||
const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}?fromVersion=${this.fromVersion}`
|
||||
return this.request.get.calledWith(url).should.equal(true)
|
||||
})
|
||||
|
||||
return it("should call the callback with the lines, version, ranges and ops", function() {
|
||||
return this.callback.calledWith(null, this.lines, this.version, this.ranges, this.ops).should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should call the callback with the lines, version, ranges and ops', function () {
|
||||
return this.callback
|
||||
.calledWith(null, this.lines, this.version, this.ranges, this.ops)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("when the document updater API returns an error", function() {
|
||||
beforeEach(function() {
|
||||
this.request.get = sinon.stub().callsArgWith(1, (this.error = new Error("something went wrong")), null, null);
|
||||
return this.DocumentUpdaterManager.getDocument(this.project_id, this.doc_id, this.fromVersion, this.callback);
|
||||
});
|
||||
describe('when the document updater API returns an error', function () {
|
||||
beforeEach(function () {
|
||||
this.request.get = sinon
|
||||
.stub()
|
||||
.callsArgWith(
|
||||
1,
|
||||
(this.error = new Error('something went wrong')),
|
||||
null,
|
||||
null
|
||||
)
|
||||
return this.DocumentUpdaterManager.getDocument(
|
||||
this.project_id,
|
||||
this.doc_id,
|
||||
this.fromVersion,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
return it("should return an error to the callback", function() {
|
||||
return this.callback.calledWith(this.error).should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should return an error to the callback', function () {
|
||||
return this.callback.calledWith(this.error).should.equal(true)
|
||||
})
|
||||
})
|
||||
;[404, 422].forEach((statusCode) =>
|
||||
describe(`when the document updater returns a ${statusCode} status code`, function () {
|
||||
beforeEach(function () {
|
||||
this.request.get = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, { statusCode }, '')
|
||||
return this.DocumentUpdaterManager.getDocument(
|
||||
this.project_id,
|
||||
this.doc_id,
|
||||
this.fromVersion,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
[404, 422].forEach(statusCode => describe(`when the document updater returns a ${statusCode} status code`, function() {
|
||||
beforeEach(function() {
|
||||
this.request.get = sinon.stub().callsArgWith(1, null, { statusCode }, "");
|
||||
return this.DocumentUpdaterManager.getDocument(this.project_id, this.doc_id, this.fromVersion, this.callback);
|
||||
});
|
||||
return it('should return the callback with an error', function () {
|
||||
this.callback.called.should.equal(true)
|
||||
const err = this.callback.getCall(0).args[0]
|
||||
err.should.have.property('statusCode', statusCode)
|
||||
err.should.have.property(
|
||||
'message',
|
||||
'doc updater could not load requested ops'
|
||||
)
|
||||
this.logger.error.called.should.equal(false)
|
||||
return this.logger.warn.called.should.equal(true)
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
return it("should return the callback with an error", function() {
|
||||
this.callback.called.should.equal(true);
|
||||
const err = this.callback.getCall(0).args[0];
|
||||
err.should.have.property('statusCode', statusCode);
|
||||
err.should.have.property('message', "doc updater could not load requested ops");
|
||||
this.logger.error.called.should.equal(false);
|
||||
return this.logger.warn.called.should.equal(true);
|
||||
});
|
||||
}));
|
||||
return describe('when the document updater returns a failure error code', function () {
|
||||
beforeEach(function () {
|
||||
this.request.get = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, { statusCode: 500 }, '')
|
||||
return this.DocumentUpdaterManager.getDocument(
|
||||
this.project_id,
|
||||
this.doc_id,
|
||||
this.fromVersion,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
return describe("when the document updater returns a failure error code", function() {
|
||||
beforeEach(function() {
|
||||
this.request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "");
|
||||
return this.DocumentUpdaterManager.getDocument(this.project_id, this.doc_id, this.fromVersion, this.callback);
|
||||
});
|
||||
return it('should return the callback with an error', function () {
|
||||
this.callback.called.should.equal(true)
|
||||
const err = this.callback.getCall(0).args[0]
|
||||
err.should.have.property('statusCode', 500)
|
||||
err.should.have.property(
|
||||
'message',
|
||||
'doc updater returned a non-success status code: 500'
|
||||
)
|
||||
return this.logger.error.called.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return it("should return the callback with an error", function() {
|
||||
this.callback.called.should.equal(true);
|
||||
const err = this.callback.getCall(0).args[0];
|
||||
err.should.have.property('statusCode', 500);
|
||||
err.should.have.property('message', "doc updater returned a non-success status code: 500");
|
||||
return this.logger.error.called.should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('flushProjectToMongoAndDelete', function () {
|
||||
beforeEach(function () {
|
||||
return (this.callback = sinon.stub())
|
||||
})
|
||||
|
||||
describe('flushProjectToMongoAndDelete', function() {
|
||||
beforeEach(function() {
|
||||
return this.callback = sinon.stub();
|
||||
});
|
||||
describe('successfully', function () {
|
||||
beforeEach(function () {
|
||||
this.request.del = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, { statusCode: 204 }, '')
|
||||
return this.DocumentUpdaterManager.flushProjectToMongoAndDelete(
|
||||
this.project_id,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
describe("successfully", function() {
|
||||
beforeEach(function() {
|
||||
this.request.del = sinon.stub().callsArgWith(1, null, {statusCode: 204}, "");
|
||||
return this.DocumentUpdaterManager.flushProjectToMongoAndDelete(this.project_id, this.callback);
|
||||
});
|
||||
it('should delete the project from the document updater', function () {
|
||||
const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}?background=true`
|
||||
return this.request.del.calledWith(url).should.equal(true)
|
||||
})
|
||||
|
||||
it('should delete the project from the document updater', function() {
|
||||
const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}?background=true`;
|
||||
return this.request.del.calledWith(url).should.equal(true);
|
||||
});
|
||||
return it('should call the callback with no error', function () {
|
||||
return this.callback.calledWith(null).should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should call the callback with no error", function() {
|
||||
return this.callback.calledWith(null).should.equal(true);
|
||||
});
|
||||
});
|
||||
describe('when the document updater API returns an error', function () {
|
||||
beforeEach(function () {
|
||||
this.request.del = sinon
|
||||
.stub()
|
||||
.callsArgWith(
|
||||
1,
|
||||
(this.error = new Error('something went wrong')),
|
||||
null,
|
||||
null
|
||||
)
|
||||
return this.DocumentUpdaterManager.flushProjectToMongoAndDelete(
|
||||
this.project_id,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
describe("when the document updater API returns an error", function() {
|
||||
beforeEach(function() {
|
||||
this.request.del = sinon.stub().callsArgWith(1, (this.error = new Error("something went wrong")), null, null);
|
||||
return this.DocumentUpdaterManager.flushProjectToMongoAndDelete(this.project_id, this.callback);
|
||||
});
|
||||
return it('should return an error to the callback', function () {
|
||||
return this.callback.calledWith(this.error).should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should return an error to the callback", function() {
|
||||
return this.callback.calledWith(this.error).should.equal(true);
|
||||
});
|
||||
});
|
||||
return describe('when the document updater returns a failure error code', function () {
|
||||
beforeEach(function () {
|
||||
this.request.del = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, { statusCode: 500 }, '')
|
||||
return this.DocumentUpdaterManager.flushProjectToMongoAndDelete(
|
||||
this.project_id,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
return describe("when the document updater returns a failure error code", function() {
|
||||
beforeEach(function() {
|
||||
this.request.del = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "");
|
||||
return this.DocumentUpdaterManager.flushProjectToMongoAndDelete(this.project_id, this.callback);
|
||||
});
|
||||
return it('should return the callback with an error', function () {
|
||||
this.callback.called.should.equal(true)
|
||||
const err = this.callback.getCall(0).args[0]
|
||||
err.should.have.property('statusCode', 500)
|
||||
return err.should.have.property(
|
||||
'message',
|
||||
'document updater returned a failure status code: 500'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return it("should return the callback with an error", function() {
|
||||
this.callback.called.should.equal(true);
|
||||
const err = this.callback.getCall(0).args[0];
|
||||
err.should.have.property('statusCode', 500);
|
||||
return err.should.have.property('message', "document updater returned a failure status code: 500");
|
||||
});
|
||||
});
|
||||
});
|
||||
return describe('queueChange', function () {
|
||||
beforeEach(function () {
|
||||
this.change = {
|
||||
doc: '1234567890',
|
||||
op: [{ d: 'test', p: 345 }],
|
||||
v: 789
|
||||
}
|
||||
this.rclient.rpush = sinon.stub().yields()
|
||||
return (this.callback = sinon.stub())
|
||||
})
|
||||
|
||||
return describe('queueChange', function() {
|
||||
beforeEach(function() {
|
||||
this.change = {
|
||||
"doc":"1234567890",
|
||||
"op":[{"d":"test", "p":345}],
|
||||
"v": 789
|
||||
};
|
||||
this.rclient.rpush = sinon.stub().yields();
|
||||
return this.callback = sinon.stub();
|
||||
});
|
||||
describe('successfully', function () {
|
||||
beforeEach(function () {
|
||||
return this.DocumentUpdaterManager.queueChange(
|
||||
this.project_id,
|
||||
this.doc_id,
|
||||
this.change,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
describe("successfully", function() {
|
||||
beforeEach(function() {
|
||||
return this.DocumentUpdaterManager.queueChange(this.project_id, this.doc_id, this.change, this.callback);
|
||||
});
|
||||
it('should push the change', function () {
|
||||
return this.rclient.rpush
|
||||
.calledWith(
|
||||
`PendingUpdates:${this.doc_id}`,
|
||||
JSON.stringify(this.change)
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it("should push the change", function() {
|
||||
return this.rclient.rpush
|
||||
.calledWith(`PendingUpdates:${this.doc_id}`, JSON.stringify(this.change))
|
||||
.should.equal(true);
|
||||
});
|
||||
return it('should notify the doc updater of the change via the pending-updates-list queue', function () {
|
||||
return this.rclient.rpush
|
||||
.calledWith(
|
||||
'pending-updates-list',
|
||||
`${this.project_id}:${this.doc_id}`
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should notify the doc updater of the change via the pending-updates-list queue", function() {
|
||||
return this.rclient.rpush
|
||||
.calledWith("pending-updates-list", `${this.project_id}:${this.doc_id}`)
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
describe('with error talking to redis during rpush', function () {
|
||||
beforeEach(function () {
|
||||
this.rclient.rpush = sinon
|
||||
.stub()
|
||||
.yields(new Error('something went wrong'))
|
||||
return this.DocumentUpdaterManager.queueChange(
|
||||
this.project_id,
|
||||
this.doc_id,
|
||||
this.change,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
describe("with error talking to redis during rpush", function() {
|
||||
beforeEach(function() {
|
||||
this.rclient.rpush = sinon.stub().yields(new Error("something went wrong"));
|
||||
return this.DocumentUpdaterManager.queueChange(this.project_id, this.doc_id, this.change, this.callback);
|
||||
});
|
||||
return it('should return an error', function () {
|
||||
return this.callback
|
||||
.calledWithExactly(sinon.match(Error))
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should return an error", function() {
|
||||
return this.callback.calledWithExactly(sinon.match(Error)).should.equal(true);
|
||||
});
|
||||
});
|
||||
describe('with null byte corruption', function () {
|
||||
beforeEach(function () {
|
||||
this.JSON.stringify = () => '["bad bytes! \u0000 <- here"]'
|
||||
return this.DocumentUpdaterManager.queueChange(
|
||||
this.project_id,
|
||||
this.doc_id,
|
||||
this.change,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
describe("with null byte corruption", function() {
|
||||
beforeEach(function() {
|
||||
this.JSON.stringify = () => '["bad bytes! \u0000 <- here"]';
|
||||
return this.DocumentUpdaterManager.queueChange(this.project_id, this.doc_id, this.change, this.callback);
|
||||
});
|
||||
it('should return an error', function () {
|
||||
return this.callback
|
||||
.calledWithExactly(sinon.match(Error))
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it("should return an error", function() {
|
||||
return this.callback.calledWithExactly(sinon.match(Error)).should.equal(true);
|
||||
});
|
||||
return it('should not push the change onto the pending-updates-list queue', function () {
|
||||
return this.rclient.rpush.called.should.equal(false)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should not push the change onto the pending-updates-list queue", function() {
|
||||
return this.rclient.rpush.called.should.equal(false);
|
||||
});
|
||||
});
|
||||
describe('when the update is too large', function () {
|
||||
beforeEach(function () {
|
||||
this.change = {
|
||||
op: { p: 12, t: 'update is too large'.repeat(1024 * 400) }
|
||||
}
|
||||
return this.DocumentUpdaterManager.queueChange(
|
||||
this.project_id,
|
||||
this.doc_id,
|
||||
this.change,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
describe("when the update is too large", function() {
|
||||
beforeEach(function() {
|
||||
this.change = {op: {p: 12,t: "update is too large".repeat(1024 * 400)}};
|
||||
return this.DocumentUpdaterManager.queueChange(this.project_id, this.doc_id, this.change, this.callback);
|
||||
});
|
||||
it('should return an error', function () {
|
||||
return this.callback
|
||||
.calledWithExactly(sinon.match(Error))
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it("should return an error", function() {
|
||||
return this.callback.calledWithExactly(sinon.match(Error)).should.equal(true);
|
||||
});
|
||||
it('should add the size to the error', function () {
|
||||
return this.callback.args[0][0].updateSize.should.equal(7782422)
|
||||
})
|
||||
|
||||
it("should add the size to the error", function() {
|
||||
return this.callback.args[0][0].updateSize.should.equal(7782422);
|
||||
});
|
||||
return it('should not push the change onto the pending-updates-list queue', function () {
|
||||
return this.rclient.rpush.called.should.equal(false)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should not push the change onto the pending-updates-list queue", function() {
|
||||
return this.rclient.rpush.called.should.equal(false);
|
||||
});
|
||||
});
|
||||
return describe('with invalid keys', function () {
|
||||
beforeEach(function () {
|
||||
this.change = {
|
||||
op: [{ d: 'test', p: 345 }],
|
||||
version: 789 // not a valid key
|
||||
}
|
||||
return this.DocumentUpdaterManager.queueChange(
|
||||
this.project_id,
|
||||
this.doc_id,
|
||||
this.change,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
return describe("with invalid keys", function() {
|
||||
beforeEach(function() {
|
||||
this.change = {
|
||||
"op":[{"d":"test", "p":345}],
|
||||
"version": 789 // not a valid key
|
||||
};
|
||||
return this.DocumentUpdaterManager.queueChange(this.project_id, this.doc_id, this.change, this.callback);
|
||||
});
|
||||
|
||||
return it("should remove the invalid keys from the change", function() {
|
||||
return this.rclient.rpush
|
||||
.calledWith(`PendingUpdates:${this.doc_id}`, JSON.stringify({op:this.change.op}))
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should remove the invalid keys from the change', function () {
|
||||
return this.rclient.rpush
|
||||
.calledWith(
|
||||
`PendingUpdates:${this.doc_id}`,
|
||||
JSON.stringify({ op: this.change.op })
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -9,111 +9,124 @@
|
|||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const should = require('chai').should();
|
||||
const sinon = require("sinon");
|
||||
const SandboxedModule = require('sandboxed-module');
|
||||
const path = require("path");
|
||||
const modulePath = path.join(__dirname, "../../../app/js/DrainManager");
|
||||
const should = require('chai').should()
|
||||
const sinon = require('sinon')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const path = require('path')
|
||||
const modulePath = path.join(__dirname, '../../../app/js/DrainManager')
|
||||
|
||||
describe("DrainManager", function() {
|
||||
beforeEach(function() {
|
||||
this.DrainManager = SandboxedModule.require(modulePath, { requires: {
|
||||
"logger-sharelatex": (this.logger = {log: sinon.stub()})
|
||||
}
|
||||
}
|
||||
);
|
||||
return this.io = {
|
||||
sockets: {
|
||||
clients: sinon.stub()
|
||||
}
|
||||
};
|
||||
});
|
||||
describe('DrainManager', function () {
|
||||
beforeEach(function () {
|
||||
this.DrainManager = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'logger-sharelatex': (this.logger = { log: sinon.stub() })
|
||||
}
|
||||
})
|
||||
return (this.io = {
|
||||
sockets: {
|
||||
clients: sinon.stub()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("startDrainTimeWindow", function() {
|
||||
beforeEach(function() {
|
||||
this.clients = [];
|
||||
for (let i = 0; i <= 5399; i++) {
|
||||
this.clients[i] = {
|
||||
id: i,
|
||||
emit: sinon.stub()
|
||||
};
|
||||
}
|
||||
this.io.sockets.clients.returns(this.clients);
|
||||
return this.DrainManager.startDrain = sinon.stub();
|
||||
});
|
||||
describe('startDrainTimeWindow', function () {
|
||||
beforeEach(function () {
|
||||
this.clients = []
|
||||
for (let i = 0; i <= 5399; i++) {
|
||||
this.clients[i] = {
|
||||
id: i,
|
||||
emit: sinon.stub()
|
||||
}
|
||||
}
|
||||
this.io.sockets.clients.returns(this.clients)
|
||||
return (this.DrainManager.startDrain = sinon.stub())
|
||||
})
|
||||
|
||||
return it("should set a drain rate fast enough", function(done){
|
||||
this.DrainManager.startDrainTimeWindow(this.io, 9);
|
||||
this.DrainManager.startDrain.calledWith(this.io, 10).should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
return it('should set a drain rate fast enough', function (done) {
|
||||
this.DrainManager.startDrainTimeWindow(this.io, 9)
|
||||
this.DrainManager.startDrain.calledWith(this.io, 10).should.equal(true)
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
return describe('reconnectNClients', function () {
|
||||
beforeEach(function () {
|
||||
this.clients = []
|
||||
for (let i = 0; i <= 9; i++) {
|
||||
this.clients[i] = {
|
||||
id: i,
|
||||
emit: sinon.stub()
|
||||
}
|
||||
}
|
||||
return this.io.sockets.clients.returns(this.clients)
|
||||
})
|
||||
|
||||
return describe("reconnectNClients", function() {
|
||||
beforeEach(function() {
|
||||
this.clients = [];
|
||||
for (let i = 0; i <= 9; i++) {
|
||||
this.clients[i] = {
|
||||
id: i,
|
||||
emit: sinon.stub()
|
||||
};
|
||||
}
|
||||
return this.io.sockets.clients.returns(this.clients);
|
||||
});
|
||||
return describe('after first pass', function () {
|
||||
beforeEach(function () {
|
||||
return this.DrainManager.reconnectNClients(this.io, 3)
|
||||
})
|
||||
|
||||
return describe("after first pass", function() {
|
||||
beforeEach(function() {
|
||||
return this.DrainManager.reconnectNClients(this.io, 3);
|
||||
});
|
||||
|
||||
it("should reconnect the first 3 clients", function() {
|
||||
return [0, 1, 2].map((i) =>
|
||||
this.clients[i].emit.calledWith("reconnectGracefully").should.equal(true));
|
||||
});
|
||||
|
||||
it("should not reconnect any more clients", function() {
|
||||
return [3, 4, 5, 6, 7, 8, 9].map((i) =>
|
||||
this.clients[i].emit.calledWith("reconnectGracefully").should.equal(false));
|
||||
});
|
||||
|
||||
return describe("after second pass", function() {
|
||||
beforeEach(function() {
|
||||
return this.DrainManager.reconnectNClients(this.io, 3);
|
||||
});
|
||||
|
||||
it("should reconnect the next 3 clients", function() {
|
||||
return [3, 4, 5].map((i) =>
|
||||
this.clients[i].emit.calledWith("reconnectGracefully").should.equal(true));
|
||||
});
|
||||
|
||||
it("should not reconnect any more clients", function() {
|
||||
return [6, 7, 8, 9].map((i) =>
|
||||
this.clients[i].emit.calledWith("reconnectGracefully").should.equal(false));
|
||||
});
|
||||
|
||||
it("should not reconnect the first 3 clients again", function() {
|
||||
return [0, 1, 2].map((i) =>
|
||||
this.clients[i].emit.calledOnce.should.equal(true));
|
||||
});
|
||||
|
||||
return describe("after final pass", function() {
|
||||
beforeEach(function() {
|
||||
return this.DrainManager.reconnectNClients(this.io, 100);
|
||||
});
|
||||
|
||||
it("should not reconnect the first 6 clients again", function() {
|
||||
return [0, 1, 2, 3, 4, 5].map((i) =>
|
||||
this.clients[i].emit.calledOnce.should.equal(true));
|
||||
});
|
||||
|
||||
return it("should log out that it reached the end", function() {
|
||||
return this.logger.log
|
||||
.calledWith("All clients have been told to reconnectGracefully")
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should reconnect the first 3 clients', function () {
|
||||
return [0, 1, 2].map((i) =>
|
||||
this.clients[i].emit
|
||||
.calledWith('reconnectGracefully')
|
||||
.should.equal(true)
|
||||
)
|
||||
})
|
||||
|
||||
it('should not reconnect any more clients', function () {
|
||||
return [3, 4, 5, 6, 7, 8, 9].map((i) =>
|
||||
this.clients[i].emit
|
||||
.calledWith('reconnectGracefully')
|
||||
.should.equal(false)
|
||||
)
|
||||
})
|
||||
|
||||
return describe('after second pass', function () {
|
||||
beforeEach(function () {
|
||||
return this.DrainManager.reconnectNClients(this.io, 3)
|
||||
})
|
||||
|
||||
it('should reconnect the next 3 clients', function () {
|
||||
return [3, 4, 5].map((i) =>
|
||||
this.clients[i].emit
|
||||
.calledWith('reconnectGracefully')
|
||||
.should.equal(true)
|
||||
)
|
||||
})
|
||||
|
||||
it('should not reconnect any more clients', function () {
|
||||
return [6, 7, 8, 9].map((i) =>
|
||||
this.clients[i].emit
|
||||
.calledWith('reconnectGracefully')
|
||||
.should.equal(false)
|
||||
)
|
||||
})
|
||||
|
||||
it('should not reconnect the first 3 clients again', function () {
|
||||
return [0, 1, 2].map((i) =>
|
||||
this.clients[i].emit.calledOnce.should.equal(true)
|
||||
)
|
||||
})
|
||||
|
||||
return describe('after final pass', function () {
|
||||
beforeEach(function () {
|
||||
return this.DrainManager.reconnectNClients(this.io, 100)
|
||||
})
|
||||
|
||||
it('should not reconnect the first 6 clients again', function () {
|
||||
return [0, 1, 2, 3, 4, 5].map((i) =>
|
||||
this.clients[i].emit.calledOnce.should.equal(true)
|
||||
)
|
||||
})
|
||||
|
||||
return it('should log out that it reached the end', function () {
|
||||
return this.logger.log
|
||||
.calledWith('All clients have been told to reconnectGracefully')
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -8,99 +8,150 @@
|
|||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
require('chai').should();
|
||||
const {
|
||||
expect
|
||||
} = require("chai");
|
||||
const SandboxedModule = require('sandboxed-module');
|
||||
const modulePath = '../../../app/js/EventLogger';
|
||||
const sinon = require("sinon");
|
||||
const tk = require("timekeeper");
|
||||
require('chai').should()
|
||||
const { expect } = require('chai')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const modulePath = '../../../app/js/EventLogger'
|
||||
const sinon = require('sinon')
|
||||
const tk = require('timekeeper')
|
||||
|
||||
describe('EventLogger', function() {
|
||||
beforeEach(function() {
|
||||
this.start = Date.now();
|
||||
tk.freeze(new Date(this.start));
|
||||
this.EventLogger = SandboxedModule.require(modulePath, { requires: {
|
||||
"logger-sharelatex": (this.logger = {error: sinon.stub(), warn: sinon.stub()}),
|
||||
"metrics-sharelatex": (this.metrics = {inc: sinon.stub()})
|
||||
}
|
||||
});
|
||||
this.channel = "applied-ops";
|
||||
this.id_1 = "random-hostname:abc-1";
|
||||
this.message_1 = "message-1";
|
||||
this.id_2 = "random-hostname:abc-2";
|
||||
return this.message_2 = "message-2";
|
||||
});
|
||||
describe('EventLogger', function () {
|
||||
beforeEach(function () {
|
||||
this.start = Date.now()
|
||||
tk.freeze(new Date(this.start))
|
||||
this.EventLogger = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'logger-sharelatex': (this.logger = {
|
||||
error: sinon.stub(),
|
||||
warn: sinon.stub()
|
||||
}),
|
||||
'metrics-sharelatex': (this.metrics = { inc: sinon.stub() })
|
||||
}
|
||||
})
|
||||
this.channel = 'applied-ops'
|
||||
this.id_1 = 'random-hostname:abc-1'
|
||||
this.message_1 = 'message-1'
|
||||
this.id_2 = 'random-hostname:abc-2'
|
||||
return (this.message_2 = 'message-2')
|
||||
})
|
||||
|
||||
afterEach(function() { return tk.reset(); });
|
||||
afterEach(function () {
|
||||
return tk.reset()
|
||||
})
|
||||
|
||||
return describe('checkEventOrder', function() {
|
||||
return describe('checkEventOrder', function () {
|
||||
describe('when the events are in order', function () {
|
||||
beforeEach(function () {
|
||||
this.EventLogger.checkEventOrder(
|
||||
this.channel,
|
||||
this.id_1,
|
||||
this.message_1
|
||||
)
|
||||
return (this.status = this.EventLogger.checkEventOrder(
|
||||
this.channel,
|
||||
this.id_2,
|
||||
this.message_2
|
||||
))
|
||||
})
|
||||
|
||||
describe('when the events are in order', function() {
|
||||
beforeEach(function() {
|
||||
this.EventLogger.checkEventOrder(this.channel, this.id_1, this.message_1);
|
||||
return this.status = this.EventLogger.checkEventOrder(this.channel, this.id_2, this.message_2);
|
||||
});
|
||||
it('should accept events in order', function () {
|
||||
return expect(this.status).to.be.undefined
|
||||
})
|
||||
|
||||
it('should accept events in order', function() {
|
||||
return expect(this.status).to.be.undefined;
|
||||
});
|
||||
return it('should increment the valid event metric', function () {
|
||||
return this.metrics.inc.calledWith(`event.${this.channel}.valid`, 1)
|
||||
.should.equal.true
|
||||
})
|
||||
})
|
||||
|
||||
return it('should increment the valid event metric', function() {
|
||||
return this.metrics.inc.calledWith(`event.${this.channel}.valid`, 1)
|
||||
.should.equal.true;
|
||||
});
|
||||
});
|
||||
describe('when there is a duplicate events', function () {
|
||||
beforeEach(function () {
|
||||
this.EventLogger.checkEventOrder(
|
||||
this.channel,
|
||||
this.id_1,
|
||||
this.message_1
|
||||
)
|
||||
return (this.status = this.EventLogger.checkEventOrder(
|
||||
this.channel,
|
||||
this.id_1,
|
||||
this.message_1
|
||||
))
|
||||
})
|
||||
|
||||
describe('when there is a duplicate events', function() {
|
||||
beforeEach(function() {
|
||||
this.EventLogger.checkEventOrder(this.channel, this.id_1, this.message_1);
|
||||
return this.status = this.EventLogger.checkEventOrder(this.channel, this.id_1, this.message_1);
|
||||
});
|
||||
it('should return "duplicate" for the same event', function () {
|
||||
return expect(this.status).to.equal('duplicate')
|
||||
})
|
||||
|
||||
it('should return "duplicate" for the same event', function() {
|
||||
return expect(this.status).to.equal("duplicate");
|
||||
});
|
||||
return it('should increment the duplicate event metric', function () {
|
||||
return this.metrics.inc.calledWith(`event.${this.channel}.duplicate`, 1)
|
||||
.should.equal.true
|
||||
})
|
||||
})
|
||||
|
||||
return it('should increment the duplicate event metric', function() {
|
||||
return this.metrics.inc.calledWith(`event.${this.channel}.duplicate`, 1)
|
||||
.should.equal.true;
|
||||
});
|
||||
});
|
||||
describe('when there are out of order events', function () {
|
||||
beforeEach(function () {
|
||||
this.EventLogger.checkEventOrder(
|
||||
this.channel,
|
||||
this.id_1,
|
||||
this.message_1
|
||||
)
|
||||
this.EventLogger.checkEventOrder(
|
||||
this.channel,
|
||||
this.id_2,
|
||||
this.message_2
|
||||
)
|
||||
return (this.status = this.EventLogger.checkEventOrder(
|
||||
this.channel,
|
||||
this.id_1,
|
||||
this.message_1
|
||||
))
|
||||
})
|
||||
|
||||
describe('when there are out of order events', function() {
|
||||
beforeEach(function() {
|
||||
this.EventLogger.checkEventOrder(this.channel, this.id_1, this.message_1);
|
||||
this.EventLogger.checkEventOrder(this.channel, this.id_2, this.message_2);
|
||||
return this.status = this.EventLogger.checkEventOrder(this.channel, this.id_1, this.message_1);
|
||||
});
|
||||
it('should return "out-of-order" for the event', function () {
|
||||
return expect(this.status).to.equal('out-of-order')
|
||||
})
|
||||
|
||||
it('should return "out-of-order" for the event', function() {
|
||||
return expect(this.status).to.equal("out-of-order");
|
||||
});
|
||||
return it('should increment the out-of-order event metric', function () {
|
||||
return this.metrics.inc.calledWith(
|
||||
`event.${this.channel}.out-of-order`,
|
||||
1
|
||||
).should.equal.true
|
||||
})
|
||||
})
|
||||
|
||||
return it('should increment the out-of-order event metric', function() {
|
||||
return this.metrics.inc.calledWith(`event.${this.channel}.out-of-order`, 1)
|
||||
.should.equal.true;
|
||||
});
|
||||
});
|
||||
|
||||
return describe('after MAX_STALE_TIME_IN_MS', function() { return it('should flush old entries', function() {
|
||||
let status;
|
||||
this.EventLogger.MAX_EVENTS_BEFORE_CLEAN = 10;
|
||||
this.EventLogger.checkEventOrder(this.channel, this.id_1, this.message_1);
|
||||
for (let i = 1; i <= 8; i++) {
|
||||
status = this.EventLogger.checkEventOrder(this.channel, this.id_1, this.message_1);
|
||||
expect(status).to.equal("duplicate");
|
||||
}
|
||||
// the next event should flush the old entries aboce
|
||||
this.EventLogger.MAX_STALE_TIME_IN_MS=1000;
|
||||
tk.freeze(new Date(this.start + (5 * 1000)));
|
||||
// because we flushed the entries this should not be a duplicate
|
||||
this.EventLogger.checkEventOrder(this.channel, 'other-1', this.message_2);
|
||||
status = this.EventLogger.checkEventOrder(this.channel, this.id_1, this.message_1);
|
||||
return expect(status).to.be.undefined;
|
||||
}); });
|
||||
});
|
||||
});
|
||||
return describe('after MAX_STALE_TIME_IN_MS', function () {
|
||||
return it('should flush old entries', function () {
|
||||
let status
|
||||
this.EventLogger.MAX_EVENTS_BEFORE_CLEAN = 10
|
||||
this.EventLogger.checkEventOrder(
|
||||
this.channel,
|
||||
this.id_1,
|
||||
this.message_1
|
||||
)
|
||||
for (let i = 1; i <= 8; i++) {
|
||||
status = this.EventLogger.checkEventOrder(
|
||||
this.channel,
|
||||
this.id_1,
|
||||
this.message_1
|
||||
)
|
||||
expect(status).to.equal('duplicate')
|
||||
}
|
||||
// the next event should flush the old entries aboce
|
||||
this.EventLogger.MAX_STALE_TIME_IN_MS = 1000
|
||||
tk.freeze(new Date(this.start + 5 * 1000))
|
||||
// because we flushed the entries this should not be a duplicate
|
||||
this.EventLogger.checkEventOrder(
|
||||
this.channel,
|
||||
'other-1',
|
||||
this.message_2
|
||||
)
|
||||
status = this.EventLogger.checkEventOrder(
|
||||
this.channel,
|
||||
this.id_1,
|
||||
this.message_1
|
||||
)
|
||||
return expect(status).to.be.undefined
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -10,357 +10,410 @@
|
|||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const chai = require('chai');
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
const should = chai.should();
|
||||
const sinon = require("sinon");
|
||||
const modulePath = "../../../app/js/RoomManager.js";
|
||||
const SandboxedModule = require('sandboxed-module');
|
||||
const chai = require('chai')
|
||||
const { expect } = chai
|
||||
const should = chai.should()
|
||||
const sinon = require('sinon')
|
||||
const modulePath = '../../../app/js/RoomManager.js'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
|
||||
describe('RoomManager', function() {
|
||||
beforeEach(function() {
|
||||
this.project_id = "project-id-123";
|
||||
this.doc_id = "doc-id-456";
|
||||
this.other_doc_id = "doc-id-789";
|
||||
this.client = {namespace: {name: ''}, id: "first-client"};
|
||||
this.RoomManager = SandboxedModule.require(modulePath, { requires: {
|
||||
"settings-sharelatex": (this.settings = {}),
|
||||
"logger-sharelatex": (this.logger = { log: sinon.stub(), warn: sinon.stub(), error: sinon.stub() }),
|
||||
"metrics-sharelatex": (this.metrics = { gauge: sinon.stub() })
|
||||
}
|
||||
});
|
||||
this.RoomManager._clientsInRoom = sinon.stub();
|
||||
this.RoomManager._clientAlreadyInRoom = sinon.stub();
|
||||
this.RoomEvents = this.RoomManager.eventSource();
|
||||
sinon.spy(this.RoomEvents, 'emit');
|
||||
return sinon.spy(this.RoomEvents, 'once');
|
||||
});
|
||||
|
||||
describe("emitOnCompletion", function() { return describe("when a subscribe errors", function() {
|
||||
afterEach(function() {
|
||||
return process.removeListener("unhandledRejection", this.onUnhandled);
|
||||
});
|
||||
describe('RoomManager', function () {
|
||||
beforeEach(function () {
|
||||
this.project_id = 'project-id-123'
|
||||
this.doc_id = 'doc-id-456'
|
||||
this.other_doc_id = 'doc-id-789'
|
||||
this.client = { namespace: { name: '' }, id: 'first-client' }
|
||||
this.RoomManager = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'settings-sharelatex': (this.settings = {}),
|
||||
'logger-sharelatex': (this.logger = {
|
||||
log: sinon.stub(),
|
||||
warn: sinon.stub(),
|
||||
error: sinon.stub()
|
||||
}),
|
||||
'metrics-sharelatex': (this.metrics = { gauge: sinon.stub() })
|
||||
}
|
||||
})
|
||||
this.RoomManager._clientsInRoom = sinon.stub()
|
||||
this.RoomManager._clientAlreadyInRoom = sinon.stub()
|
||||
this.RoomEvents = this.RoomManager.eventSource()
|
||||
sinon.spy(this.RoomEvents, 'emit')
|
||||
return sinon.spy(this.RoomEvents, 'once')
|
||||
})
|
||||
|
||||
beforeEach(function(done) {
|
||||
this.onUnhandled = error => {
|
||||
this.unhandledError = error;
|
||||
return done(new Error(`unhandledRejection: ${error.message}`));
|
||||
};
|
||||
process.on("unhandledRejection", this.onUnhandled);
|
||||
describe('emitOnCompletion', function () {
|
||||
return describe('when a subscribe errors', function () {
|
||||
afterEach(function () {
|
||||
return process.removeListener('unhandledRejection', this.onUnhandled)
|
||||
})
|
||||
|
||||
let reject;
|
||||
const subscribePromise = new Promise((_, r) => reject = r);
|
||||
const promises = [subscribePromise];
|
||||
const eventName = "project-subscribed-123";
|
||||
this.RoomEvents.once(eventName, () => setTimeout(done, 100));
|
||||
this.RoomManager.emitOnCompletion(promises, eventName);
|
||||
return setTimeout(() => reject(new Error("subscribe failed")));
|
||||
});
|
||||
beforeEach(function (done) {
|
||||
this.onUnhandled = (error) => {
|
||||
this.unhandledError = error
|
||||
return done(new Error(`unhandledRejection: ${error.message}`))
|
||||
}
|
||||
process.on('unhandledRejection', this.onUnhandled)
|
||||
|
||||
return it("should keep going", function() {
|
||||
return expect(this.unhandledError).to.not.exist;
|
||||
});
|
||||
}); });
|
||||
let reject
|
||||
const subscribePromise = new Promise((_, r) => (reject = r))
|
||||
const promises = [subscribePromise]
|
||||
const eventName = 'project-subscribed-123'
|
||||
this.RoomEvents.once(eventName, () => setTimeout(done, 100))
|
||||
this.RoomManager.emitOnCompletion(promises, eventName)
|
||||
return setTimeout(() => reject(new Error('subscribe failed')))
|
||||
})
|
||||
|
||||
describe("joinProject", function() {
|
||||
|
||||
describe("when the project room is empty", function() {
|
||||
return it('should keep going', function () {
|
||||
return expect(this.unhandledError).to.not.exist
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function(done) {
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.project_id)
|
||||
.onFirstCall().returns(0);
|
||||
this.client.join = sinon.stub();
|
||||
this.callback = sinon.stub();
|
||||
this.RoomEvents.on('project-active', id => {
|
||||
return setTimeout(() => {
|
||||
return this.RoomEvents.emit(`project-subscribed-${id}`);
|
||||
}
|
||||
, 100);
|
||||
});
|
||||
return this.RoomManager.joinProject(this.client, this.project_id, err => {
|
||||
this.callback(err);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
describe('joinProject', function () {
|
||||
describe('when the project room is empty', function () {
|
||||
beforeEach(function (done) {
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.project_id)
|
||||
.onFirstCall()
|
||||
.returns(0)
|
||||
this.client.join = sinon.stub()
|
||||
this.callback = sinon.stub()
|
||||
this.RoomEvents.on('project-active', (id) => {
|
||||
return setTimeout(() => {
|
||||
return this.RoomEvents.emit(`project-subscribed-${id}`)
|
||||
}, 100)
|
||||
})
|
||||
return this.RoomManager.joinProject(
|
||||
this.client,
|
||||
this.project_id,
|
||||
(err) => {
|
||||
this.callback(err)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should emit a 'project-active' event with the id", function() {
|
||||
return this.RoomEvents.emit.calledWithExactly('project-active', this.project_id).should.equal(true);
|
||||
});
|
||||
it("should emit a 'project-active' event with the id", function () {
|
||||
return this.RoomEvents.emit
|
||||
.calledWithExactly('project-active', this.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it("should listen for the 'project-subscribed-id' event", function() {
|
||||
return this.RoomEvents.once.calledWith(`project-subscribed-${this.project_id}`).should.equal(true);
|
||||
});
|
||||
it("should listen for the 'project-subscribed-id' event", function () {
|
||||
return this.RoomEvents.once
|
||||
.calledWith(`project-subscribed-${this.project_id}`)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
return it("should join the room using the id", function() {
|
||||
return this.client.join.calledWithExactly(this.project_id).should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should join the room using the id', function () {
|
||||
return this.client.join
|
||||
.calledWithExactly(this.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return describe("when there are other clients in the project room", function() {
|
||||
return describe('when there are other clients in the project room', function () {
|
||||
beforeEach(function () {
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.project_id)
|
||||
.onFirstCall()
|
||||
.returns(123)
|
||||
.onSecondCall()
|
||||
.returns(124)
|
||||
this.client.join = sinon.stub()
|
||||
return this.RoomManager.joinProject(this.client, this.project_id)
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.project_id)
|
||||
.onFirstCall().returns(123)
|
||||
.onSecondCall().returns(124);
|
||||
this.client.join = sinon.stub();
|
||||
return this.RoomManager.joinProject(this.client, this.project_id);
|
||||
});
|
||||
it('should join the room using the id', function () {
|
||||
return this.client.join.called.should.equal(true)
|
||||
})
|
||||
|
||||
it("should join the room using the id", function() {
|
||||
return this.client.join.called.should.equal(true);
|
||||
});
|
||||
return it('should not emit any events', function () {
|
||||
return this.RoomEvents.emit.called.should.equal(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return it("should not emit any events", function() {
|
||||
return this.RoomEvents.emit.called.should.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('joinDoc', function () {
|
||||
describe('when the doc room is empty', function () {
|
||||
beforeEach(function (done) {
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onFirstCall()
|
||||
.returns(0)
|
||||
this.client.join = sinon.stub()
|
||||
this.callback = sinon.stub()
|
||||
this.RoomEvents.on('doc-active', (id) => {
|
||||
return setTimeout(() => {
|
||||
return this.RoomEvents.emit(`doc-subscribed-${id}`)
|
||||
}, 100)
|
||||
})
|
||||
return this.RoomManager.joinDoc(this.client, this.doc_id, (err) => {
|
||||
this.callback(err)
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
it("should emit a 'doc-active' event with the id", function () {
|
||||
return this.RoomEvents.emit
|
||||
.calledWithExactly('doc-active', this.doc_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
describe("joinDoc", function() {
|
||||
it("should listen for the 'doc-subscribed-id' event", function () {
|
||||
return this.RoomEvents.once
|
||||
.calledWith(`doc-subscribed-${this.doc_id}`)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
describe("when the doc room is empty", function() {
|
||||
return it('should join the room using the id', function () {
|
||||
return this.client.join
|
||||
.calledWithExactly(this.doc_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function(done) {
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onFirstCall().returns(0);
|
||||
this.client.join = sinon.stub();
|
||||
this.callback = sinon.stub();
|
||||
this.RoomEvents.on('doc-active', id => {
|
||||
return setTimeout(() => {
|
||||
return this.RoomEvents.emit(`doc-subscribed-${id}`);
|
||||
}
|
||||
, 100);
|
||||
});
|
||||
return this.RoomManager.joinDoc(this.client, this.doc_id, err => {
|
||||
this.callback(err);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
return describe('when there are other clients in the doc room', function () {
|
||||
beforeEach(function () {
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onFirstCall()
|
||||
.returns(123)
|
||||
.onSecondCall()
|
||||
.returns(124)
|
||||
this.client.join = sinon.stub()
|
||||
return this.RoomManager.joinDoc(this.client, this.doc_id)
|
||||
})
|
||||
|
||||
it("should emit a 'doc-active' event with the id", function() {
|
||||
return this.RoomEvents.emit.calledWithExactly('doc-active', this.doc_id).should.equal(true);
|
||||
});
|
||||
it('should join the room using the id', function () {
|
||||
return this.client.join.called.should.equal(true)
|
||||
})
|
||||
|
||||
it("should listen for the 'doc-subscribed-id' event", function() {
|
||||
return this.RoomEvents.once.calledWith(`doc-subscribed-${this.doc_id}`).should.equal(true);
|
||||
});
|
||||
return it('should not emit any events', function () {
|
||||
return this.RoomEvents.emit.called.should.equal(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return it("should join the room using the id", function() {
|
||||
return this.client.join.calledWithExactly(this.doc_id).should.equal(true);
|
||||
});
|
||||
});
|
||||
describe('leaveDoc', function () {
|
||||
describe('when doc room will be empty after this client has left', function () {
|
||||
beforeEach(function () {
|
||||
this.RoomManager._clientAlreadyInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.returns(true)
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onCall(0)
|
||||
.returns(0)
|
||||
this.client.leave = sinon.stub()
|
||||
return this.RoomManager.leaveDoc(this.client, this.doc_id)
|
||||
})
|
||||
|
||||
return describe("when there are other clients in the doc room", function() {
|
||||
it('should leave the room using the id', function () {
|
||||
return this.client.leave
|
||||
.calledWithExactly(this.doc_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onFirstCall().returns(123)
|
||||
.onSecondCall().returns(124);
|
||||
this.client.join = sinon.stub();
|
||||
return this.RoomManager.joinDoc(this.client, this.doc_id);
|
||||
});
|
||||
return it("should emit a 'doc-empty' event with the id", function () {
|
||||
return this.RoomEvents.emit
|
||||
.calledWithExactly('doc-empty', this.doc_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
it("should join the room using the id", function() {
|
||||
return this.client.join.called.should.equal(true);
|
||||
});
|
||||
describe('when there are other clients in the doc room', function () {
|
||||
beforeEach(function () {
|
||||
this.RoomManager._clientAlreadyInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.returns(true)
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onCall(0)
|
||||
.returns(123)
|
||||
this.client.leave = sinon.stub()
|
||||
return this.RoomManager.leaveDoc(this.client, this.doc_id)
|
||||
})
|
||||
|
||||
return it("should not emit any events", function() {
|
||||
return this.RoomEvents.emit.called.should.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should leave the room using the id', function () {
|
||||
return this.client.leave
|
||||
.calledWithExactly(this.doc_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
return it('should not emit any events', function () {
|
||||
return this.RoomEvents.emit.called.should.equal(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("leaveDoc", function() {
|
||||
return describe('when the client is not in the doc room', function () {
|
||||
beforeEach(function () {
|
||||
this.RoomManager._clientAlreadyInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.returns(false)
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onCall(0)
|
||||
.returns(0)
|
||||
this.client.leave = sinon.stub()
|
||||
return this.RoomManager.leaveDoc(this.client, this.doc_id)
|
||||
})
|
||||
|
||||
describe("when doc room will be empty after this client has left", function() {
|
||||
it('should not leave the room', function () {
|
||||
return this.client.leave.called.should.equal(false)
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
this.RoomManager._clientAlreadyInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.returns(true);
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onCall(0).returns(0);
|
||||
this.client.leave = sinon.stub();
|
||||
return this.RoomManager.leaveDoc(this.client, this.doc_id);
|
||||
});
|
||||
return it('should not emit any events', function () {
|
||||
return this.RoomEvents.emit.called.should.equal(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("should leave the room using the id", function() {
|
||||
return this.client.leave.calledWithExactly(this.doc_id).should.equal(true);
|
||||
});
|
||||
return describe('leaveProjectAndDocs', function () {
|
||||
return describe('when the client is connected to the project and multiple docs', function () {
|
||||
beforeEach(function () {
|
||||
this.RoomManager._roomsClientIsIn = sinon
|
||||
.stub()
|
||||
.returns([this.project_id, this.doc_id, this.other_doc_id])
|
||||
this.client.join = sinon.stub()
|
||||
return (this.client.leave = sinon.stub())
|
||||
})
|
||||
|
||||
return it("should emit a 'doc-empty' event with the id", function() {
|
||||
return this.RoomEvents.emit.calledWithExactly('doc-empty', this.doc_id).should.equal(true);
|
||||
});
|
||||
});
|
||||
describe('when this is the only client connected', function () {
|
||||
beforeEach(function (done) {
|
||||
// first call is for the join,
|
||||
// second for the leave
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onCall(0)
|
||||
.returns(0)
|
||||
.onCall(1)
|
||||
.returns(0)
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.other_doc_id)
|
||||
.onCall(0)
|
||||
.returns(0)
|
||||
.onCall(1)
|
||||
.returns(0)
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.project_id)
|
||||
.onCall(0)
|
||||
.returns(0)
|
||||
.onCall(1)
|
||||
.returns(0)
|
||||
this.RoomManager._clientAlreadyInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.returns(true)
|
||||
.withArgs(this.client, this.other_doc_id)
|
||||
.returns(true)
|
||||
.withArgs(this.client, this.project_id)
|
||||
.returns(true)
|
||||
this.RoomEvents.on('project-active', (id) => {
|
||||
return setTimeout(() => {
|
||||
return this.RoomEvents.emit(`project-subscribed-${id}`)
|
||||
}, 100)
|
||||
})
|
||||
this.RoomEvents.on('doc-active', (id) => {
|
||||
return setTimeout(() => {
|
||||
return this.RoomEvents.emit(`doc-subscribed-${id}`)
|
||||
}, 100)
|
||||
})
|
||||
// put the client in the rooms
|
||||
return this.RoomManager.joinProject(
|
||||
this.client,
|
||||
this.project_id,
|
||||
() => {
|
||||
return this.RoomManager.joinDoc(this.client, this.doc_id, () => {
|
||||
return this.RoomManager.joinDoc(
|
||||
this.client,
|
||||
this.other_doc_id,
|
||||
() => {
|
||||
// now leave the project
|
||||
this.RoomManager.leaveProjectAndDocs(this.client)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should leave all the docs', function () {
|
||||
this.client.leave.calledWithExactly(this.doc_id).should.equal(true)
|
||||
return this.client.leave
|
||||
.calledWithExactly(this.other_doc_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
describe("when there are other clients in the doc room", function() {
|
||||
it('should leave the project', function () {
|
||||
return this.client.leave
|
||||
.calledWithExactly(this.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
this.RoomManager._clientAlreadyInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.returns(true);
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onCall(0).returns(123);
|
||||
this.client.leave = sinon.stub();
|
||||
return this.RoomManager.leaveDoc(this.client, this.doc_id);
|
||||
});
|
||||
it("should emit a 'doc-empty' event with the id for each doc", function () {
|
||||
this.RoomEvents.emit
|
||||
.calledWithExactly('doc-empty', this.doc_id)
|
||||
.should.equal(true)
|
||||
return this.RoomEvents.emit
|
||||
.calledWithExactly('doc-empty', this.other_doc_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it("should leave the room using the id", function() {
|
||||
return this.client.leave.calledWithExactly(this.doc_id).should.equal(true);
|
||||
});
|
||||
return it("should emit a 'project-empty' event with the id for the project", function () {
|
||||
return this.RoomEvents.emit
|
||||
.calledWithExactly('project-empty', this.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return it("should not emit any events", function() {
|
||||
return this.RoomEvents.emit.called.should.equal(false);
|
||||
});
|
||||
});
|
||||
return describe('when other clients are still connected', function () {
|
||||
beforeEach(function () {
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onFirstCall()
|
||||
.returns(123)
|
||||
.onSecondCall()
|
||||
.returns(122)
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.other_doc_id)
|
||||
.onFirstCall()
|
||||
.returns(123)
|
||||
.onSecondCall()
|
||||
.returns(122)
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.project_id)
|
||||
.onFirstCall()
|
||||
.returns(123)
|
||||
.onSecondCall()
|
||||
.returns(122)
|
||||
this.RoomManager._clientAlreadyInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.returns(true)
|
||||
.withArgs(this.client, this.other_doc_id)
|
||||
.returns(true)
|
||||
.withArgs(this.client, this.project_id)
|
||||
.returns(true)
|
||||
return this.RoomManager.leaveProjectAndDocs(this.client)
|
||||
})
|
||||
|
||||
return describe("when the client is not in the doc room", function() {
|
||||
it('should leave all the docs', function () {
|
||||
this.client.leave.calledWithExactly(this.doc_id).should.equal(true)
|
||||
return this.client.leave
|
||||
.calledWithExactly(this.other_doc_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
this.RoomManager._clientAlreadyInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.returns(false);
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onCall(0).returns(0);
|
||||
this.client.leave = sinon.stub();
|
||||
return this.RoomManager.leaveDoc(this.client, this.doc_id);
|
||||
});
|
||||
it('should leave the project', function () {
|
||||
return this.client.leave
|
||||
.calledWithExactly(this.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it("should not leave the room", function() {
|
||||
return this.client.leave.called.should.equal(false);
|
||||
});
|
||||
|
||||
return it("should not emit any events", function() {
|
||||
return this.RoomEvents.emit.called.should.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
return describe("leaveProjectAndDocs", function() { return describe("when the client is connected to the project and multiple docs", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.RoomManager._roomsClientIsIn = sinon.stub().returns([this.project_id, this.doc_id, this.other_doc_id]);
|
||||
this.client.join = sinon.stub();
|
||||
return this.client.leave = sinon.stub();
|
||||
});
|
||||
|
||||
describe("when this is the only client connected", function() {
|
||||
|
||||
beforeEach(function(done) {
|
||||
// first call is for the join,
|
||||
// second for the leave
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onCall(0).returns(0)
|
||||
.onCall(1).returns(0);
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.other_doc_id)
|
||||
.onCall(0).returns(0)
|
||||
.onCall(1).returns(0);
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.project_id)
|
||||
.onCall(0).returns(0)
|
||||
.onCall(1).returns(0);
|
||||
this.RoomManager._clientAlreadyInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.returns(true)
|
||||
.withArgs(this.client, this.other_doc_id)
|
||||
.returns(true)
|
||||
.withArgs(this.client, this.project_id)
|
||||
.returns(true);
|
||||
this.RoomEvents.on('project-active', id => {
|
||||
return setTimeout(() => {
|
||||
return this.RoomEvents.emit(`project-subscribed-${id}`);
|
||||
}
|
||||
, 100);
|
||||
});
|
||||
this.RoomEvents.on('doc-active', id => {
|
||||
return setTimeout(() => {
|
||||
return this.RoomEvents.emit(`doc-subscribed-${id}`);
|
||||
}
|
||||
, 100);
|
||||
});
|
||||
// put the client in the rooms
|
||||
return this.RoomManager.joinProject(this.client, this.project_id, () => {
|
||||
return this.RoomManager.joinDoc(this.client, this.doc_id, () => {
|
||||
return this.RoomManager.joinDoc(this.client, this.other_doc_id, () => {
|
||||
// now leave the project
|
||||
this.RoomManager.leaveProjectAndDocs(this.client);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should leave all the docs", function() {
|
||||
this.client.leave.calledWithExactly(this.doc_id).should.equal(true);
|
||||
return this.client.leave.calledWithExactly(this.other_doc_id).should.equal(true);
|
||||
});
|
||||
|
||||
it("should leave the project", function() {
|
||||
return this.client.leave.calledWithExactly(this.project_id).should.equal(true);
|
||||
});
|
||||
|
||||
it("should emit a 'doc-empty' event with the id for each doc", function() {
|
||||
this.RoomEvents.emit.calledWithExactly('doc-empty', this.doc_id).should.equal(true);
|
||||
return this.RoomEvents.emit.calledWithExactly('doc-empty', this.other_doc_id).should.equal(true);
|
||||
});
|
||||
|
||||
return it("should emit a 'project-empty' event with the id for the project", function() {
|
||||
return this.RoomEvents.emit.calledWithExactly('project-empty', this.project_id).should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
return describe("when other clients are still connected", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.onFirstCall().returns(123)
|
||||
.onSecondCall().returns(122);
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.other_doc_id)
|
||||
.onFirstCall().returns(123)
|
||||
.onSecondCall().returns(122);
|
||||
this.RoomManager._clientsInRoom
|
||||
.withArgs(this.client, this.project_id)
|
||||
.onFirstCall().returns(123)
|
||||
.onSecondCall().returns(122);
|
||||
this.RoomManager._clientAlreadyInRoom
|
||||
.withArgs(this.client, this.doc_id)
|
||||
.returns(true)
|
||||
.withArgs(this.client, this.other_doc_id)
|
||||
.returns(true)
|
||||
.withArgs(this.client, this.project_id)
|
||||
.returns(true);
|
||||
return this.RoomManager.leaveProjectAndDocs(this.client);
|
||||
});
|
||||
|
||||
it("should leave all the docs", function() {
|
||||
this.client.leave.calledWithExactly(this.doc_id).should.equal(true);
|
||||
return this.client.leave.calledWithExactly(this.other_doc_id).should.equal(true);
|
||||
});
|
||||
|
||||
it("should leave the project", function() {
|
||||
return this.client.leave.calledWithExactly(this.project_id).should.equal(true);
|
||||
});
|
||||
|
||||
return it("should not emit any events", function() {
|
||||
return this.RoomEvents.emit.called.should.equal(false);
|
||||
});
|
||||
});
|
||||
}); });
|
||||
});
|
||||
return it('should not emit any events', function () {
|
||||
return this.RoomEvents.emit.called.should.equal(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -11,49 +11,49 @@
|
|||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
require('chai').should();
|
||||
const {
|
||||
expect
|
||||
} = require("chai");
|
||||
const SandboxedModule = require('sandboxed-module');
|
||||
const modulePath = '../../../app/js/SafeJsonParse';
|
||||
const sinon = require("sinon");
|
||||
require('chai').should()
|
||||
const { expect } = require('chai')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const modulePath = '../../../app/js/SafeJsonParse'
|
||||
const sinon = require('sinon')
|
||||
|
||||
describe('SafeJsonParse', function() {
|
||||
beforeEach(function() {
|
||||
return this.SafeJsonParse = SandboxedModule.require(modulePath, { requires: {
|
||||
"settings-sharelatex": (this.Settings = {
|
||||
maxUpdateSize: 16 * 1024
|
||||
}),
|
||||
"logger-sharelatex": (this.logger = {error: sinon.stub()})
|
||||
}
|
||||
});});
|
||||
describe('SafeJsonParse', function () {
|
||||
beforeEach(function () {
|
||||
return (this.SafeJsonParse = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'settings-sharelatex': (this.Settings = {
|
||||
maxUpdateSize: 16 * 1024
|
||||
}),
|
||||
'logger-sharelatex': (this.logger = { error: sinon.stub() })
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
return describe("parse", function() {
|
||||
it("should parse documents correctly", function(done) {
|
||||
return this.SafeJsonParse.parse('{"foo": "bar"}', (error, parsed) => {
|
||||
expect(parsed).to.deep.equal({foo: "bar"});
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return an error on bad data", function(done) {
|
||||
return this.SafeJsonParse.parse('blah', (error, parsed) => {
|
||||
expect(error).to.exist;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
return it("should return an error on oversized data", function(done) {
|
||||
// we have a 2k overhead on top of max size
|
||||
const big_blob = Array(16*1024).join("A");
|
||||
const data = `{\"foo\": \"${big_blob}\"}`;
|
||||
this.Settings.maxUpdateSize = 2 * 1024;
|
||||
return this.SafeJsonParse.parse(data, (error, parsed) => {
|
||||
this.logger.error.called.should.equal(true);
|
||||
expect(error).to.exist;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return describe('parse', function () {
|
||||
it('should parse documents correctly', function (done) {
|
||||
return this.SafeJsonParse.parse('{"foo": "bar"}', (error, parsed) => {
|
||||
expect(parsed).to.deep.equal({ foo: 'bar' })
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should return an error on bad data', function (done) {
|
||||
return this.SafeJsonParse.parse('blah', (error, parsed) => {
|
||||
expect(error).to.exist
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
return it('should return an error on oversized data', function (done) {
|
||||
// we have a 2k overhead on top of max size
|
||||
const big_blob = Array(16 * 1024).join('A')
|
||||
const data = `{\"foo\": \"${big_blob}\"}`
|
||||
this.Settings.maxUpdateSize = 2 * 1024
|
||||
return this.SafeJsonParse.parse(data, (error, parsed) => {
|
||||
this.logger.error.called.should.equal(true)
|
||||
expect(error).to.exist
|
||||
return done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -9,168 +9,189 @@
|
|||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const {EventEmitter} = require('events');
|
||||
const {expect} = require('chai');
|
||||
const SandboxedModule = require('sandboxed-module');
|
||||
const modulePath = '../../../app/js/SessionSockets';
|
||||
const sinon = require('sinon');
|
||||
const { EventEmitter } = require('events')
|
||||
const { expect } = require('chai')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const modulePath = '../../../app/js/SessionSockets'
|
||||
const sinon = require('sinon')
|
||||
|
||||
describe('SessionSockets', function() {
|
||||
before(function() {
|
||||
this.SessionSocketsModule = SandboxedModule.require(modulePath);
|
||||
this.io = new EventEmitter();
|
||||
this.id1 = Math.random().toString();
|
||||
this.id2 = Math.random().toString();
|
||||
const redisResponses = {
|
||||
error: [new Error('Redis: something went wrong'), null],
|
||||
unknownId: [null, null]
|
||||
};
|
||||
redisResponses[this.id1] = [null, {user: {_id: '123'}}];
|
||||
redisResponses[this.id2] = [null, {user: {_id: 'abc'}}];
|
||||
describe('SessionSockets', function () {
|
||||
before(function () {
|
||||
this.SessionSocketsModule = SandboxedModule.require(modulePath)
|
||||
this.io = new EventEmitter()
|
||||
this.id1 = Math.random().toString()
|
||||
this.id2 = Math.random().toString()
|
||||
const redisResponses = {
|
||||
error: [new Error('Redis: something went wrong'), null],
|
||||
unknownId: [null, null]
|
||||
}
|
||||
redisResponses[this.id1] = [null, { user: { _id: '123' } }]
|
||||
redisResponses[this.id2] = [null, { user: { _id: 'abc' } }]
|
||||
|
||||
this.sessionStore = {
|
||||
get: sinon.stub().callsFake((id, fn) => fn.apply(null, redisResponses[id]))
|
||||
};
|
||||
this.cookieParser = function(req, res, next) {
|
||||
req.signedCookies = req._signedCookies;
|
||||
return next();
|
||||
};
|
||||
this.SessionSockets = this.SessionSocketsModule(this.io, this.sessionStore, this.cookieParser, 'ol.sid');
|
||||
return this.checkSocket = (socket, fn) => {
|
||||
this.SessionSockets.once('connection', fn);
|
||||
return this.io.emit('connection', socket);
|
||||
};
|
||||
});
|
||||
this.sessionStore = {
|
||||
get: sinon
|
||||
.stub()
|
||||
.callsFake((id, fn) => fn.apply(null, redisResponses[id]))
|
||||
}
|
||||
this.cookieParser = function (req, res, next) {
|
||||
req.signedCookies = req._signedCookies
|
||||
return next()
|
||||
}
|
||||
this.SessionSockets = this.SessionSocketsModule(
|
||||
this.io,
|
||||
this.sessionStore,
|
||||
this.cookieParser,
|
||||
'ol.sid'
|
||||
)
|
||||
return (this.checkSocket = (socket, fn) => {
|
||||
this.SessionSockets.once('connection', fn)
|
||||
return this.io.emit('connection', socket)
|
||||
})
|
||||
})
|
||||
|
||||
describe('without cookies', function() {
|
||||
before(function() {
|
||||
return this.socket = {handshake: {}};});
|
||||
describe('without cookies', function () {
|
||||
before(function () {
|
||||
return (this.socket = { handshake: {} })
|
||||
})
|
||||
|
||||
it('should return a lookup error', function(done) {
|
||||
return this.checkSocket(this.socket, (error) => {
|
||||
expect(error).to.exist;
|
||||
expect(error.message).to.equal('could not look up session by key');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should return a lookup error', function (done) {
|
||||
return this.checkSocket(this.socket, (error) => {
|
||||
expect(error).to.exist
|
||||
expect(error.message).to.equal('could not look up session by key')
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
return it('should not query redis', function(done) {
|
||||
return this.checkSocket(this.socket, () => {
|
||||
expect(this.sessionStore.get.called).to.equal(false);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should not query redis', function (done) {
|
||||
return this.checkSocket(this.socket, () => {
|
||||
expect(this.sessionStore.get.called).to.equal(false)
|
||||
return done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a different cookie', function() {
|
||||
before(function() {
|
||||
return this.socket = {handshake: {_signedCookies: {other: 1}}};});
|
||||
describe('with a different cookie', function () {
|
||||
before(function () {
|
||||
return (this.socket = { handshake: { _signedCookies: { other: 1 } } })
|
||||
})
|
||||
|
||||
it('should return a lookup error', function(done) {
|
||||
return this.checkSocket(this.socket, (error) => {
|
||||
expect(error).to.exist;
|
||||
expect(error.message).to.equal('could not look up session by key');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should return a lookup error', function (done) {
|
||||
return this.checkSocket(this.socket, (error) => {
|
||||
expect(error).to.exist
|
||||
expect(error.message).to.equal('could not look up session by key')
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
return it('should not query redis', function(done) {
|
||||
return this.checkSocket(this.socket, () => {
|
||||
expect(this.sessionStore.get.called).to.equal(false);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should not query redis', function (done) {
|
||||
return this.checkSocket(this.socket, () => {
|
||||
expect(this.sessionStore.get.called).to.equal(false)
|
||||
return done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a valid cookie and a failing session lookup', function() {
|
||||
before(function() {
|
||||
return this.socket = {handshake: {_signedCookies: {'ol.sid': 'error'}}};});
|
||||
describe('with a valid cookie and a failing session lookup', function () {
|
||||
before(function () {
|
||||
return (this.socket = {
|
||||
handshake: { _signedCookies: { 'ol.sid': 'error' } }
|
||||
})
|
||||
})
|
||||
|
||||
it('should query redis', function(done) {
|
||||
return this.checkSocket(this.socket, () => {
|
||||
expect(this.sessionStore.get.called).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should query redis', function (done) {
|
||||
return this.checkSocket(this.socket, () => {
|
||||
expect(this.sessionStore.get.called).to.equal(true)
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
return it('should return a redis error', function(done) {
|
||||
return this.checkSocket(this.socket, (error) => {
|
||||
expect(error).to.exist;
|
||||
expect(error.message).to.equal('Redis: something went wrong');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should return a redis error', function (done) {
|
||||
return this.checkSocket(this.socket, (error) => {
|
||||
expect(error).to.exist
|
||||
expect(error.message).to.equal('Redis: something went wrong')
|
||||
return done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a valid cookie and no matching session', function() {
|
||||
before(function() {
|
||||
return this.socket = {handshake: {_signedCookies: {'ol.sid': 'unknownId'}}};});
|
||||
describe('with a valid cookie and no matching session', function () {
|
||||
before(function () {
|
||||
return (this.socket = {
|
||||
handshake: { _signedCookies: { 'ol.sid': 'unknownId' } }
|
||||
})
|
||||
})
|
||||
|
||||
it('should query redis', function(done) {
|
||||
return this.checkSocket(this.socket, () => {
|
||||
expect(this.sessionStore.get.called).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should query redis', function (done) {
|
||||
return this.checkSocket(this.socket, () => {
|
||||
expect(this.sessionStore.get.called).to.equal(true)
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
return it('should return a lookup error', function(done) {
|
||||
return this.checkSocket(this.socket, (error) => {
|
||||
expect(error).to.exist;
|
||||
expect(error.message).to.equal('could not look up session by key');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should return a lookup error', function (done) {
|
||||
return this.checkSocket(this.socket, (error) => {
|
||||
expect(error).to.exist
|
||||
expect(error.message).to.equal('could not look up session by key')
|
||||
return done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a valid cookie and a matching session', function() {
|
||||
before(function() {
|
||||
return this.socket = {handshake: {_signedCookies: {'ol.sid': this.id1}}};});
|
||||
describe('with a valid cookie and a matching session', function () {
|
||||
before(function () {
|
||||
return (this.socket = {
|
||||
handshake: { _signedCookies: { 'ol.sid': this.id1 } }
|
||||
})
|
||||
})
|
||||
|
||||
it('should query redis', function(done) {
|
||||
return this.checkSocket(this.socket, () => {
|
||||
expect(this.sessionStore.get.called).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should query redis', function (done) {
|
||||
return this.checkSocket(this.socket, () => {
|
||||
expect(this.sessionStore.get.called).to.equal(true)
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should not return an error', function(done) {
|
||||
return this.checkSocket(this.socket, (error) => {
|
||||
expect(error).to.not.exist;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should not return an error', function (done) {
|
||||
return this.checkSocket(this.socket, (error) => {
|
||||
expect(error).to.not.exist
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
return it('should return the session', function(done) {
|
||||
return this.checkSocket(this.socket, (error, s, session) => {
|
||||
expect(session).to.deep.equal({user: {_id: '123'}});
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should return the session', function (done) {
|
||||
return this.checkSocket(this.socket, (error, s, session) => {
|
||||
expect(session).to.deep.equal({ user: { _id: '123' } })
|
||||
return done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return describe('with a different valid cookie and matching session', function() {
|
||||
before(function() {
|
||||
return this.socket = {handshake: {_signedCookies: {'ol.sid': this.id2}}};});
|
||||
return describe('with a different valid cookie and matching session', function () {
|
||||
before(function () {
|
||||
return (this.socket = {
|
||||
handshake: { _signedCookies: { 'ol.sid': this.id2 } }
|
||||
})
|
||||
})
|
||||
|
||||
it('should query redis', function(done) {
|
||||
return this.checkSocket(this.socket, () => {
|
||||
expect(this.sessionStore.get.called).to.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should query redis', function (done) {
|
||||
return this.checkSocket(this.socket, () => {
|
||||
expect(this.sessionStore.get.called).to.equal(true)
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should not return an error', function(done) {
|
||||
return this.checkSocket(this.socket, (error) => {
|
||||
expect(error).to.not.exist;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
it('should not return an error', function (done) {
|
||||
return this.checkSocket(this.socket, (error) => {
|
||||
expect(error).to.not.exist
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
return it('should return the other session', function(done) {
|
||||
return this.checkSocket(this.socket, (error, s, session) => {
|
||||
expect(session).to.deep.equal({user: {_id: 'abc'}});
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should return the other session', function (done) {
|
||||
return this.checkSocket(this.socket, (error, s, session) => {
|
||||
expect(session).to.deep.equal({ user: { _id: 'abc' } })
|
||||
return done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -9,109 +9,154 @@
|
|||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const chai = require('chai');
|
||||
const should = chai.should();
|
||||
const sinon = require("sinon");
|
||||
const modulePath = "../../../app/js/WebApiManager.js";
|
||||
const SandboxedModule = require('sandboxed-module');
|
||||
const { CodedError } = require('../../../app/js/Errors');
|
||||
const chai = require('chai')
|
||||
const should = chai.should()
|
||||
const sinon = require('sinon')
|
||||
const modulePath = '../../../app/js/WebApiManager.js'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const { CodedError } = require('../../../app/js/Errors')
|
||||
|
||||
describe('WebApiManager', function() {
|
||||
beforeEach(function() {
|
||||
this.project_id = "project-id-123";
|
||||
this.user_id = "user-id-123";
|
||||
this.user = {_id: this.user_id};
|
||||
this.callback = sinon.stub();
|
||||
return this.WebApiManager = SandboxedModule.require(modulePath, { requires: {
|
||||
"request": (this.request = {}),
|
||||
"settings-sharelatex": (this.settings = {
|
||||
apis: {
|
||||
web: {
|
||||
url: "http://web.example.com",
|
||||
user: "username",
|
||||
pass: "password"
|
||||
}
|
||||
}
|
||||
}),
|
||||
"logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() })
|
||||
}
|
||||
});});
|
||||
describe('WebApiManager', function () {
|
||||
beforeEach(function () {
|
||||
this.project_id = 'project-id-123'
|
||||
this.user_id = 'user-id-123'
|
||||
this.user = { _id: this.user_id }
|
||||
this.callback = sinon.stub()
|
||||
return (this.WebApiManager = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
request: (this.request = {}),
|
||||
'settings-sharelatex': (this.settings = {
|
||||
apis: {
|
||||
web: {
|
||||
url: 'http://web.example.com',
|
||||
user: 'username',
|
||||
pass: 'password'
|
||||
}
|
||||
}
|
||||
}),
|
||||
'logger-sharelatex': (this.logger = {
|
||||
log: sinon.stub(),
|
||||
error: sinon.stub()
|
||||
})
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
return describe("joinProject", function() {
|
||||
describe("successfully", function() {
|
||||
beforeEach(function() {
|
||||
this.response = {
|
||||
project: { name: "Test project" },
|
||||
privilegeLevel: "owner",
|
||||
isRestrictedUser: true
|
||||
};
|
||||
this.request.post = sinon.stub().callsArgWith(1, null, {statusCode: 200}, this.response);
|
||||
return this.WebApiManager.joinProject(this.project_id, this.user, this.callback);
|
||||
});
|
||||
return describe('joinProject', function () {
|
||||
describe('successfully', function () {
|
||||
beforeEach(function () {
|
||||
this.response = {
|
||||
project: { name: 'Test project' },
|
||||
privilegeLevel: 'owner',
|
||||
isRestrictedUser: true
|
||||
}
|
||||
this.request.post = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, { statusCode: 200 }, this.response)
|
||||
return this.WebApiManager.joinProject(
|
||||
this.project_id,
|
||||
this.user,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
it("should send a request to web to join the project", function() {
|
||||
return this.request.post
|
||||
.calledWith({
|
||||
url: `${this.settings.apis.web.url}/project/${this.project_id}/join`,
|
||||
qs: {
|
||||
user_id: this.user_id
|
||||
},
|
||||
auth: {
|
||||
user: this.settings.apis.web.user,
|
||||
pass: this.settings.apis.web.pass,
|
||||
sendImmediately: true
|
||||
},
|
||||
json: true,
|
||||
jar: false,
|
||||
headers: {}
|
||||
})
|
||||
.should.equal(true);
|
||||
});
|
||||
it('should send a request to web to join the project', function () {
|
||||
return this.request.post
|
||||
.calledWith({
|
||||
url: `${this.settings.apis.web.url}/project/${this.project_id}/join`,
|
||||
qs: {
|
||||
user_id: this.user_id
|
||||
},
|
||||
auth: {
|
||||
user: this.settings.apis.web.user,
|
||||
pass: this.settings.apis.web.pass,
|
||||
sendImmediately: true
|
||||
},
|
||||
json: true,
|
||||
jar: false,
|
||||
headers: {}
|
||||
})
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
return it("should return the project, privilegeLevel, and restricted flag", function() {
|
||||
return this.callback
|
||||
.calledWith(null, this.response.project, this.response.privilegeLevel, this.response.isRestrictedUser)
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should return the project, privilegeLevel, and restricted flag', function () {
|
||||
return this.callback
|
||||
.calledWith(
|
||||
null,
|
||||
this.response.project,
|
||||
this.response.privilegeLevel,
|
||||
this.response.isRestrictedUser
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("with an error from web", function() {
|
||||
beforeEach(function() {
|
||||
this.request.post = sinon.stub().callsArgWith(1, null, {statusCode: 500}, null);
|
||||
return this.WebApiManager.joinProject(this.project_id, this.user_id, this.callback);
|
||||
});
|
||||
describe('with an error from web', function () {
|
||||
beforeEach(function () {
|
||||
this.request.post = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, { statusCode: 500 }, null)
|
||||
return this.WebApiManager.joinProject(
|
||||
this.project_id,
|
||||
this.user_id,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
return it("should call the callback with an error", function() {
|
||||
return this.callback
|
||||
.calledWith(sinon.match({message: "non-success status code from web: 500"}))
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should call the callback with an error', function () {
|
||||
return this.callback
|
||||
.calledWith(
|
||||
sinon.match({ message: 'non-success status code from web: 500' })
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("with no data from web", function() {
|
||||
beforeEach(function() {
|
||||
this.request.post = sinon.stub().callsArgWith(1, null, {statusCode: 200}, null);
|
||||
return this.WebApiManager.joinProject(this.project_id, this.user_id, this.callback);
|
||||
});
|
||||
describe('with no data from web', function () {
|
||||
beforeEach(function () {
|
||||
this.request.post = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, { statusCode: 200 }, null)
|
||||
return this.WebApiManager.joinProject(
|
||||
this.project_id,
|
||||
this.user_id,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
return it("should call the callback with an error", function() {
|
||||
return this.callback
|
||||
.calledWith(sinon.match({message: "no data returned from joinProject request"}))
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should call the callback with an error', function () {
|
||||
return this.callback
|
||||
.calledWith(
|
||||
sinon.match({
|
||||
message: 'no data returned from joinProject request'
|
||||
})
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return describe("when the project is over its rate limit", function() {
|
||||
beforeEach(function() {
|
||||
this.request.post = sinon.stub().callsArgWith(1, null, {statusCode: 429}, null);
|
||||
return this.WebApiManager.joinProject(this.project_id, this.user_id, this.callback);
|
||||
});
|
||||
return describe('when the project is over its rate limit', function () {
|
||||
beforeEach(function () {
|
||||
this.request.post = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, { statusCode: 429 }, null)
|
||||
return this.WebApiManager.joinProject(
|
||||
this.project_id,
|
||||
this.user_id,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
return it("should call the callback with a TooManyRequests error code", function() {
|
||||
return this.callback
|
||||
.calledWith(sinon.match({message: "rate-limit hit when joining project", code: "TooManyRequests"}))
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should call the callback with a TooManyRequests error code', function () {
|
||||
return this.callback
|
||||
.calledWith(
|
||||
sinon.match({
|
||||
message: 'rate-limit hit when joining project',
|
||||
code: 'TooManyRequests'
|
||||
})
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,201 +9,301 @@
|
|||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const SandboxedModule = require('sandboxed-module');
|
||||
const sinon = require('sinon');
|
||||
require('chai').should();
|
||||
const modulePath = require('path').join(__dirname, '../../../app/js/WebsocketLoadBalancer');
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const sinon = require('sinon')
|
||||
require('chai').should()
|
||||
const modulePath = require('path').join(
|
||||
__dirname,
|
||||
'../../../app/js/WebsocketLoadBalancer'
|
||||
)
|
||||
|
||||
describe("WebsocketLoadBalancer", function() {
|
||||
beforeEach(function() {
|
||||
this.rclient = {};
|
||||
this.RoomEvents = {on: sinon.stub()};
|
||||
this.WebsocketLoadBalancer = SandboxedModule.require(modulePath, { requires: {
|
||||
"./RedisClientManager": {
|
||||
createClientList: () => []
|
||||
},
|
||||
"logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() }),
|
||||
"./SafeJsonParse": (this.SafeJsonParse =
|
||||
{parse: (data, cb) => cb(null, JSON.parse(data))}),
|
||||
"./EventLogger": {checkEventOrder: sinon.stub()},
|
||||
"./HealthCheckManager": {check: sinon.stub()},
|
||||
"./RoomManager" : (this.RoomManager = {eventSource: sinon.stub().returns(this.RoomEvents)}),
|
||||
"./ChannelManager": (this.ChannelManager = {publish: sinon.stub()}),
|
||||
"./ConnectedUsersManager": (this.ConnectedUsersManager = {refreshClient: sinon.stub()})
|
||||
}
|
||||
});
|
||||
this.io = {};
|
||||
this.WebsocketLoadBalancer.rclientPubList = [{publish: sinon.stub()}];
|
||||
this.WebsocketLoadBalancer.rclientSubList = [{
|
||||
subscribe: sinon.stub(),
|
||||
on: sinon.stub()
|
||||
}];
|
||||
describe('WebsocketLoadBalancer', function () {
|
||||
beforeEach(function () {
|
||||
this.rclient = {}
|
||||
this.RoomEvents = { on: sinon.stub() }
|
||||
this.WebsocketLoadBalancer = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'./RedisClientManager': {
|
||||
createClientList: () => []
|
||||
},
|
||||
'logger-sharelatex': (this.logger = {
|
||||
log: sinon.stub(),
|
||||
error: sinon.stub()
|
||||
}),
|
||||
'./SafeJsonParse': (this.SafeJsonParse = {
|
||||
parse: (data, cb) => cb(null, JSON.parse(data))
|
||||
}),
|
||||
'./EventLogger': { checkEventOrder: sinon.stub() },
|
||||
'./HealthCheckManager': { check: sinon.stub() },
|
||||
'./RoomManager': (this.RoomManager = {
|
||||
eventSource: sinon.stub().returns(this.RoomEvents)
|
||||
}),
|
||||
'./ChannelManager': (this.ChannelManager = { publish: sinon.stub() }),
|
||||
'./ConnectedUsersManager': (this.ConnectedUsersManager = {
|
||||
refreshClient: sinon.stub()
|
||||
})
|
||||
}
|
||||
})
|
||||
this.io = {}
|
||||
this.WebsocketLoadBalancer.rclientPubList = [{ publish: sinon.stub() }]
|
||||
this.WebsocketLoadBalancer.rclientSubList = [
|
||||
{
|
||||
subscribe: sinon.stub(),
|
||||
on: sinon.stub()
|
||||
}
|
||||
]
|
||||
|
||||
this.room_id = "room-id";
|
||||
this.message = "otUpdateApplied";
|
||||
return this.payload = ["argument one", 42];});
|
||||
this.room_id = 'room-id'
|
||||
this.message = 'otUpdateApplied'
|
||||
return (this.payload = ['argument one', 42])
|
||||
})
|
||||
|
||||
describe("emitToRoom", function() {
|
||||
beforeEach(function() {
|
||||
return this.WebsocketLoadBalancer.emitToRoom(this.room_id, this.message, ...Array.from(this.payload));
|
||||
});
|
||||
describe('emitToRoom', function () {
|
||||
beforeEach(function () {
|
||||
return this.WebsocketLoadBalancer.emitToRoom(
|
||||
this.room_id,
|
||||
this.message,
|
||||
...Array.from(this.payload)
|
||||
)
|
||||
})
|
||||
|
||||
return it("should publish the message to redis", function() {
|
||||
return this.ChannelManager.publish
|
||||
.calledWith(this.WebsocketLoadBalancer.rclientPubList[0], "editor-events", this.room_id, JSON.stringify({
|
||||
room_id: this.room_id,
|
||||
message: this.message,
|
||||
payload: this.payload
|
||||
}))
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should publish the message to redis', function () {
|
||||
return this.ChannelManager.publish
|
||||
.calledWith(
|
||||
this.WebsocketLoadBalancer.rclientPubList[0],
|
||||
'editor-events',
|
||||
this.room_id,
|
||||
JSON.stringify({
|
||||
room_id: this.room_id,
|
||||
message: this.message,
|
||||
payload: this.payload
|
||||
})
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("emitToAll", function() {
|
||||
beforeEach(function() {
|
||||
this.WebsocketLoadBalancer.emitToRoom = sinon.stub();
|
||||
return this.WebsocketLoadBalancer.emitToAll(this.message, ...Array.from(this.payload));
|
||||
});
|
||||
describe('emitToAll', function () {
|
||||
beforeEach(function () {
|
||||
this.WebsocketLoadBalancer.emitToRoom = sinon.stub()
|
||||
return this.WebsocketLoadBalancer.emitToAll(
|
||||
this.message,
|
||||
...Array.from(this.payload)
|
||||
)
|
||||
})
|
||||
|
||||
return it("should emit to the room 'all'", function() {
|
||||
return this.WebsocketLoadBalancer.emitToRoom
|
||||
.calledWith("all", this.message, ...Array.from(this.payload))
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it("should emit to the room 'all'", function () {
|
||||
return this.WebsocketLoadBalancer.emitToRoom
|
||||
.calledWith('all', this.message, ...Array.from(this.payload))
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("listenForEditorEvents", function() {
|
||||
beforeEach(function() {
|
||||
this.WebsocketLoadBalancer._processEditorEvent = sinon.stub();
|
||||
return this.WebsocketLoadBalancer.listenForEditorEvents();
|
||||
});
|
||||
describe('listenForEditorEvents', function () {
|
||||
beforeEach(function () {
|
||||
this.WebsocketLoadBalancer._processEditorEvent = sinon.stub()
|
||||
return this.WebsocketLoadBalancer.listenForEditorEvents()
|
||||
})
|
||||
|
||||
it("should subscribe to the editor-events channel", function() {
|
||||
return this.WebsocketLoadBalancer.rclientSubList[0].subscribe
|
||||
.calledWith("editor-events")
|
||||
.should.equal(true);
|
||||
});
|
||||
it('should subscribe to the editor-events channel', function () {
|
||||
return this.WebsocketLoadBalancer.rclientSubList[0].subscribe
|
||||
.calledWith('editor-events')
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
return it("should process the events with _processEditorEvent", function() {
|
||||
return this.WebsocketLoadBalancer.rclientSubList[0].on
|
||||
.calledWith("message", sinon.match.func)
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should process the events with _processEditorEvent', function () {
|
||||
return this.WebsocketLoadBalancer.rclientSubList[0].on
|
||||
.calledWith('message', sinon.match.func)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
return describe("_processEditorEvent", function() {
|
||||
describe("with bad JSON", function() {
|
||||
beforeEach(function() {
|
||||
this.isRestrictedUser = false;
|
||||
this.SafeJsonParse.parse = sinon.stub().callsArgWith(1, new Error("oops"));
|
||||
return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", "blah");
|
||||
});
|
||||
return describe('_processEditorEvent', function () {
|
||||
describe('with bad JSON', function () {
|
||||
beforeEach(function () {
|
||||
this.isRestrictedUser = false
|
||||
this.SafeJsonParse.parse = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, new Error('oops'))
|
||||
return this.WebsocketLoadBalancer._processEditorEvent(
|
||||
this.io,
|
||||
'editor-events',
|
||||
'blah'
|
||||
)
|
||||
})
|
||||
|
||||
return it("should log an error", function() {
|
||||
return this.logger.error.called.should.equal(true);
|
||||
});
|
||||
});
|
||||
return it('should log an error', function () {
|
||||
return this.logger.error.called.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("with a designated room", function() {
|
||||
beforeEach(function() {
|
||||
this.io.sockets = {
|
||||
clients: sinon.stub().returns([
|
||||
{id: 'client-id-1', emit: (this.emit1 = sinon.stub()), ol_context: {}},
|
||||
{id: 'client-id-2', emit: (this.emit2 = sinon.stub()), ol_context: {}},
|
||||
{id: 'client-id-1', emit: (this.emit3 = sinon.stub()), ol_context: {}} // duplicate client
|
||||
])
|
||||
};
|
||||
const data = JSON.stringify({
|
||||
room_id: this.room_id,
|
||||
message: this.message,
|
||||
payload: this.payload
|
||||
});
|
||||
return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", data);
|
||||
});
|
||||
describe('with a designated room', function () {
|
||||
beforeEach(function () {
|
||||
this.io.sockets = {
|
||||
clients: sinon.stub().returns([
|
||||
{
|
||||
id: 'client-id-1',
|
||||
emit: (this.emit1 = sinon.stub()),
|
||||
ol_context: {}
|
||||
},
|
||||
{
|
||||
id: 'client-id-2',
|
||||
emit: (this.emit2 = sinon.stub()),
|
||||
ol_context: {}
|
||||
},
|
||||
{
|
||||
id: 'client-id-1',
|
||||
emit: (this.emit3 = sinon.stub()),
|
||||
ol_context: {}
|
||||
} // duplicate client
|
||||
])
|
||||
}
|
||||
const data = JSON.stringify({
|
||||
room_id: this.room_id,
|
||||
message: this.message,
|
||||
payload: this.payload
|
||||
})
|
||||
return this.WebsocketLoadBalancer._processEditorEvent(
|
||||
this.io,
|
||||
'editor-events',
|
||||
data
|
||||
)
|
||||
})
|
||||
|
||||
return it("should send the message to all (unique) clients in the room", function() {
|
||||
this.io.sockets.clients
|
||||
.calledWith(this.room_id)
|
||||
.should.equal(true);
|
||||
this.emit1.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
|
||||
this.emit2.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
|
||||
return this.emit3.called.should.equal(false);
|
||||
});
|
||||
}); // duplicate client should be ignored
|
||||
return it('should send the message to all (unique) clients in the room', function () {
|
||||
this.io.sockets.clients.calledWith(this.room_id).should.equal(true)
|
||||
this.emit1
|
||||
.calledWith(this.message, ...Array.from(this.payload))
|
||||
.should.equal(true)
|
||||
this.emit2
|
||||
.calledWith(this.message, ...Array.from(this.payload))
|
||||
.should.equal(true)
|
||||
return this.emit3.called.should.equal(false)
|
||||
})
|
||||
}) // duplicate client should be ignored
|
||||
|
||||
describe("with a designated room, and restricted clients, not restricted message", function() {
|
||||
beforeEach(function() {
|
||||
this.io.sockets = {
|
||||
clients: sinon.stub().returns([
|
||||
{id: 'client-id-1', emit: (this.emit1 = sinon.stub()), ol_context: {}},
|
||||
{id: 'client-id-2', emit: (this.emit2 = sinon.stub()), ol_context: {}},
|
||||
{id: 'client-id-1', emit: (this.emit3 = sinon.stub()), ol_context: {}}, // duplicate client
|
||||
{id: 'client-id-4', emit: (this.emit4 = sinon.stub()), ol_context: {is_restricted_user: true}}
|
||||
])
|
||||
};
|
||||
const data = JSON.stringify({
|
||||
room_id: this.room_id,
|
||||
message: this.message,
|
||||
payload: this.payload
|
||||
});
|
||||
return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", data);
|
||||
});
|
||||
describe('with a designated room, and restricted clients, not restricted message', function () {
|
||||
beforeEach(function () {
|
||||
this.io.sockets = {
|
||||
clients: sinon.stub().returns([
|
||||
{
|
||||
id: 'client-id-1',
|
||||
emit: (this.emit1 = sinon.stub()),
|
||||
ol_context: {}
|
||||
},
|
||||
{
|
||||
id: 'client-id-2',
|
||||
emit: (this.emit2 = sinon.stub()),
|
||||
ol_context: {}
|
||||
},
|
||||
{
|
||||
id: 'client-id-1',
|
||||
emit: (this.emit3 = sinon.stub()),
|
||||
ol_context: {}
|
||||
}, // duplicate client
|
||||
{
|
||||
id: 'client-id-4',
|
||||
emit: (this.emit4 = sinon.stub()),
|
||||
ol_context: { is_restricted_user: true }
|
||||
}
|
||||
])
|
||||
}
|
||||
const data = JSON.stringify({
|
||||
room_id: this.room_id,
|
||||
message: this.message,
|
||||
payload: this.payload
|
||||
})
|
||||
return this.WebsocketLoadBalancer._processEditorEvent(
|
||||
this.io,
|
||||
'editor-events',
|
||||
data
|
||||
)
|
||||
})
|
||||
|
||||
return it("should send the message to all (unique) clients in the room", function() {
|
||||
this.io.sockets.clients
|
||||
.calledWith(this.room_id)
|
||||
.should.equal(true);
|
||||
this.emit1.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
|
||||
this.emit2.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
|
||||
this.emit3.called.should.equal(false); // duplicate client should be ignored
|
||||
return this.emit4.called.should.equal(true);
|
||||
});
|
||||
}); // restricted client, but should be called
|
||||
return it('should send the message to all (unique) clients in the room', function () {
|
||||
this.io.sockets.clients.calledWith(this.room_id).should.equal(true)
|
||||
this.emit1
|
||||
.calledWith(this.message, ...Array.from(this.payload))
|
||||
.should.equal(true)
|
||||
this.emit2
|
||||
.calledWith(this.message, ...Array.from(this.payload))
|
||||
.should.equal(true)
|
||||
this.emit3.called.should.equal(false) // duplicate client should be ignored
|
||||
return this.emit4.called.should.equal(true)
|
||||
})
|
||||
}) // restricted client, but should be called
|
||||
|
||||
describe("with a designated room, and restricted clients, restricted message", function() {
|
||||
beforeEach(function() {
|
||||
this.io.sockets = {
|
||||
clients: sinon.stub().returns([
|
||||
{id: 'client-id-1', emit: (this.emit1 = sinon.stub()), ol_context: {}},
|
||||
{id: 'client-id-2', emit: (this.emit2 = sinon.stub()), ol_context: {}},
|
||||
{id: 'client-id-1', emit: (this.emit3 = sinon.stub()), ol_context: {}}, // duplicate client
|
||||
{id: 'client-id-4', emit: (this.emit4 = sinon.stub()), ol_context: {is_restricted_user: true}}
|
||||
])
|
||||
};
|
||||
const data = JSON.stringify({
|
||||
room_id: this.room_id,
|
||||
message: (this.restrictedMessage = 'new-comment'),
|
||||
payload: this.payload
|
||||
});
|
||||
return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", data);
|
||||
});
|
||||
describe('with a designated room, and restricted clients, restricted message', function () {
|
||||
beforeEach(function () {
|
||||
this.io.sockets = {
|
||||
clients: sinon.stub().returns([
|
||||
{
|
||||
id: 'client-id-1',
|
||||
emit: (this.emit1 = sinon.stub()),
|
||||
ol_context: {}
|
||||
},
|
||||
{
|
||||
id: 'client-id-2',
|
||||
emit: (this.emit2 = sinon.stub()),
|
||||
ol_context: {}
|
||||
},
|
||||
{
|
||||
id: 'client-id-1',
|
||||
emit: (this.emit3 = sinon.stub()),
|
||||
ol_context: {}
|
||||
}, // duplicate client
|
||||
{
|
||||
id: 'client-id-4',
|
||||
emit: (this.emit4 = sinon.stub()),
|
||||
ol_context: { is_restricted_user: true }
|
||||
}
|
||||
])
|
||||
}
|
||||
const data = JSON.stringify({
|
||||
room_id: this.room_id,
|
||||
message: (this.restrictedMessage = 'new-comment'),
|
||||
payload: this.payload
|
||||
})
|
||||
return this.WebsocketLoadBalancer._processEditorEvent(
|
||||
this.io,
|
||||
'editor-events',
|
||||
data
|
||||
)
|
||||
})
|
||||
|
||||
return it("should send the message to all (unique) clients in the room, who are not restricted", function() {
|
||||
this.io.sockets.clients
|
||||
.calledWith(this.room_id)
|
||||
.should.equal(true);
|
||||
this.emit1.calledWith(this.restrictedMessage, ...Array.from(this.payload)).should.equal(true);
|
||||
this.emit2.calledWith(this.restrictedMessage, ...Array.from(this.payload)).should.equal(true);
|
||||
this.emit3.called.should.equal(false); // duplicate client should be ignored
|
||||
return this.emit4.called.should.equal(false);
|
||||
});
|
||||
}); // restricted client, should not be called
|
||||
return it('should send the message to all (unique) clients in the room, who are not restricted', function () {
|
||||
this.io.sockets.clients.calledWith(this.room_id).should.equal(true)
|
||||
this.emit1
|
||||
.calledWith(this.restrictedMessage, ...Array.from(this.payload))
|
||||
.should.equal(true)
|
||||
this.emit2
|
||||
.calledWith(this.restrictedMessage, ...Array.from(this.payload))
|
||||
.should.equal(true)
|
||||
this.emit3.called.should.equal(false) // duplicate client should be ignored
|
||||
return this.emit4.called.should.equal(false)
|
||||
})
|
||||
}) // restricted client, should not be called
|
||||
|
||||
return describe("when emitting to all", function() {
|
||||
beforeEach(function() {
|
||||
this.io.sockets =
|
||||
{emit: (this.emit = sinon.stub())};
|
||||
const data = JSON.stringify({
|
||||
room_id: "all",
|
||||
message: this.message,
|
||||
payload: this.payload
|
||||
});
|
||||
return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", data);
|
||||
});
|
||||
return describe('when emitting to all', function () {
|
||||
beforeEach(function () {
|
||||
this.io.sockets = { emit: (this.emit = sinon.stub()) }
|
||||
const data = JSON.stringify({
|
||||
room_id: 'all',
|
||||
message: this.message,
|
||||
payload: this.payload
|
||||
})
|
||||
return this.WebsocketLoadBalancer._processEditorEvent(
|
||||
this.io,
|
||||
'editor-events',
|
||||
data
|
||||
)
|
||||
})
|
||||
|
||||
return it("should send the message to all clients", function() {
|
||||
return this.emit.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return it('should send the message to all clients', function () {
|
||||
return this.emit
|
||||
.calledWith(this.message, ...Array.from(this.payload))
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -3,20 +3,20 @@
|
|||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
let MockClient;
|
||||
const sinon = require('sinon');
|
||||
let MockClient
|
||||
const sinon = require('sinon')
|
||||
|
||||
let idCounter = 0;
|
||||
let idCounter = 0
|
||||
|
||||
module.exports = (MockClient = class MockClient {
|
||||
constructor() {
|
||||
this.ol_context = {};
|
||||
this.join = sinon.stub();
|
||||
this.emit = sinon.stub();
|
||||
this.disconnect = sinon.stub();
|
||||
this.id = idCounter++;
|
||||
this.publicId = idCounter++;
|
||||
}
|
||||
module.exports = MockClient = class MockClient {
|
||||
constructor() {
|
||||
this.ol_context = {}
|
||||
this.join = sinon.stub()
|
||||
this.emit = sinon.stub()
|
||||
this.disconnect = sinon.stub()
|
||||
this.id = idCounter++
|
||||
this.publicId = idCounter++
|
||||
}
|
||||
|
||||
disconnect() {}
|
||||
});
|
||||
disconnect() {}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue