2020-05-06 06:10:51 -04:00
|
|
|
/*
|
|
|
|
* 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() {},
|
2016-06-17 07:17:22 -04:00
|
|
|
exec: sinon.stub()
|
2020-05-06 06:10:51 -04:00
|
|
|
};
|
|
|
|
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: {
|
2019-07-10 04:42:05 -04:00
|
|
|
name: "pubsub"
|
2020-05-06 06:10:51 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"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 }] },
|
2016-06-17 07:17:22 -04:00
|
|
|
{ op: [{ i: "foo", p: 4 }] }
|
2020-05-06 06:10:51 -04:00
|
|
|
];
|
|
|
|
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 }] }),
|
2016-06-17 07:17:22 -04:00
|
|
|
"broken json"
|
2020-05-06 06:10:51 -04:00
|
|
|
];
|
|
|
|
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);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|