overleaf/services/document-updater/test/unit/coffee/RealTimeRedisManager/RealTimeRedisManagerTests.js

129 lines
4.2 KiB
JavaScript

/*
* 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/RealTimeRedisManager.js";
const SandboxedModule = require('sandboxed-module');
const Errors = require("../../../../app/js/Errors");
describe("RealTimeRedisManager", function() {
beforeEach(function() {
this.rclient = {
auth() {},
exec: sinon.stub()
};
this.rclient.multi = () => this.rclient;
this.pubsubClient =
{publish: sinon.stub()};
this.RealTimeRedisManager = SandboxedModule.require(modulePath, { requires: {
"redis-sharelatex": { createClient: config => (config.name === 'pubsub') ? this.pubsubClient : this.rclient
},
"settings-sharelatex": {
redis: {
documentupdater: (this.settings = {
key_schema: {
pendingUpdates({doc_id}) { return `PendingUpdates:${doc_id}`; }
}
}),
pubsub: {
name: "pubsub"
}
}
},
"logger-sharelatex": { log() {} },
"crypto": (this.crypto = { randomBytes: sinon.stub().withArgs(4).returns(Buffer.from([0x1, 0x2, 0x3, 0x4])) }),
"os": (this.os = {hostname: sinon.stub().returns("somehost")}),
"./Metrics": (this.metrics = { summary: sinon.stub()})
}
});
this.doc_id = "doc-id-123";
this.project_id = "project-id-123";
return this.callback = sinon.stub();
});
describe("getPendingUpdatesForDoc", function() {
beforeEach(function() {
this.rclient.lrange = sinon.stub();
return this.rclient.ltrim = sinon.stub();
});
describe("successfully", function() {
beforeEach(function() {
this.updates = [
{ op: [{ i: "foo", p: 4 }] },
{ op: [{ i: "foo", p: 4 }] }
];
this.jsonUpdates = this.updates.map(update => JSON.stringify(update));
this.rclient.exec = sinon.stub().callsArgWith(0, null, [this.jsonUpdates]);
return this.RealTimeRedisManager.getPendingUpdatesForDoc(this.doc_id, this.callback);
});
it("should get the pending updates", function() {
return this.rclient.lrange
.calledWith(`PendingUpdates:${this.doc_id}`, 0, 7)
.should.equal(true);
});
it("should delete the pending updates", function() {
return this.rclient.ltrim
.calledWith(`PendingUpdates:${this.doc_id}`, 8, -1)
.should.equal(true);
});
return it("should call the callback with the updates", function() {
return this.callback.calledWith(null, this.updates).should.equal(true);
});
});
return describe("when the JSON doesn't parse", function() {
beforeEach(function() {
this.jsonUpdates = [
JSON.stringify({ op: [{ i: "foo", p: 4 }] }),
"broken json"
];
this.rclient.exec = sinon.stub().callsArgWith(0, null, [this.jsonUpdates]);
return this.RealTimeRedisManager.getPendingUpdatesForDoc(this.doc_id, this.callback);
});
return it("should return an error to the callback", function() {
return this.callback.calledWith(new Error("JSON parse error")).should.equal(true);
});
});
});
describe("getUpdatesLength", function() {
beforeEach(function() {
this.rclient.llen = sinon.stub().yields(null, (this.length = 3));
return this.RealTimeRedisManager.getUpdatesLength(this.doc_id, this.callback);
});
it("should look up the length", function() {
return this.rclient.llen.calledWith(`PendingUpdates:${this.doc_id}`).should.equal(true);
});
return it("should return the length", function() {
return this.callback.calledWith(null, this.length).should.equal(true);
});
});
return describe("sendData", function() {
beforeEach(function() {
this.message_id = "doc:somehost:01020304-0";
return this.RealTimeRedisManager.sendData({op: "thisop"});
});
it("should send the op with a message id", function() {
return this.pubsubClient.publish.calledWith("applied-ops", JSON.stringify({op:"thisop",_id:this.message_id})).should.equal(true);
});
return it("should track the payload size", function() {
return this.metrics.summary.calledWith("redis.publish.applied-ops", JSON.stringify({op:"thisop",_id:this.message_id}).length).should.equal(true);
});
});
});