overleaf/services/document-updater/test/unit/coffee/ShareJsUpdateManager/ShareJsUpdateManagerTests.js

182 lines
6.2 KiB
JavaScript
Raw Normal View History

/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const sinon = require('sinon');
const chai = require('chai');
const should = chai.should();
const modulePath = "../../../../app/js/ShareJsUpdateManager.js";
const SandboxedModule = require('sandboxed-module');
const crypto = require('crypto');
describe("ShareJsUpdateManager", function() {
beforeEach(function() {
let Model;
this.project_id = "project-id-123";
this.doc_id = "document-id-123";
this.callback = sinon.stub();
return this.ShareJsUpdateManager = SandboxedModule.require(modulePath, {
requires: {
2014-02-12 05:40:42 -05:00
"./sharejs/server/model":
(Model = class Model {
constructor(db) {
this.db = db;
}
}),
"./ShareJsDB" : (this.ShareJsDB = { mockDB: true }),
"redis-sharelatex" : { createClient: () => { return this.rclient = {auth() {}}; }
},
"logger-sharelatex": (this.logger = { log: sinon.stub() }),
"./RealTimeRedisManager": (this.RealTimeRedisManager = {}),
"./Metrics": (this.metrics = { inc: sinon.stub() })
},
globals: {
clearTimeout: (this.clearTimeout = sinon.stub())
}
}
);
});
describe("applyUpdate", function() {
beforeEach(function() {
this.lines = ["one", "two"];
this.version = 34;
this.updatedDocLines = ["onefoo", "two"];
const content = this.updatedDocLines.join("\n");
this.hash = crypto.createHash('sha1').update("blob " + content.length + "\x00").update(content, 'utf8').digest('hex');
this.update = {p: 4, t: "foo", v:this.version, hash:this.hash};
this.model = {
applyOp: sinon.stub().callsArg(2),
getSnapshot: sinon.stub(),
db: {
appliedOps: {}
}
};
this.ShareJsUpdateManager.getNewShareJsModel = sinon.stub().returns(this.model);
this.ShareJsUpdateManager._listenForOps = sinon.stub();
return this.ShareJsUpdateManager.removeDocFromCache = sinon.stub().callsArg(1);
});
describe("successfully", function() {
beforeEach(function(done) {
this.model.getSnapshot.callsArgWith(1, null, {snapshot: this.updatedDocLines.join("\n"), v: this.version});
this.model.db.appliedOps[`${this.project_id}:${this.doc_id}`] = (this.appliedOps = ["mock-ops"]);
return this.ShareJsUpdateManager.applyUpdate(this.project_id, this.doc_id, this.update, this.lines, this.version, (err, docLines, version, appliedOps) => {
this.callback(err, docLines, version, appliedOps);
return done();
});
});
it("should create a new ShareJs model", function() {
return this.ShareJsUpdateManager.getNewShareJsModel
.calledWith(this.project_id, this.doc_id, this.lines, this.version)
.should.equal(true);
});
it("should listen for ops on the model", function() {
return this.ShareJsUpdateManager._listenForOps
.calledWith(this.model)
.should.equal(true);
});
it("should send the update to ShareJs", function() {
return this.model.applyOp
.calledWith(`${this.project_id}:${this.doc_id}`, this.update)
.should.equal(true);
});
it("should get the updated doc lines", function() {
return this.model.getSnapshot
.calledWith(`${this.project_id}:${this.doc_id}`)
.should.equal(true);
});
return it("should return the updated doc lines, version and ops", function() {
return this.callback.calledWith(null, this.updatedDocLines, this.version, this.appliedOps).should.equal(true);
});
});
describe("when applyOp fails", function() {
beforeEach(function(done) {
this.error = new Error("Something went wrong");
this.model.applyOp = sinon.stub().callsArgWith(2, this.error);
return this.ShareJsUpdateManager.applyUpdate(this.project_id, this.doc_id, this.update, this.lines, this.version, (err, docLines, version) => {
this.callback(err, docLines, version);
return done();
});
});
return it("should call the callback with the error", function() {
return this.callback.calledWith(this.error).should.equal(true);
});
});
describe("when getSnapshot fails", function() {
beforeEach(function(done) {
this.error = new Error("Something went wrong");
this.model.getSnapshot.callsArgWith(1, this.error);
return this.ShareJsUpdateManager.applyUpdate(this.project_id, this.doc_id, this.update, this.lines, this.version, (err, docLines, version) => {
this.callback(err, docLines, version);
return done();
});
});
return it("should call the callback with the error", function() {
return this.callback.calledWith(this.error).should.equal(true);
});
});
return describe("with an invalid hash", function() {
beforeEach(function(done) {
this.error = new Error("invalid hash");
this.model.getSnapshot.callsArgWith(1, null, {snapshot: "unexpected content", v: this.version});
this.model.db.appliedOps[`${this.project_id}:${this.doc_id}`] = (this.appliedOps = ["mock-ops"]);
return this.ShareJsUpdateManager.applyUpdate(this.project_id, this.doc_id, this.update, this.lines, this.version, (err, docLines, version, appliedOps) => {
this.callback(err, docLines, version, appliedOps);
return done();
});
});
return it("should call the callback with the error", function() {
return this.callback.calledWith(this.error).should.equal(true);
});
});
});
return describe("_listenForOps", function() {
beforeEach(function() {
this.model = { on: (event, callback) => {
return this.callback = callback;
}
};
sinon.spy(this.model, "on");
return this.ShareJsUpdateManager._listenForOps(this.model);
});
it("should listen to the model for updates", function() {
return this.model.on.calledWith("applyOp")
.should.equal(true);
});
return describe("the callback", function() {
beforeEach(function() {
this.opData = {
op: {t: "foo", p: 1},
meta: { source: "bar"
}
};
this.RealTimeRedisManager.sendData = sinon.stub();
return this.callback(`${this.project_id}:${this.doc_id}`, this.opData);
});
return it("should publish the op to redis", function() {
return this.RealTimeRedisManager.sendData
.calledWith({project_id: this.project_id, doc_id: this.doc_id, op: this.opData})
.should.equal(true);
});
});
});
});
2014-02-12 05:40:42 -05:00