mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-27 02:51:57 +02:00
decaffeinate: Convert ApplyingUpdatesToADocTests.coffee and 14 other files to JS
This commit is contained in:
@@ -1,394 +1,499 @@
|
||||
sinon = require "sinon"
|
||||
chai = require("chai")
|
||||
chai.should()
|
||||
expect = chai.expect
|
||||
async = require "async"
|
||||
Settings = require('settings-sharelatex')
|
||||
rclient_history = require("redis-sharelatex").createClient(Settings.redis.history) # note: this is track changes, not project-history
|
||||
rclient_project_history = require("redis-sharelatex").createClient(Settings.redis.project_history)
|
||||
rclient_du = require("redis-sharelatex").createClient(Settings.redis.documentupdater)
|
||||
Keys = Settings.redis.documentupdater.key_schema
|
||||
HistoryKeys = Settings.redis.history.key_schema
|
||||
ProjectHistoryKeys = Settings.redis.project_history.key_schema
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require("sinon");
|
||||
const chai = require("chai");
|
||||
chai.should();
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
const async = require("async");
|
||||
const Settings = require('settings-sharelatex');
|
||||
const rclient_history = require("redis-sharelatex").createClient(Settings.redis.history); // note: this is track changes, not project-history
|
||||
const rclient_project_history = require("redis-sharelatex").createClient(Settings.redis.project_history);
|
||||
const rclient_du = require("redis-sharelatex").createClient(Settings.redis.documentupdater);
|
||||
const Keys = Settings.redis.documentupdater.key_schema;
|
||||
const HistoryKeys = Settings.redis.history.key_schema;
|
||||
const ProjectHistoryKeys = Settings.redis.project_history.key_schema;
|
||||
|
||||
MockTrackChangesApi = require "./helpers/MockTrackChangesApi"
|
||||
MockWebApi = require "./helpers/MockWebApi"
|
||||
DocUpdaterClient = require "./helpers/DocUpdaterClient"
|
||||
DocUpdaterApp = require "./helpers/DocUpdaterApp"
|
||||
const MockTrackChangesApi = require("./helpers/MockTrackChangesApi");
|
||||
const MockWebApi = require("./helpers/MockWebApi");
|
||||
const DocUpdaterClient = require("./helpers/DocUpdaterClient");
|
||||
const DocUpdaterApp = require("./helpers/DocUpdaterApp");
|
||||
|
||||
describe "Applying updates to a doc", ->
|
||||
before (done) ->
|
||||
@lines = ["one", "two", "three"]
|
||||
@version = 42
|
||||
@update =
|
||||
doc: @doc_id
|
||||
describe("Applying updates to a doc", function() {
|
||||
before(function(done) {
|
||||
this.lines = ["one", "two", "three"];
|
||||
this.version = 42;
|
||||
this.update = {
|
||||
doc: this.doc_id,
|
||||
op: [{
|
||||
i: "one and a half\n"
|
||||
i: "one and a half\n",
|
||||
p: 4
|
||||
}]
|
||||
v: @version
|
||||
@result = ["one", "one and a half", "two", "three"]
|
||||
DocUpdaterApp.ensureRunning(done)
|
||||
}],
|
||||
v: this.version
|
||||
};
|
||||
this.result = ["one", "one and a half", "two", "three"];
|
||||
return DocUpdaterApp.ensureRunning(done);
|
||||
});
|
||||
|
||||
describe "when the document is not loaded", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
sinon.spy MockWebApi, "getDocument"
|
||||
@startTime = Date.now()
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return null
|
||||
describe("when the document is not loaded", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
sinon.spy(MockWebApi, "getDocument");
|
||||
this.startTime = Date.now();
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.update, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.getDocument.restore()
|
||||
after(() => MockWebApi.getDocument.restore());
|
||||
|
||||
it "should load the document from the web API", ->
|
||||
MockWebApi.getDocument
|
||||
.calledWith(@project_id, @doc_id)
|
||||
.should.equal true
|
||||
it("should load the document from the web API", function() {
|
||||
return MockWebApi.getDocument
|
||||
.calledWith(this.project_id, this.doc_id)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should update the doc", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
doc.lines.should.deep.equal @result
|
||||
done()
|
||||
return null
|
||||
it("should update the doc", function(done) {
|
||||
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
doc.lines.should.deep.equal(this.result);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should push the applied updates to the track changes api", (done) ->
|
||||
rclient_history.lrange HistoryKeys.uncompressedHistoryOps({@doc_id}), 0, -1, (error, updates) =>
|
||||
throw error if error?
|
||||
JSON.parse(updates[0]).op.should.deep.equal @update.op
|
||||
rclient_history.sismember HistoryKeys.docsWithHistoryOps({@project_id}), @doc_id, (error, result) =>
|
||||
throw error if error?
|
||||
result.should.equal 1
|
||||
done()
|
||||
return null
|
||||
it("should push the applied updates to the track changes api", function(done) {
|
||||
rclient_history.lrange(HistoryKeys.uncompressedHistoryOps({doc_id: this.doc_id}), 0, -1, (error, updates) => {
|
||||
if (error != null) { throw error; }
|
||||
JSON.parse(updates[0]).op.should.deep.equal(this.update.op);
|
||||
return rclient_history.sismember(HistoryKeys.docsWithHistoryOps({project_id: this.project_id}), this.doc_id, (error, result) => {
|
||||
if (error != null) { throw error; }
|
||||
result.should.equal(1);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should push the applied updates to the project history changes api", (done) ->
|
||||
rclient_project_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
||||
throw error if error?
|
||||
JSON.parse(updates[0]).op.should.deep.equal @update.op
|
||||
done()
|
||||
return null
|
||||
it("should push the applied updates to the project history changes api", function(done) {
|
||||
rclient_project_history.lrange(ProjectHistoryKeys.projectHistoryOps({project_id: this.project_id}), 0, -1, (error, updates) => {
|
||||
if (error != null) { throw error; }
|
||||
JSON.parse(updates[0]).op.should.deep.equal(this.update.op);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should set the first op timestamp", (done) ->
|
||||
rclient_project_history.get ProjectHistoryKeys.projectHistoryFirstOpTimestamp({@project_id}), (error, result) =>
|
||||
throw error if error?
|
||||
result.should.be.within(@startTime, Date.now())
|
||||
@firstOpTimestamp = result
|
||||
done()
|
||||
return null
|
||||
it("should set the first op timestamp", function(done) {
|
||||
rclient_project_history.get(ProjectHistoryKeys.projectHistoryFirstOpTimestamp({project_id: this.project_id}), (error, result) => {
|
||||
if (error != null) { throw error; }
|
||||
result.should.be.within(this.startTime, Date.now());
|
||||
this.firstOpTimestamp = result;
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
describe "when sending another update", ->
|
||||
before (done) ->
|
||||
@timeout = 10000
|
||||
@second_update = Object.create(@update)
|
||||
@second_update.v = @version + 1
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, @second_update, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return null
|
||||
return describe("when sending another update", function() {
|
||||
before(function(done) {
|
||||
this.timeout = 10000;
|
||||
this.second_update = Object.create(this.update);
|
||||
this.second_update.v = this.version + 1;
|
||||
DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.second_update, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should not change the first op timestamp", (done) ->
|
||||
rclient_project_history.get ProjectHistoryKeys.projectHistoryFirstOpTimestamp({@project_id}), (error, result) =>
|
||||
throw error if error?
|
||||
result.should.equal @firstOpTimestamp
|
||||
done()
|
||||
return null
|
||||
return it("should not change the first op timestamp", function(done) {
|
||||
rclient_project_history.get(ProjectHistoryKeys.projectHistoryFirstOpTimestamp({project_id: this.project_id}), (error, result) => {
|
||||
if (error != null) { throw error; }
|
||||
result.should.equal(this.firstOpTimestamp);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the document is loaded", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
describe("when the document is loaded", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||
throw error if error?
|
||||
sinon.spy MockWebApi, "getDocument"
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return null
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => {
|
||||
if (error != null) { throw error; }
|
||||
sinon.spy(MockWebApi, "getDocument");
|
||||
return DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.update, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.getDocument.restore()
|
||||
after(() => MockWebApi.getDocument.restore());
|
||||
|
||||
it "should not need to call the web api", ->
|
||||
MockWebApi.getDocument.called.should.equal false
|
||||
it("should not need to call the web api", () => MockWebApi.getDocument.called.should.equal(false));
|
||||
|
||||
it "should update the doc", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
doc.lines.should.deep.equal @result
|
||||
done()
|
||||
return null
|
||||
it("should update the doc", function(done) {
|
||||
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
doc.lines.should.deep.equal(this.result);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should push the applied updates to the track changes api", (done) ->
|
||||
rclient_history.lrange HistoryKeys.uncompressedHistoryOps({@doc_id}), 0, -1, (error, updates) =>
|
||||
JSON.parse(updates[0]).op.should.deep.equal @update.op
|
||||
rclient_history.sismember HistoryKeys.docsWithHistoryOps({@project_id}), @doc_id, (error, result) =>
|
||||
result.should.equal 1
|
||||
done()
|
||||
return null
|
||||
it("should push the applied updates to the track changes api", function(done) {
|
||||
rclient_history.lrange(HistoryKeys.uncompressedHistoryOps({doc_id: this.doc_id}), 0, -1, (error, updates) => {
|
||||
JSON.parse(updates[0]).op.should.deep.equal(this.update.op);
|
||||
return rclient_history.sismember(HistoryKeys.docsWithHistoryOps({project_id: this.project_id}), this.doc_id, (error, result) => {
|
||||
result.should.equal(1);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should push the applied updates to the project history changes api", (done) ->
|
||||
rclient_project_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
||||
JSON.parse(updates[0]).op.should.deep.equal @update.op
|
||||
done()
|
||||
return null
|
||||
return it("should push the applied updates to the project history changes api", function(done) {
|
||||
rclient_project_history.lrange(ProjectHistoryKeys.projectHistoryOps({project_id: this.project_id}), 0, -1, (error, updates) => {
|
||||
JSON.parse(updates[0]).op.should.deep.equal(this.update.op);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the document is loaded and is using project-history only", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
describe("when the document is loaded and is using project-history only", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version, projectHistoryType: 'project-history'}
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||
throw error if error?
|
||||
sinon.spy MockWebApi, "getDocument"
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return null
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version, projectHistoryType: 'project-history'});
|
||||
DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => {
|
||||
if (error != null) { throw error; }
|
||||
sinon.spy(MockWebApi, "getDocument");
|
||||
return DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.update, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.getDocument.restore()
|
||||
after(() => MockWebApi.getDocument.restore());
|
||||
|
||||
it "should update the doc", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
doc.lines.should.deep.equal @result
|
||||
done()
|
||||
return null
|
||||
it("should update the doc", function(done) {
|
||||
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
doc.lines.should.deep.equal(this.result);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should not push any applied updates to the track changes api", (done) ->
|
||||
rclient_history.lrange HistoryKeys.uncompressedHistoryOps({@doc_id}), 0, -1, (error, updates) =>
|
||||
updates.length.should.equal 0
|
||||
done()
|
||||
return null
|
||||
it("should not push any applied updates to the track changes api", function(done) {
|
||||
rclient_history.lrange(HistoryKeys.uncompressedHistoryOps({doc_id: this.doc_id}), 0, -1, (error, updates) => {
|
||||
updates.length.should.equal(0);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should push the applied updates to the project history changes api", (done) ->
|
||||
rclient_project_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
||||
JSON.parse(updates[0]).op.should.deep.equal @update.op
|
||||
done()
|
||||
return null
|
||||
return it("should push the applied updates to the project history changes api", function(done) {
|
||||
rclient_project_history.lrange(ProjectHistoryKeys.projectHistoryOps({project_id: this.project_id}), 0, -1, (error, updates) => {
|
||||
JSON.parse(updates[0]).op.should.deep.equal(this.update.op);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the document has been deleted", ->
|
||||
describe "when the ops come in a single linear order", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
lines = ["", "", ""]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: lines, version: 0}
|
||||
@updates = [
|
||||
{ doc_id: @doc_id, v: 0, op: [i: "h", p: 0 ] }
|
||||
{ doc_id: @doc_id, v: 1, op: [i: "e", p: 1 ] }
|
||||
{ doc_id: @doc_id, v: 2, op: [i: "l", p: 2 ] }
|
||||
{ doc_id: @doc_id, v: 3, op: [i: "l", p: 3 ] }
|
||||
{ doc_id: @doc_id, v: 4, op: [i: "o", p: 4 ] }
|
||||
{ doc_id: @doc_id, v: 5, op: [i: " ", p: 5 ] }
|
||||
{ doc_id: @doc_id, v: 6, op: [i: "w", p: 6 ] }
|
||||
{ doc_id: @doc_id, v: 7, op: [i: "o", p: 7 ] }
|
||||
{ doc_id: @doc_id, v: 8, op: [i: "r", p: 8 ] }
|
||||
{ doc_id: @doc_id, v: 9, op: [i: "l", p: 9 ] }
|
||||
{ doc_id: @doc_id, v: 10, op: [i: "d", p: 10] }
|
||||
]
|
||||
@my_result = ["hello world", "", ""]
|
||||
done()
|
||||
describe("when the document has been deleted", function() {
|
||||
describe("when the ops come in a single linear order", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
const lines = ["", "", ""];
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines, version: 0});
|
||||
this.updates = [
|
||||
{ doc_id: this.doc_id, v: 0, op: [{i: "h", p: 0} ] },
|
||||
{ doc_id: this.doc_id, v: 1, op: [{i: "e", p: 1} ] },
|
||||
{ doc_id: this.doc_id, v: 2, op: [{i: "l", p: 2} ] },
|
||||
{ doc_id: this.doc_id, v: 3, op: [{i: "l", p: 3} ] },
|
||||
{ doc_id: this.doc_id, v: 4, op: [{i: "o", p: 4} ] },
|
||||
{ doc_id: this.doc_id, v: 5, op: [{i: " ", p: 5} ] },
|
||||
{ doc_id: this.doc_id, v: 6, op: [{i: "w", p: 6} ] },
|
||||
{ doc_id: this.doc_id, v: 7, op: [{i: "o", p: 7} ] },
|
||||
{ doc_id: this.doc_id, v: 8, op: [{i: "r", p: 8} ] },
|
||||
{ doc_id: this.doc_id, v: 9, op: [{i: "l", p: 9} ] },
|
||||
{ doc_id: this.doc_id, v: 10, op: [{i: "d", p: 10}] }
|
||||
];
|
||||
this.my_result = ["hello world", "", ""];
|
||||
return done();
|
||||
});
|
||||
|
||||
it "should be able to continue applying updates when the project has been deleted", (done) ->
|
||||
actions = []
|
||||
for update in @updates.slice(0,6)
|
||||
do (update) =>
|
||||
actions.push (callback) => DocUpdaterClient.sendUpdate @project_id, @doc_id, update, callback
|
||||
actions.push (callback) => DocUpdaterClient.deleteDoc @project_id, @doc_id, callback
|
||||
for update in @updates.slice(6)
|
||||
do (update) =>
|
||||
actions.push (callback) => DocUpdaterClient.sendUpdate @project_id, @doc_id, update, callback
|
||||
it("should be able to continue applying updates when the project has been deleted", function(done) {
|
||||
let update;
|
||||
const actions = [];
|
||||
for (update of Array.from(this.updates.slice(0,6))) {
|
||||
(update => {
|
||||
return actions.push(callback => DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, update, callback));
|
||||
})(update);
|
||||
}
|
||||
actions.push(callback => DocUpdaterClient.deleteDoc(this.project_id, this.doc_id, callback));
|
||||
for (update of Array.from(this.updates.slice(6))) {
|
||||
(update => {
|
||||
return actions.push(callback => DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, update, callback));
|
||||
})(update);
|
||||
}
|
||||
|
||||
async.series actions, (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
doc.lines.should.deep.equal @my_result
|
||||
done()
|
||||
return null
|
||||
async.series(actions, error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
doc.lines.should.deep.equal(this.my_result);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should push the applied updates to the track changes api", (done) ->
|
||||
rclient_history.lrange HistoryKeys.uncompressedHistoryOps({@doc_id}), 0, -1, (error, updates) =>
|
||||
updates = (JSON.parse(u) for u in updates)
|
||||
for appliedUpdate, i in @updates
|
||||
appliedUpdate.op.should.deep.equal updates[i].op
|
||||
it("should push the applied updates to the track changes api", function(done) {
|
||||
rclient_history.lrange(HistoryKeys.uncompressedHistoryOps({doc_id: this.doc_id}), 0, -1, (error, updates) => {
|
||||
updates = (Array.from(updates).map((u) => JSON.parse(u)));
|
||||
for (let i = 0; i < this.updates.length; i++) {
|
||||
const appliedUpdate = this.updates[i];
|
||||
appliedUpdate.op.should.deep.equal(updates[i].op);
|
||||
}
|
||||
|
||||
rclient_history.sismember HistoryKeys.docsWithHistoryOps({@project_id}), @doc_id, (error, result) =>
|
||||
result.should.equal 1
|
||||
done()
|
||||
return null
|
||||
return rclient_history.sismember(HistoryKeys.docsWithHistoryOps({project_id: this.project_id}), this.doc_id, (error, result) => {
|
||||
result.should.equal(1);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should store the doc ops in the correct order", (done) ->
|
||||
rclient_du.lrange Keys.docOps({doc_id: @doc_id}), 0, -1, (error, updates) =>
|
||||
updates = (JSON.parse(u) for u in updates)
|
||||
for appliedUpdate, i in @updates
|
||||
appliedUpdate.op.should.deep.equal updates[i].op
|
||||
done()
|
||||
return null
|
||||
return it("should store the doc ops in the correct order", function(done) {
|
||||
rclient_du.lrange(Keys.docOps({doc_id: this.doc_id}), 0, -1, (error, updates) => {
|
||||
updates = (Array.from(updates).map((u) => JSON.parse(u)));
|
||||
for (let i = 0; i < this.updates.length; i++) {
|
||||
const appliedUpdate = this.updates[i];
|
||||
appliedUpdate.op.should.deep.equal(updates[i].op);
|
||||
}
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "when older ops come in after the delete", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
lines = ["", "", ""]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: lines, version: 0}
|
||||
@updates = [
|
||||
{ doc_id: @doc_id, v: 0, op: [i: "h", p: 0 ] }
|
||||
{ doc_id: @doc_id, v: 1, op: [i: "e", p: 1 ] }
|
||||
{ doc_id: @doc_id, v: 2, op: [i: "l", p: 2 ] }
|
||||
{ doc_id: @doc_id, v: 3, op: [i: "l", p: 3 ] }
|
||||
{ doc_id: @doc_id, v: 4, op: [i: "o", p: 4 ] }
|
||||
{ doc_id: @doc_id, v: 0, op: [i: "world", p: 1 ] }
|
||||
]
|
||||
@my_result = ["hello", "world", ""]
|
||||
done()
|
||||
return describe("when older ops come in after the delete", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
const lines = ["", "", ""];
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines, version: 0});
|
||||
this.updates = [
|
||||
{ doc_id: this.doc_id, v: 0, op: [{i: "h", p: 0} ] },
|
||||
{ doc_id: this.doc_id, v: 1, op: [{i: "e", p: 1} ] },
|
||||
{ doc_id: this.doc_id, v: 2, op: [{i: "l", p: 2} ] },
|
||||
{ doc_id: this.doc_id, v: 3, op: [{i: "l", p: 3} ] },
|
||||
{ doc_id: this.doc_id, v: 4, op: [{i: "o", p: 4} ] },
|
||||
{ doc_id: this.doc_id, v: 0, op: [{i: "world", p: 1} ] }
|
||||
];
|
||||
this.my_result = ["hello", "world", ""];
|
||||
return done();
|
||||
});
|
||||
|
||||
it "should be able to continue applying updates when the project has been deleted", (done) ->
|
||||
actions = []
|
||||
for update in @updates.slice(0,5)
|
||||
do (update) =>
|
||||
actions.push (callback) => DocUpdaterClient.sendUpdate @project_id, @doc_id, update, callback
|
||||
actions.push (callback) => DocUpdaterClient.deleteDoc @project_id, @doc_id, callback
|
||||
for update in @updates.slice(5)
|
||||
do (update) =>
|
||||
actions.push (callback) => DocUpdaterClient.sendUpdate @project_id, @doc_id, update, callback
|
||||
return it("should be able to continue applying updates when the project has been deleted", function(done) {
|
||||
let update;
|
||||
const actions = [];
|
||||
for (update of Array.from(this.updates.slice(0,5))) {
|
||||
(update => {
|
||||
return actions.push(callback => DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, update, callback));
|
||||
})(update);
|
||||
}
|
||||
actions.push(callback => DocUpdaterClient.deleteDoc(this.project_id, this.doc_id, callback));
|
||||
for (update of Array.from(this.updates.slice(5))) {
|
||||
(update => {
|
||||
return actions.push(callback => DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, update, callback));
|
||||
})(update);
|
||||
}
|
||||
|
||||
async.series actions, (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
doc.lines.should.deep.equal @my_result
|
||||
done()
|
||||
return null
|
||||
async.series(actions, error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
doc.lines.should.deep.equal(this.my_result);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "with a broken update", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
@broken_update = { doc_id: @doc_id, v: @version, op: [d: "not the correct content", p: 0 ] }
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
describe("with a broken update", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
this.broken_update = { doc_id: this.doc_id, v: this.version, op: [{d: "not the correct content", p: 0} ] };
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
|
||||
DocUpdaterClient.subscribeToAppliedOps @messageCallback = sinon.stub()
|
||||
DocUpdaterClient.subscribeToAppliedOps(this.messageCallback = sinon.stub());
|
||||
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, @broken_update, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return null
|
||||
DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.broken_update, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should not update the doc", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
doc.lines.should.deep.equal @lines
|
||||
done()
|
||||
return null
|
||||
it("should not update the doc", function(done) {
|
||||
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
doc.lines.should.deep.equal(this.lines);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should send a message with an error", ->
|
||||
@messageCallback.called.should.equal true
|
||||
[channel, message] = @messageCallback.args[0]
|
||||
channel.should.equal "applied-ops"
|
||||
JSON.parse(message).should.deep.include {
|
||||
project_id: @project_id,
|
||||
doc_id: @doc_id,
|
||||
return it("should send a message with an error", function() {
|
||||
this.messageCallback.called.should.equal(true);
|
||||
const [channel, message] = Array.from(this.messageCallback.args[0]);
|
||||
channel.should.equal("applied-ops");
|
||||
return JSON.parse(message).should.deep.include({
|
||||
project_id: this.project_id,
|
||||
doc_id: this.doc_id,
|
||||
error:'Delete component does not match'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("with enough updates to flush to the track changes api", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
const updates = [];
|
||||
for (let v = 0; v <= 199; v++) { // Should flush after 100 ops
|
||||
updates.push({
|
||||
doc_id: this.doc_id,
|
||||
op: [{i: v.toString(), p: 0}],
|
||||
v
|
||||
});
|
||||
}
|
||||
|
||||
describe "with enough updates to flush to the track changes api", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
updates = []
|
||||
for v in [0..199] # Should flush after 100 ops
|
||||
updates.push
|
||||
doc_id: @doc_id,
|
||||
op: [i: v.toString(), p: 0]
|
||||
v: v
|
||||
sinon.spy(MockTrackChangesApi, "flushDoc");
|
||||
|
||||
sinon.spy MockTrackChangesApi, "flushDoc"
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: 0});
|
||||
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: 0}
|
||||
|
||||
# Send updates in chunks to causes multiple flushes
|
||||
actions = []
|
||||
for i in [0..19]
|
||||
do (i) =>
|
||||
actions.push (cb) =>
|
||||
DocUpdaterClient.sendUpdates @project_id, @doc_id, updates.slice(i*10, (i+1)*10), cb
|
||||
async.series actions, (error) =>
|
||||
throw error if error?
|
||||
setTimeout done, 2000
|
||||
return null
|
||||
|
||||
after ->
|
||||
MockTrackChangesApi.flushDoc.restore()
|
||||
|
||||
it "should flush the doc twice", ->
|
||||
MockTrackChangesApi.flushDoc.calledTwice.should.equal true
|
||||
|
||||
describe "when there is no version in Mongo", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {
|
||||
lines: @lines
|
||||
// Send updates in chunks to causes multiple flushes
|
||||
const actions = [];
|
||||
for (let i = 0; i <= 19; i++) {
|
||||
(i => {
|
||||
return actions.push(cb => {
|
||||
return DocUpdaterClient.sendUpdates(this.project_id, this.doc_id, updates.slice(i*10, (i+1)*10), cb);
|
||||
});
|
||||
})(i);
|
||||
}
|
||||
async.series(actions, error => {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 2000);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
update =
|
||||
doc: @doc_id
|
||||
op: @update.op
|
||||
after(() => MockTrackChangesApi.flushDoc.restore());
|
||||
|
||||
return it("should flush the doc twice", () => MockTrackChangesApi.flushDoc.calledTwice.should.equal(true));
|
||||
});
|
||||
|
||||
describe("when there is no version in Mongo", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
||||
lines: this.lines
|
||||
});
|
||||
|
||||
const update = {
|
||||
doc: this.doc_id,
|
||||
op: this.update.op,
|
||||
v: 0
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, update, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return null
|
||||
};
|
||||
DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, update, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should update the doc (using version = 0)", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
doc.lines.should.deep.equal @result
|
||||
done()
|
||||
return null
|
||||
return it("should update the doc (using version = 0)", function(done) {
|
||||
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
doc.lines.should.deep.equal(this.result);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the sending duplicate ops", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
return describe("when the sending duplicate ops", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
|
||||
DocUpdaterClient.subscribeToAppliedOps @messageCallback = sinon.stub()
|
||||
DocUpdaterClient.subscribeToAppliedOps(this.messageCallback = sinon.stub());
|
||||
|
||||
# One user delete 'one', the next turns it into 'once'. The second becomes a NOP.
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, {
|
||||
doc: @doc_id
|
||||
// One user delete 'one', the next turns it into 'once'. The second becomes a NOP.
|
||||
DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, {
|
||||
doc: this.doc_id,
|
||||
op: [{
|
||||
i: "one and a half\n"
|
||||
i: "one and a half\n",
|
||||
p: 4
|
||||
}]
|
||||
v: @version
|
||||
meta:
|
||||
}],
|
||||
v: this.version,
|
||||
meta: {
|
||||
source: "ikHceq3yfAdQYzBo4-xZ"
|
||||
}, (error) =>
|
||||
throw error if error?
|
||||
setTimeout () =>
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, {
|
||||
doc: @doc_id
|
||||
}
|
||||
}, error => {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(() => {
|
||||
return DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, {
|
||||
doc: this.doc_id,
|
||||
op: [{
|
||||
i: "one and a half\n"
|
||||
i: "one and a half\n",
|
||||
p: 4
|
||||
}]
|
||||
v: @version
|
||||
dupIfSource: ["ikHceq3yfAdQYzBo4-xZ"]
|
||||
meta:
|
||||
}],
|
||||
v: this.version,
|
||||
dupIfSource: ["ikHceq3yfAdQYzBo4-xZ"],
|
||||
meta: {
|
||||
source: "ikHceq3yfAdQYzBo4-xZ"
|
||||
}, (error) =>
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
, 200
|
||||
return null
|
||||
}
|
||||
}, error => {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
}
|
||||
, 200);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should update the doc", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
doc.lines.should.deep.equal @result
|
||||
done()
|
||||
return null
|
||||
it("should update the doc", function(done) {
|
||||
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
doc.lines.should.deep.equal(this.result);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should return a message about duplicate ops", ->
|
||||
@messageCallback.calledTwice.should.equal true
|
||||
@messageCallback.args[0][0].should.equal "applied-ops"
|
||||
expect(JSON.parse(@messageCallback.args[0][1]).op.dup).to.be.undefined
|
||||
@messageCallback.args[1][0].should.equal "applied-ops"
|
||||
expect(JSON.parse(@messageCallback.args[1][1]).op.dup).to.equal true
|
||||
return it("should return a message about duplicate ops", function() {
|
||||
this.messageCallback.calledTwice.should.equal(true);
|
||||
this.messageCallback.args[0][0].should.equal("applied-ops");
|
||||
expect(JSON.parse(this.messageCallback.args[0][1]).op.dup).to.be.undefined;
|
||||
this.messageCallback.args[1][0].should.equal("applied-ops");
|
||||
return expect(JSON.parse(this.messageCallback.args[1][1]).op.dup).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,300 +1,363 @@
|
||||
sinon = require "sinon"
|
||||
chai = require("chai")
|
||||
chai.should()
|
||||
Settings = require('settings-sharelatex')
|
||||
rclient_project_history = require("redis-sharelatex").createClient(Settings.redis.project_history)
|
||||
ProjectHistoryKeys = Settings.redis.project_history.key_schema
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require("sinon");
|
||||
const chai = require("chai");
|
||||
chai.should();
|
||||
const Settings = require('settings-sharelatex');
|
||||
const rclient_project_history = require("redis-sharelatex").createClient(Settings.redis.project_history);
|
||||
const ProjectHistoryKeys = Settings.redis.project_history.key_schema;
|
||||
|
||||
MockProjectHistoryApi = require "./helpers/MockProjectHistoryApi"
|
||||
MockWebApi = require "./helpers/MockWebApi"
|
||||
DocUpdaterClient = require "./helpers/DocUpdaterClient"
|
||||
DocUpdaterApp = require "./helpers/DocUpdaterApp"
|
||||
const MockProjectHistoryApi = require("./helpers/MockProjectHistoryApi");
|
||||
const MockWebApi = require("./helpers/MockWebApi");
|
||||
const DocUpdaterClient = require("./helpers/DocUpdaterClient");
|
||||
const DocUpdaterApp = require("./helpers/DocUpdaterApp");
|
||||
|
||||
describe "Applying updates to a project's structure", ->
|
||||
before ->
|
||||
@user_id = 'user-id-123'
|
||||
@version = 1234
|
||||
describe("Applying updates to a project's structure", function() {
|
||||
before(function() {
|
||||
this.user_id = 'user-id-123';
|
||||
return this.version = 1234;
|
||||
});
|
||||
|
||||
describe "renaming a file", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@fileUpdate =
|
||||
id: DocUpdaterClient.randomId()
|
||||
pathname: '/file-path'
|
||||
describe("renaming a file", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.fileUpdate = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
pathname: '/file-path',
|
||||
newPathname: '/new-file-path'
|
||||
@fileUpdates = [ @fileUpdate ]
|
||||
DocUpdaterApp.ensureRunning (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.sendProjectUpdate @project_id, @user_id, [], @fileUpdates, @version, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
};
|
||||
this.fileUpdates = [ this.fileUpdate ];
|
||||
return DocUpdaterApp.ensureRunning(error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.sendProjectUpdate(this.project_id, this.user_id, [], this.fileUpdates, this.version, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it "should push the applied file renames to the project history api", (done) ->
|
||||
rclient_project_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
||||
throw error if error?
|
||||
return it("should push the applied file renames to the project history api", function(done) {
|
||||
rclient_project_history.lrange(ProjectHistoryKeys.projectHistoryOps({project_id: this.project_id}), 0, -1, (error, updates) => {
|
||||
if (error != null) { throw error; }
|
||||
|
||||
update = JSON.parse(updates[0])
|
||||
update.file.should.equal @fileUpdate.id
|
||||
update.pathname.should.equal '/file-path'
|
||||
update.new_pathname.should.equal '/new-file-path'
|
||||
update.meta.user_id.should.equal @user_id
|
||||
update.meta.ts.should.be.a('string')
|
||||
update.version.should.equal "#{@version}.0"
|
||||
const update = JSON.parse(updates[0]);
|
||||
update.file.should.equal(this.fileUpdate.id);
|
||||
update.pathname.should.equal('/file-path');
|
||||
update.new_pathname.should.equal('/new-file-path');
|
||||
update.meta.user_id.should.equal(this.user_id);
|
||||
update.meta.ts.should.be.a('string');
|
||||
update.version.should.equal(`${this.version}.0`);
|
||||
|
||||
done()
|
||||
return null
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "renaming a document", ->
|
||||
before ->
|
||||
@docUpdate =
|
||||
id: DocUpdaterClient.randomId()
|
||||
pathname: '/doc-path'
|
||||
describe("renaming a document", function() {
|
||||
before(function() {
|
||||
this.docUpdate = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
pathname: '/doc-path',
|
||||
newPathname: '/new-doc-path'
|
||||
@docUpdates = [ @docUpdate ]
|
||||
};
|
||||
return this.docUpdates = [ this.docUpdate ];});
|
||||
|
||||
describe "when the document is not loaded", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
DocUpdaterClient.sendProjectUpdate @project_id, @user_id, @docUpdates, [], @version, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return null
|
||||
describe("when the document is not loaded", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
DocUpdaterClient.sendProjectUpdate(this.project_id, this.user_id, this.docUpdates, [], this.version, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should push the applied doc renames to the project history api", (done) ->
|
||||
rclient_project_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
||||
throw error if error?
|
||||
return it("should push the applied doc renames to the project history api", function(done) {
|
||||
rclient_project_history.lrange(ProjectHistoryKeys.projectHistoryOps({project_id: this.project_id}), 0, -1, (error, updates) => {
|
||||
if (error != null) { throw error; }
|
||||
|
||||
update = JSON.parse(updates[0])
|
||||
update.doc.should.equal @docUpdate.id
|
||||
update.pathname.should.equal '/doc-path'
|
||||
update.new_pathname.should.equal '/new-doc-path'
|
||||
update.meta.user_id.should.equal @user_id
|
||||
update.meta.ts.should.be.a('string')
|
||||
update.version.should.equal "#{@version}.0"
|
||||
const update = JSON.parse(updates[0]);
|
||||
update.doc.should.equal(this.docUpdate.id);
|
||||
update.pathname.should.equal('/doc-path');
|
||||
update.new_pathname.should.equal('/new-doc-path');
|
||||
update.meta.user_id.should.equal(this.user_id);
|
||||
update.meta.ts.should.be.a('string');
|
||||
update.version.should.equal(`${this.version}.0`);
|
||||
|
||||
done()
|
||||
return null
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the document is loaded", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
MockWebApi.insertDoc @project_id, @docUpdate.id, {}
|
||||
DocUpdaterClient.preloadDoc @project_id, @docUpdate.id, (error) =>
|
||||
throw error if error?
|
||||
sinon.spy MockWebApi, "getDocument"
|
||||
DocUpdaterClient.sendProjectUpdate @project_id, @user_id, @docUpdates, [], @version, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return null
|
||||
return describe("when the document is loaded", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
MockWebApi.insertDoc(this.project_id, this.docUpdate.id, {});
|
||||
DocUpdaterClient.preloadDoc(this.project_id, this.docUpdate.id, error => {
|
||||
if (error != null) { throw error; }
|
||||
sinon.spy(MockWebApi, "getDocument");
|
||||
return DocUpdaterClient.sendProjectUpdate(this.project_id, this.user_id, this.docUpdates, [], this.version, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.getDocument.restore()
|
||||
after(() => MockWebApi.getDocument.restore());
|
||||
|
||||
it "should update the doc", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @docUpdate.id, (error, res, doc) =>
|
||||
doc.pathname.should.equal @docUpdate.newPathname
|
||||
done()
|
||||
return null
|
||||
it("should update the doc", function(done) {
|
||||
DocUpdaterClient.getDoc(this.project_id, this.docUpdate.id, (error, res, doc) => {
|
||||
doc.pathname.should.equal(this.docUpdate.newPathname);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should push the applied doc renames to the project history api", (done) ->
|
||||
rclient_project_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
||||
throw error if error?
|
||||
return it("should push the applied doc renames to the project history api", function(done) {
|
||||
rclient_project_history.lrange(ProjectHistoryKeys.projectHistoryOps({project_id: this.project_id}), 0, -1, (error, updates) => {
|
||||
if (error != null) { throw error; }
|
||||
|
||||
update = JSON.parse(updates[0])
|
||||
update.doc.should.equal @docUpdate.id
|
||||
update.pathname.should.equal '/doc-path'
|
||||
update.new_pathname.should.equal '/new-doc-path'
|
||||
update.meta.user_id.should.equal @user_id
|
||||
update.meta.ts.should.be.a('string')
|
||||
update.version.should.equal "#{@version}.0"
|
||||
const update = JSON.parse(updates[0]);
|
||||
update.doc.should.equal(this.docUpdate.id);
|
||||
update.pathname.should.equal('/doc-path');
|
||||
update.new_pathname.should.equal('/new-doc-path');
|
||||
update.meta.user_id.should.equal(this.user_id);
|
||||
update.meta.ts.should.be.a('string');
|
||||
update.version.should.equal(`${this.version}.0`);
|
||||
|
||||
done()
|
||||
return null
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "renaming multiple documents and files", ->
|
||||
before ->
|
||||
@docUpdate0 =
|
||||
id: DocUpdaterClient.randomId()
|
||||
pathname: '/doc-path0'
|
||||
describe("renaming multiple documents and files", function() {
|
||||
before(function() {
|
||||
this.docUpdate0 = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
pathname: '/doc-path0',
|
||||
newPathname: '/new-doc-path0'
|
||||
@docUpdate1 =
|
||||
id: DocUpdaterClient.randomId()
|
||||
pathname: '/doc-path1'
|
||||
};
|
||||
this.docUpdate1 = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
pathname: '/doc-path1',
|
||||
newPathname: '/new-doc-path1'
|
||||
@docUpdates = [ @docUpdate0, @docUpdate1 ]
|
||||
@fileUpdate0 =
|
||||
id: DocUpdaterClient.randomId()
|
||||
pathname: '/file-path0'
|
||||
};
|
||||
this.docUpdates = [ this.docUpdate0, this.docUpdate1 ];
|
||||
this.fileUpdate0 = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
pathname: '/file-path0',
|
||||
newPathname: '/new-file-path0'
|
||||
@fileUpdate1 =
|
||||
id: DocUpdaterClient.randomId()
|
||||
pathname: '/file-path1'
|
||||
};
|
||||
this.fileUpdate1 = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
pathname: '/file-path1',
|
||||
newPathname: '/new-file-path1'
|
||||
@fileUpdates = [ @fileUpdate0, @fileUpdate1 ]
|
||||
};
|
||||
return this.fileUpdates = [ this.fileUpdate0, this.fileUpdate1 ];});
|
||||
|
||||
describe "when the documents are not loaded", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
DocUpdaterClient.sendProjectUpdate @project_id, @user_id, @docUpdates, @fileUpdates, @version, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return null
|
||||
return describe("when the documents are not loaded", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
DocUpdaterClient.sendProjectUpdate(this.project_id, this.user_id, this.docUpdates, this.fileUpdates, this.version, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should push the applied doc renames to the project history api", (done) ->
|
||||
rclient_project_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
||||
throw error if error?
|
||||
return it("should push the applied doc renames to the project history api", function(done) {
|
||||
rclient_project_history.lrange(ProjectHistoryKeys.projectHistoryOps({project_id: this.project_id}), 0, -1, (error, updates) => {
|
||||
if (error != null) { throw error; }
|
||||
|
||||
update = JSON.parse(updates[0])
|
||||
update.doc.should.equal @docUpdate0.id
|
||||
update.pathname.should.equal '/doc-path0'
|
||||
update.new_pathname.should.equal '/new-doc-path0'
|
||||
update.meta.user_id.should.equal @user_id
|
||||
update.meta.ts.should.be.a('string')
|
||||
update.version.should.equal "#{@version}.0"
|
||||
let update = JSON.parse(updates[0]);
|
||||
update.doc.should.equal(this.docUpdate0.id);
|
||||
update.pathname.should.equal('/doc-path0');
|
||||
update.new_pathname.should.equal('/new-doc-path0');
|
||||
update.meta.user_id.should.equal(this.user_id);
|
||||
update.meta.ts.should.be.a('string');
|
||||
update.version.should.equal(`${this.version}.0`);
|
||||
|
||||
update = JSON.parse(updates[1])
|
||||
update.doc.should.equal @docUpdate1.id
|
||||
update.pathname.should.equal '/doc-path1'
|
||||
update.new_pathname.should.equal '/new-doc-path1'
|
||||
update.meta.user_id.should.equal @user_id
|
||||
update.meta.ts.should.be.a('string')
|
||||
update.version.should.equal "#{@version}.1"
|
||||
update = JSON.parse(updates[1]);
|
||||
update.doc.should.equal(this.docUpdate1.id);
|
||||
update.pathname.should.equal('/doc-path1');
|
||||
update.new_pathname.should.equal('/new-doc-path1');
|
||||
update.meta.user_id.should.equal(this.user_id);
|
||||
update.meta.ts.should.be.a('string');
|
||||
update.version.should.equal(`${this.version}.1`);
|
||||
|
||||
update = JSON.parse(updates[2])
|
||||
update.file.should.equal @fileUpdate0.id
|
||||
update.pathname.should.equal '/file-path0'
|
||||
update.new_pathname.should.equal '/new-file-path0'
|
||||
update.meta.user_id.should.equal @user_id
|
||||
update.meta.ts.should.be.a('string')
|
||||
update.version.should.equal "#{@version}.2"
|
||||
update = JSON.parse(updates[2]);
|
||||
update.file.should.equal(this.fileUpdate0.id);
|
||||
update.pathname.should.equal('/file-path0');
|
||||
update.new_pathname.should.equal('/new-file-path0');
|
||||
update.meta.user_id.should.equal(this.user_id);
|
||||
update.meta.ts.should.be.a('string');
|
||||
update.version.should.equal(`${this.version}.2`);
|
||||
|
||||
update = JSON.parse(updates[3])
|
||||
update.file.should.equal @fileUpdate1.id
|
||||
update.pathname.should.equal '/file-path1'
|
||||
update.new_pathname.should.equal '/new-file-path1'
|
||||
update.meta.user_id.should.equal @user_id
|
||||
update.meta.ts.should.be.a('string')
|
||||
update.version.should.equal "#{@version}.3"
|
||||
update = JSON.parse(updates[3]);
|
||||
update.file.should.equal(this.fileUpdate1.id);
|
||||
update.pathname.should.equal('/file-path1');
|
||||
update.new_pathname.should.equal('/new-file-path1');
|
||||
update.meta.user_id.should.equal(this.user_id);
|
||||
update.meta.ts.should.be.a('string');
|
||||
update.version.should.equal(`${this.version}.3`);
|
||||
|
||||
done()
|
||||
return null
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe "adding a file", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@fileUpdate =
|
||||
id: DocUpdaterClient.randomId()
|
||||
pathname: '/file-path'
|
||||
describe("adding a file", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.fileUpdate = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
pathname: '/file-path',
|
||||
url: 'filestore.example.com'
|
||||
@fileUpdates = [ @fileUpdate ]
|
||||
DocUpdaterClient.sendProjectUpdate @project_id, @user_id, [], @fileUpdates, @version, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return null
|
||||
};
|
||||
this.fileUpdates = [ this.fileUpdate ];
|
||||
DocUpdaterClient.sendProjectUpdate(this.project_id, this.user_id, [], this.fileUpdates, this.version, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should push the file addition to the project history api", (done) ->
|
||||
rclient_project_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
||||
throw error if error?
|
||||
return it("should push the file addition to the project history api", function(done) {
|
||||
rclient_project_history.lrange(ProjectHistoryKeys.projectHistoryOps({project_id: this.project_id}), 0, -1, (error, updates) => {
|
||||
if (error != null) { throw error; }
|
||||
|
||||
update = JSON.parse(updates[0])
|
||||
update.file.should.equal @fileUpdate.id
|
||||
update.pathname.should.equal '/file-path'
|
||||
update.url.should.equal 'filestore.example.com'
|
||||
update.meta.user_id.should.equal @user_id
|
||||
update.meta.ts.should.be.a('string')
|
||||
update.version.should.equal "#{@version}.0"
|
||||
const update = JSON.parse(updates[0]);
|
||||
update.file.should.equal(this.fileUpdate.id);
|
||||
update.pathname.should.equal('/file-path');
|
||||
update.url.should.equal('filestore.example.com');
|
||||
update.meta.user_id.should.equal(this.user_id);
|
||||
update.meta.ts.should.be.a('string');
|
||||
update.version.should.equal(`${this.version}.0`);
|
||||
|
||||
done()
|
||||
return null
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "adding a doc", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@docUpdate =
|
||||
id: DocUpdaterClient.randomId()
|
||||
pathname: '/file-path'
|
||||
describe("adding a doc", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.docUpdate = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
pathname: '/file-path',
|
||||
docLines: 'a\nb'
|
||||
@docUpdates = [ @docUpdate ]
|
||||
DocUpdaterClient.sendProjectUpdate @project_id, @user_id, @docUpdates, [], @version, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return null
|
||||
};
|
||||
this.docUpdates = [ this.docUpdate ];
|
||||
DocUpdaterClient.sendProjectUpdate(this.project_id, this.user_id, this.docUpdates, [], this.version, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should push the doc addition to the project history api", (done) ->
|
||||
rclient_project_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
||||
throw error if error?
|
||||
return it("should push the doc addition to the project history api", function(done) {
|
||||
rclient_project_history.lrange(ProjectHistoryKeys.projectHistoryOps({project_id: this.project_id}), 0, -1, (error, updates) => {
|
||||
if (error != null) { throw error; }
|
||||
|
||||
update = JSON.parse(updates[0])
|
||||
update.doc.should.equal @docUpdate.id
|
||||
update.pathname.should.equal '/file-path'
|
||||
update.docLines.should.equal 'a\nb'
|
||||
update.meta.user_id.should.equal @user_id
|
||||
update.meta.ts.should.be.a('string')
|
||||
update.version.should.equal "#{@version}.0"
|
||||
const update = JSON.parse(updates[0]);
|
||||
update.doc.should.equal(this.docUpdate.id);
|
||||
update.pathname.should.equal('/file-path');
|
||||
update.docLines.should.equal('a\nb');
|
||||
update.meta.user_id.should.equal(this.user_id);
|
||||
update.meta.ts.should.be.a('string');
|
||||
update.version.should.equal(`${this.version}.0`);
|
||||
|
||||
done()
|
||||
return null
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "with enough updates to flush to the history service", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@user_id = DocUpdaterClient.randomId()
|
||||
@version0 = 12345
|
||||
@version1 = @version0 + 1
|
||||
updates = []
|
||||
for v in [0..599] # Should flush after 500 ops
|
||||
updates.push
|
||||
describe("with enough updates to flush to the history service", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.user_id = DocUpdaterClient.randomId();
|
||||
this.version0 = 12345;
|
||||
this.version1 = this.version0 + 1;
|
||||
const updates = [];
|
||||
for (let v = 0; v <= 599; v++) { // Should flush after 500 ops
|
||||
updates.push({
|
||||
id: DocUpdaterClient.randomId(),
|
||||
pathname: '/file-' + v
|
||||
pathname: '/file-' + v,
|
||||
docLines: 'a\nb'
|
||||
});
|
||||
}
|
||||
|
||||
sinon.spy MockProjectHistoryApi, "flushProject"
|
||||
sinon.spy(MockProjectHistoryApi, "flushProject");
|
||||
|
||||
# Send updates in chunks to causes multiple flushes
|
||||
projectId = @project_id
|
||||
userId = @project_id
|
||||
DocUpdaterClient.sendProjectUpdate projectId, userId, updates.slice(0, 250), [], @version0, (error) ->
|
||||
throw error if error?
|
||||
DocUpdaterClient.sendProjectUpdate projectId, userId, updates.slice(250), [], @version1, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 2000
|
||||
return null
|
||||
// Send updates in chunks to causes multiple flushes
|
||||
const projectId = this.project_id;
|
||||
const userId = this.project_id;
|
||||
DocUpdaterClient.sendProjectUpdate(projectId, userId, updates.slice(0, 250), [], this.version0, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.sendProjectUpdate(projectId, userId, updates.slice(250), [], this.version1, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 2000);
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
after ->
|
||||
MockProjectHistoryApi.flushProject.restore()
|
||||
after(() => MockProjectHistoryApi.flushProject.restore());
|
||||
|
||||
it "should flush project history", ->
|
||||
MockProjectHistoryApi.flushProject.calledWith(@project_id).should.equal true
|
||||
return it("should flush project history", function() {
|
||||
return MockProjectHistoryApi.flushProject.calledWith(this.project_id).should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe "with too few updates to flush to the history service", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@user_id = DocUpdaterClient.randomId()
|
||||
@version0 = 12345
|
||||
@version1 = @version0 + 1
|
||||
return describe("with too few updates to flush to the history service", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.user_id = DocUpdaterClient.randomId();
|
||||
this.version0 = 12345;
|
||||
this.version1 = this.version0 + 1;
|
||||
|
||||
updates = []
|
||||
for v in [0..42] # Should flush after 500 ops
|
||||
updates.push
|
||||
const updates = [];
|
||||
for (let v = 0; v <= 42; v++) { // Should flush after 500 ops
|
||||
updates.push({
|
||||
id: DocUpdaterClient.randomId(),
|
||||
pathname: '/file-' + v
|
||||
pathname: '/file-' + v,
|
||||
docLines: 'a\nb'
|
||||
});
|
||||
}
|
||||
|
||||
sinon.spy MockProjectHistoryApi, "flushProject"
|
||||
sinon.spy(MockProjectHistoryApi, "flushProject");
|
||||
|
||||
# Send updates in chunks
|
||||
projectId = @project_id
|
||||
userId = @project_id
|
||||
DocUpdaterClient.sendProjectUpdate projectId, userId, updates.slice(0, 10), [], @version0, (error) ->
|
||||
throw error if error?
|
||||
DocUpdaterClient.sendProjectUpdate projectId, userId, updates.slice(10), [], @version1, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 2000
|
||||
return null
|
||||
// Send updates in chunks
|
||||
const projectId = this.project_id;
|
||||
const userId = this.project_id;
|
||||
DocUpdaterClient.sendProjectUpdate(projectId, userId, updates.slice(0, 10), [], this.version0, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.sendProjectUpdate(projectId, userId, updates.slice(10), [], this.version1, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 2000);
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
after ->
|
||||
MockProjectHistoryApi.flushProject.restore()
|
||||
after(() => MockProjectHistoryApi.flushProject.restore());
|
||||
|
||||
it "should not flush project history", ->
|
||||
MockProjectHistoryApi.flushProject.calledWith(@project_id).should.equal false
|
||||
return it("should not flush project history", function() {
|
||||
return MockProjectHistoryApi.flushProject.calledWith(this.project_id).should.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,109 +1,141 @@
|
||||
sinon = require "sinon"
|
||||
chai = require("chai")
|
||||
chai.should()
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require("sinon");
|
||||
const chai = require("chai");
|
||||
chai.should();
|
||||
|
||||
MockTrackChangesApi = require "./helpers/MockTrackChangesApi"
|
||||
MockProjectHistoryApi = require "./helpers/MockProjectHistoryApi"
|
||||
MockWebApi = require "./helpers/MockWebApi"
|
||||
DocUpdaterClient = require "./helpers/DocUpdaterClient"
|
||||
DocUpdaterApp = require "./helpers/DocUpdaterApp"
|
||||
const MockTrackChangesApi = require("./helpers/MockTrackChangesApi");
|
||||
const MockProjectHistoryApi = require("./helpers/MockProjectHistoryApi");
|
||||
const MockWebApi = require("./helpers/MockWebApi");
|
||||
const DocUpdaterClient = require("./helpers/DocUpdaterClient");
|
||||
const DocUpdaterApp = require("./helpers/DocUpdaterApp");
|
||||
|
||||
describe "Deleting a document", ->
|
||||
before (done) ->
|
||||
@lines = ["one", "two", "three"]
|
||||
@version = 42
|
||||
@update =
|
||||
doc: @doc_id
|
||||
describe("Deleting a document", function() {
|
||||
before(function(done) {
|
||||
this.lines = ["one", "two", "three"];
|
||||
this.version = 42;
|
||||
this.update = {
|
||||
doc: this.doc_id,
|
||||
op: [{
|
||||
i: "one and a half\n"
|
||||
i: "one and a half\n",
|
||||
p: 4
|
||||
}]
|
||||
v: @version
|
||||
@result = ["one", "one and a half", "two", "three"]
|
||||
}],
|
||||
v: this.version
|
||||
};
|
||||
this.result = ["one", "one and a half", "two", "three"];
|
||||
|
||||
sinon.spy MockTrackChangesApi, "flushDoc"
|
||||
sinon.spy MockProjectHistoryApi, "flushProject"
|
||||
DocUpdaterApp.ensureRunning(done)
|
||||
sinon.spy(MockTrackChangesApi, "flushDoc");
|
||||
sinon.spy(MockProjectHistoryApi, "flushProject");
|
||||
return DocUpdaterApp.ensureRunning(done);
|
||||
});
|
||||
|
||||
after ->
|
||||
MockTrackChangesApi.flushDoc.restore()
|
||||
MockProjectHistoryApi.flushProject.restore()
|
||||
after(function() {
|
||||
MockTrackChangesApi.flushDoc.restore();
|
||||
return MockProjectHistoryApi.flushProject.restore();
|
||||
});
|
||||
|
||||
describe "when the updated doc exists in the doc updater", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
sinon.spy MockWebApi, "setDocument"
|
||||
sinon.spy MockWebApi, "getDocument"
|
||||
describe("when the updated doc exists in the doc updater", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
sinon.spy(MockWebApi, "setDocument");
|
||||
sinon.spy(MockWebApi, "getDocument");
|
||||
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) =>
|
||||
throw error if error?
|
||||
setTimeout () =>
|
||||
DocUpdaterClient.deleteDoc @project_id, @doc_id, (error, res, body) =>
|
||||
@statusCode = res.statusCode
|
||||
setTimeout done, 200
|
||||
, 200
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.update, error => {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(() => {
|
||||
return DocUpdaterClient.deleteDoc(this.project_id, this.doc_id, (error, res, body) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
}
|
||||
, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.setDocument.restore()
|
||||
MockWebApi.getDocument.restore()
|
||||
after(function() {
|
||||
MockWebApi.setDocument.restore();
|
||||
return MockWebApi.getDocument.restore();
|
||||
});
|
||||
|
||||
it "should return a 204 status code", ->
|
||||
@statusCode.should.equal 204
|
||||
it("should return a 204 status code", function() {
|
||||
return this.statusCode.should.equal(204);
|
||||
});
|
||||
|
||||
it "should send the updated document and version to the web api", ->
|
||||
MockWebApi.setDocument
|
||||
.calledWith(@project_id, @doc_id, @result, @version + 1)
|
||||
.should.equal true
|
||||
it("should send the updated document and version to the web api", function() {
|
||||
return MockWebApi.setDocument
|
||||
.calledWith(this.project_id, this.doc_id, this.result, this.version + 1)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should need to reload the doc if read again", (done) ->
|
||||
MockWebApi.getDocument.called.should.equal.false
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
it("should need to reload the doc if read again", function(done) {
|
||||
MockWebApi.getDocument.called.should.equal.false;
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
MockWebApi.getDocument
|
||||
.calledWith(@project_id, @doc_id)
|
||||
.should.equal true
|
||||
done()
|
||||
.calledWith(this.project_id, this.doc_id)
|
||||
.should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it "should flush track changes", ->
|
||||
MockTrackChangesApi.flushDoc.calledWith(@doc_id).should.equal true
|
||||
it("should flush track changes", function() {
|
||||
return MockTrackChangesApi.flushDoc.calledWith(this.doc_id).should.equal(true);
|
||||
});
|
||||
|
||||
it "should flush project history", ->
|
||||
MockProjectHistoryApi.flushProject.calledWith(@project_id).should.equal true
|
||||
return it("should flush project history", function() {
|
||||
return MockProjectHistoryApi.flushProject.calledWith(this.project_id).should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the doc is not in the doc updater", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {
|
||||
lines: @lines
|
||||
}
|
||||
sinon.spy MockWebApi, "setDocument"
|
||||
sinon.spy MockWebApi, "getDocument"
|
||||
DocUpdaterClient.deleteDoc @project_id, @doc_id, (error, res, body) =>
|
||||
@statusCode = res.statusCode
|
||||
setTimeout done, 200
|
||||
return describe("when the doc is not in the doc updater", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
||||
lines: this.lines
|
||||
});
|
||||
sinon.spy(MockWebApi, "setDocument");
|
||||
sinon.spy(MockWebApi, "getDocument");
|
||||
return DocUpdaterClient.deleteDoc(this.project_id, this.doc_id, (error, res, body) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.setDocument.restore()
|
||||
MockWebApi.getDocument.restore()
|
||||
after(function() {
|
||||
MockWebApi.setDocument.restore();
|
||||
return MockWebApi.getDocument.restore();
|
||||
});
|
||||
|
||||
it "should return a 204 status code", ->
|
||||
@statusCode.should.equal 204
|
||||
it("should return a 204 status code", function() {
|
||||
return this.statusCode.should.equal(204);
|
||||
});
|
||||
|
||||
it "should not need to send the updated document to the web api", ->
|
||||
MockWebApi.setDocument.called.should.equal false
|
||||
it("should not need to send the updated document to the web api", () => MockWebApi.setDocument.called.should.equal(false));
|
||||
|
||||
it "should need to reload the doc if read again", (done) ->
|
||||
MockWebApi.getDocument.called.should.equal.false
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
it("should need to reload the doc if read again", function(done) {
|
||||
MockWebApi.getDocument.called.should.equal.false;
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
MockWebApi.getDocument
|
||||
.calledWith(@project_id, @doc_id)
|
||||
.should.equal true
|
||||
done()
|
||||
.calledWith(this.project_id, this.doc_id)
|
||||
.should.equal(true);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it "should flush track changes", ->
|
||||
MockTrackChangesApi.flushDoc.calledWith(@doc_id).should.equal true
|
||||
it("should flush track changes", function() {
|
||||
return MockTrackChangesApi.flushDoc.calledWith(this.doc_id).should.equal(true);
|
||||
});
|
||||
|
||||
it "should flush project history", ->
|
||||
MockProjectHistoryApi.flushProject.calledWith(@project_id).should.equal true
|
||||
return it("should flush project history", function() {
|
||||
return MockProjectHistoryApi.flushProject.calledWith(this.project_id).should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,174 +1,217 @@
|
||||
sinon = require "sinon"
|
||||
chai = require("chai")
|
||||
chai.should()
|
||||
async = require "async"
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require("sinon");
|
||||
const chai = require("chai");
|
||||
chai.should();
|
||||
const async = require("async");
|
||||
|
||||
MockTrackChangesApi = require "./helpers/MockTrackChangesApi"
|
||||
MockProjectHistoryApi = require "./helpers/MockProjectHistoryApi"
|
||||
MockWebApi = require "./helpers/MockWebApi"
|
||||
DocUpdaterClient = require "./helpers/DocUpdaterClient"
|
||||
DocUpdaterApp = require "./helpers/DocUpdaterApp"
|
||||
const MockTrackChangesApi = require("./helpers/MockTrackChangesApi");
|
||||
const MockProjectHistoryApi = require("./helpers/MockProjectHistoryApi");
|
||||
const MockWebApi = require("./helpers/MockWebApi");
|
||||
const DocUpdaterClient = require("./helpers/DocUpdaterClient");
|
||||
const DocUpdaterApp = require("./helpers/DocUpdaterApp");
|
||||
|
||||
describe "Deleting a project", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@docs = [{
|
||||
id: doc_id0 = DocUpdaterClient.randomId()
|
||||
lines: ["one", "two", "three"]
|
||||
update:
|
||||
doc: doc_id0
|
||||
describe("Deleting a project", function() {
|
||||
before(function(done) {
|
||||
let doc_id0, doc_id1;
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.docs = [{
|
||||
id: (doc_id0 = DocUpdaterClient.randomId()),
|
||||
lines: ["one", "two", "three"],
|
||||
update: {
|
||||
doc: doc_id0,
|
||||
op: [{
|
||||
i: "one and a half\n"
|
||||
i: "one and a half\n",
|
||||
p: 4
|
||||
}]
|
||||
}],
|
||||
v: 0
|
||||
},
|
||||
updatedLines: ["one", "one and a half", "two", "three"]
|
||||
}, {
|
||||
id: doc_id1 = DocUpdaterClient.randomId()
|
||||
lines: ["four", "five", "six"]
|
||||
update:
|
||||
doc: doc_id1
|
||||
id: (doc_id1 = DocUpdaterClient.randomId()),
|
||||
lines: ["four", "five", "six"],
|
||||
update: {
|
||||
doc: doc_id1,
|
||||
op: [{
|
||||
i: "four and a half\n"
|
||||
i: "four and a half\n",
|
||||
p: 5
|
||||
}]
|
||||
}],
|
||||
v: 0
|
||||
},
|
||||
updatedLines: ["four", "four and a half", "five", "six"]
|
||||
}]
|
||||
for doc in @docs
|
||||
MockWebApi.insertDoc @project_id, doc.id, {
|
||||
lines: doc.lines
|
||||
}];
|
||||
for (let doc of Array.from(this.docs)) {
|
||||
MockWebApi.insertDoc(this.project_id, doc.id, {
|
||||
lines: doc.lines,
|
||||
version: doc.update.v
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
DocUpdaterApp.ensureRunning(done)
|
||||
return DocUpdaterApp.ensureRunning(done);
|
||||
});
|
||||
|
||||
|
||||
describe "with documents which have been updated", ->
|
||||
before (done) ->
|
||||
sinon.spy MockWebApi, "setDocument"
|
||||
sinon.spy MockTrackChangesApi, "flushDoc"
|
||||
sinon.spy MockProjectHistoryApi, "flushProject"
|
||||
describe("with documents which have been updated", function() {
|
||||
before(function(done) {
|
||||
sinon.spy(MockWebApi, "setDocument");
|
||||
sinon.spy(MockTrackChangesApi, "flushDoc");
|
||||
sinon.spy(MockProjectHistoryApi, "flushProject");
|
||||
|
||||
async.series @docs.map((doc) =>
|
||||
(callback) =>
|
||||
DocUpdaterClient.preloadDoc @project_id, doc.id, (error) =>
|
||||
return callback(error) if error?
|
||||
DocUpdaterClient.sendUpdate @project_id, doc.id, doc.update, (error) =>
|
||||
callback(error)
|
||||
), (error) =>
|
||||
throw error if error?
|
||||
setTimeout () =>
|
||||
DocUpdaterClient.deleteProject @project_id, (error, res, body) =>
|
||||
@statusCode = res.statusCode
|
||||
done()
|
||||
, 200
|
||||
return async.series(this.docs.map(doc => {
|
||||
return callback => {
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, doc.id, error => {
|
||||
if (error != null) { return callback(error); }
|
||||
return DocUpdaterClient.sendUpdate(this.project_id, doc.id, doc.update, error => {
|
||||
return callback(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
}), error => {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(() => {
|
||||
return DocUpdaterClient.deleteProject(this.project_id, (error, res, body) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return done();
|
||||
});
|
||||
}
|
||||
, 200);
|
||||
});
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.setDocument.restore()
|
||||
MockTrackChangesApi.flushDoc.restore()
|
||||
MockProjectHistoryApi.flushProject.restore()
|
||||
after(function() {
|
||||
MockWebApi.setDocument.restore();
|
||||
MockTrackChangesApi.flushDoc.restore();
|
||||
return MockProjectHistoryApi.flushProject.restore();
|
||||
});
|
||||
|
||||
it "should return a 204 status code", ->
|
||||
@statusCode.should.equal 204
|
||||
it("should return a 204 status code", function() {
|
||||
return this.statusCode.should.equal(204);
|
||||
});
|
||||
|
||||
it "should send each document to the web api", ->
|
||||
for doc in @docs
|
||||
it("should send each document to the web api", function() {
|
||||
return Array.from(this.docs).map((doc) =>
|
||||
MockWebApi.setDocument
|
||||
.calledWith(@project_id, doc.id, doc.updatedLines)
|
||||
.should.equal true
|
||||
.calledWith(this.project_id, doc.id, doc.updatedLines)
|
||||
.should.equal(true));
|
||||
});
|
||||
|
||||
it "should need to reload the docs if read again", (done) ->
|
||||
sinon.spy MockWebApi, "getDocument"
|
||||
async.series @docs.map((doc) =>
|
||||
(callback) =>
|
||||
MockWebApi.getDocument.calledWith(@project_id, doc.id).should.equal false
|
||||
DocUpdaterClient.getDoc @project_id, doc.id, (error, res, returnedDoc) =>
|
||||
MockWebApi.getDocument.calledWith(@project_id, doc.id).should.equal true
|
||||
callback()
|
||||
), () ->
|
||||
MockWebApi.getDocument.restore()
|
||||
done()
|
||||
it("should need to reload the docs if read again", function(done) {
|
||||
sinon.spy(MockWebApi, "getDocument");
|
||||
return async.series(this.docs.map(doc => {
|
||||
return callback => {
|
||||
MockWebApi.getDocument.calledWith(this.project_id, doc.id).should.equal(false);
|
||||
return DocUpdaterClient.getDoc(this.project_id, doc.id, (error, res, returnedDoc) => {
|
||||
MockWebApi.getDocument.calledWith(this.project_id, doc.id).should.equal(true);
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
}), function() {
|
||||
MockWebApi.getDocument.restore();
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it "should flush each doc in track changes", ->
|
||||
for doc in @docs
|
||||
MockTrackChangesApi.flushDoc.calledWith(doc.id).should.equal true
|
||||
it("should flush each doc in track changes", function() {
|
||||
return Array.from(this.docs).map((doc) =>
|
||||
MockTrackChangesApi.flushDoc.calledWith(doc.id).should.equal(true));
|
||||
});
|
||||
|
||||
it "should flush each doc in project history", ->
|
||||
MockProjectHistoryApi.flushProject.calledWith(@project_id).should.equal true
|
||||
return it("should flush each doc in project history", function() {
|
||||
return MockProjectHistoryApi.flushProject.calledWith(this.project_id).should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe "with the background=true parameter from realtime and no request to flush the queue", ->
|
||||
before (done) ->
|
||||
sinon.spy MockWebApi, "setDocument"
|
||||
sinon.spy MockTrackChangesApi, "flushDoc"
|
||||
sinon.spy MockProjectHistoryApi, "flushProject"
|
||||
describe("with the background=true parameter from realtime and no request to flush the queue", function() {
|
||||
before(function(done) {
|
||||
sinon.spy(MockWebApi, "setDocument");
|
||||
sinon.spy(MockTrackChangesApi, "flushDoc");
|
||||
sinon.spy(MockProjectHistoryApi, "flushProject");
|
||||
|
||||
async.series @docs.map((doc) =>
|
||||
(callback) =>
|
||||
DocUpdaterClient.preloadDoc @project_id, doc.id, callback
|
||||
), (error) =>
|
||||
throw error if error?
|
||||
setTimeout () =>
|
||||
DocUpdaterClient.deleteProjectOnShutdown @project_id, (error, res, body) =>
|
||||
@statusCode = res.statusCode
|
||||
done()
|
||||
, 200
|
||||
return async.series(this.docs.map(doc => {
|
||||
return callback => {
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, doc.id, callback);
|
||||
};
|
||||
}), error => {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(() => {
|
||||
return DocUpdaterClient.deleteProjectOnShutdown(this.project_id, (error, res, body) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return done();
|
||||
});
|
||||
}
|
||||
, 200);
|
||||
});
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.setDocument.restore()
|
||||
MockTrackChangesApi.flushDoc.restore()
|
||||
MockProjectHistoryApi.flushProject.restore()
|
||||
after(function() {
|
||||
MockWebApi.setDocument.restore();
|
||||
MockTrackChangesApi.flushDoc.restore();
|
||||
return MockProjectHistoryApi.flushProject.restore();
|
||||
});
|
||||
|
||||
it "should return a 204 status code", ->
|
||||
@statusCode.should.equal 204
|
||||
it("should return a 204 status code", function() {
|
||||
return this.statusCode.should.equal(204);
|
||||
});
|
||||
|
||||
it "should not send any documents to the web api", ->
|
||||
MockWebApi.setDocument.called.should.equal false
|
||||
it("should not send any documents to the web api", () => MockWebApi.setDocument.called.should.equal(false));
|
||||
|
||||
it "should not flush any docs in track changes", ->
|
||||
MockTrackChangesApi.flushDoc.called.should.equal false
|
||||
it("should not flush any docs in track changes", () => MockTrackChangesApi.flushDoc.called.should.equal(false));
|
||||
|
||||
it "should not flush to project history", ->
|
||||
MockProjectHistoryApi.flushProject.called.should.equal false
|
||||
return it("should not flush to project history", () => MockProjectHistoryApi.flushProject.called.should.equal(false));
|
||||
});
|
||||
|
||||
describe "with the background=true parameter from realtime and a request to flush the queue", ->
|
||||
before (done) ->
|
||||
sinon.spy MockWebApi, "setDocument"
|
||||
sinon.spy MockTrackChangesApi, "flushDoc"
|
||||
sinon.spy MockProjectHistoryApi, "flushProject"
|
||||
return describe("with the background=true parameter from realtime and a request to flush the queue", function() {
|
||||
before(function(done) {
|
||||
sinon.spy(MockWebApi, "setDocument");
|
||||
sinon.spy(MockTrackChangesApi, "flushDoc");
|
||||
sinon.spy(MockProjectHistoryApi, "flushProject");
|
||||
|
||||
async.series @docs.map((doc) =>
|
||||
(callback) =>
|
||||
DocUpdaterClient.preloadDoc @project_id, doc.id, callback
|
||||
), (error) =>
|
||||
throw error if error?
|
||||
setTimeout () =>
|
||||
DocUpdaterClient.deleteProjectOnShutdown @project_id, (error, res, body) =>
|
||||
@statusCode = res.statusCode
|
||||
# after deleting the project and putting it in the queue, flush the queue
|
||||
setTimeout () ->
|
||||
DocUpdaterClient.flushOldProjects done
|
||||
, 2000
|
||||
, 200
|
||||
return async.series(this.docs.map(doc => {
|
||||
return callback => {
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, doc.id, callback);
|
||||
};
|
||||
}), error => {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(() => {
|
||||
return DocUpdaterClient.deleteProjectOnShutdown(this.project_id, (error, res, body) => {
|
||||
this.statusCode = res.statusCode;
|
||||
// after deleting the project and putting it in the queue, flush the queue
|
||||
return setTimeout(() => DocUpdaterClient.flushOldProjects(done)
|
||||
, 2000);
|
||||
});
|
||||
}
|
||||
, 200);
|
||||
});
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.setDocument.restore()
|
||||
MockTrackChangesApi.flushDoc.restore()
|
||||
MockProjectHistoryApi.flushProject.restore()
|
||||
after(function() {
|
||||
MockWebApi.setDocument.restore();
|
||||
MockTrackChangesApi.flushDoc.restore();
|
||||
return MockProjectHistoryApi.flushProject.restore();
|
||||
});
|
||||
|
||||
it "should return a 204 status code", ->
|
||||
@statusCode.should.equal 204
|
||||
it("should return a 204 status code", function() {
|
||||
return this.statusCode.should.equal(204);
|
||||
});
|
||||
|
||||
it "should send each document to the web api", ->
|
||||
for doc in @docs
|
||||
it("should send each document to the web api", function() {
|
||||
return Array.from(this.docs).map((doc) =>
|
||||
MockWebApi.setDocument
|
||||
.calledWith(@project_id, doc.id, doc.updatedLines)
|
||||
.should.equal true
|
||||
.calledWith(this.project_id, doc.id, doc.updatedLines)
|
||||
.should.equal(true));
|
||||
});
|
||||
|
||||
it "should flush each doc in track changes", ->
|
||||
for doc in @docs
|
||||
MockTrackChangesApi.flushDoc.calledWith(doc.id).should.equal true
|
||||
it("should flush each doc in track changes", function() {
|
||||
return Array.from(this.docs).map((doc) =>
|
||||
MockTrackChangesApi.flushDoc.calledWith(doc.id).should.equal(true));
|
||||
});
|
||||
|
||||
it "should flush to project history", ->
|
||||
MockProjectHistoryApi.flushProject.called.should.equal true
|
||||
return it("should flush to project history", () => MockProjectHistoryApi.flushProject.called.should.equal(true));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,80 +1,105 @@
|
||||
sinon = require "sinon"
|
||||
chai = require("chai")
|
||||
chai.should()
|
||||
async = require "async"
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require("sinon");
|
||||
const chai = require("chai");
|
||||
chai.should();
|
||||
const async = require("async");
|
||||
|
||||
MockWebApi = require "./helpers/MockWebApi"
|
||||
DocUpdaterClient = require "./helpers/DocUpdaterClient"
|
||||
DocUpdaterApp = require "./helpers/DocUpdaterApp"
|
||||
const MockWebApi = require("./helpers/MockWebApi");
|
||||
const DocUpdaterClient = require("./helpers/DocUpdaterClient");
|
||||
const DocUpdaterApp = require("./helpers/DocUpdaterApp");
|
||||
|
||||
describe "Flushing a project", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@docs = [{
|
||||
id: doc_id0 = DocUpdaterClient.randomId()
|
||||
lines: ["one", "two", "three"]
|
||||
update:
|
||||
doc: doc_id0
|
||||
describe("Flushing a project", function() {
|
||||
before(function(done) {
|
||||
let doc_id0, doc_id1;
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.docs = [{
|
||||
id: (doc_id0 = DocUpdaterClient.randomId()),
|
||||
lines: ["one", "two", "three"],
|
||||
update: {
|
||||
doc: doc_id0,
|
||||
op: [{
|
||||
i: "one and a half\n"
|
||||
i: "one and a half\n",
|
||||
p: 4
|
||||
}]
|
||||
}],
|
||||
v: 0
|
||||
},
|
||||
updatedLines: ["one", "one and a half", "two", "three"]
|
||||
}, {
|
||||
id: doc_id1 = DocUpdaterClient.randomId()
|
||||
lines: ["four", "five", "six"]
|
||||
update:
|
||||
doc: doc_id1
|
||||
id: (doc_id1 = DocUpdaterClient.randomId()),
|
||||
lines: ["four", "five", "six"],
|
||||
update: {
|
||||
doc: doc_id1,
|
||||
op: [{
|
||||
i: "four and a half\n"
|
||||
i: "four and a half\n",
|
||||
p: 5
|
||||
}]
|
||||
}],
|
||||
v: 0
|
||||
},
|
||||
updatedLines: ["four", "four and a half", "five", "six"]
|
||||
}]
|
||||
for doc in @docs
|
||||
MockWebApi.insertDoc @project_id, doc.id, {
|
||||
lines: doc.lines
|
||||
}];
|
||||
for (let doc of Array.from(this.docs)) {
|
||||
MockWebApi.insertDoc(this.project_id, doc.id, {
|
||||
lines: doc.lines,
|
||||
version: doc.update.v
|
||||
}
|
||||
DocUpdaterApp.ensureRunning(done)
|
||||
});
|
||||
}
|
||||
return DocUpdaterApp.ensureRunning(done);
|
||||
});
|
||||
|
||||
describe "with documents which have been updated", ->
|
||||
before (done) ->
|
||||
sinon.spy MockWebApi, "setDocument"
|
||||
return describe("with documents which have been updated", function() {
|
||||
before(function(done) {
|
||||
sinon.spy(MockWebApi, "setDocument");
|
||||
|
||||
async.series @docs.map((doc) =>
|
||||
(callback) =>
|
||||
DocUpdaterClient.preloadDoc @project_id, doc.id, (error) =>
|
||||
return callback(error) if error?
|
||||
DocUpdaterClient.sendUpdate @project_id, doc.id, doc.update, (error) =>
|
||||
callback(error)
|
||||
), (error) =>
|
||||
throw error if error?
|
||||
setTimeout () =>
|
||||
DocUpdaterClient.flushProject @project_id, (error, res, body) =>
|
||||
@statusCode = res.statusCode
|
||||
done()
|
||||
, 200
|
||||
return async.series(this.docs.map(doc => {
|
||||
return callback => {
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, doc.id, error => {
|
||||
if (error != null) { return callback(error); }
|
||||
return DocUpdaterClient.sendUpdate(this.project_id, doc.id, doc.update, error => {
|
||||
return callback(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
}), error => {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(() => {
|
||||
return DocUpdaterClient.flushProject(this.project_id, (error, res, body) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return done();
|
||||
});
|
||||
}
|
||||
, 200);
|
||||
});
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.setDocument.restore()
|
||||
after(() => MockWebApi.setDocument.restore());
|
||||
|
||||
it "should return a 204 status code", ->
|
||||
@statusCode.should.equal 204
|
||||
it("should return a 204 status code", function() {
|
||||
return this.statusCode.should.equal(204);
|
||||
});
|
||||
|
||||
it "should send each document to the web api", ->
|
||||
for doc in @docs
|
||||
it("should send each document to the web api", function() {
|
||||
return Array.from(this.docs).map((doc) =>
|
||||
MockWebApi.setDocument
|
||||
.calledWith(@project_id, doc.id, doc.updatedLines)
|
||||
.should.equal true
|
||||
.calledWith(this.project_id, doc.id, doc.updatedLines)
|
||||
.should.equal(true));
|
||||
});
|
||||
|
||||
it "should update the lines in the doc updater", (done) ->
|
||||
async.series @docs.map((doc) =>
|
||||
(callback) =>
|
||||
DocUpdaterClient.getDoc @project_id, doc.id, (error, res, returnedDoc) =>
|
||||
returnedDoc.lines.should.deep.equal doc.updatedLines
|
||||
callback()
|
||||
), done
|
||||
return it("should update the lines in the doc updater", function(done) {
|
||||
return async.series(this.docs.map(doc => {
|
||||
return callback => {
|
||||
return DocUpdaterClient.getDoc(this.project_id, doc.id, (error, res, returnedDoc) => {
|
||||
returnedDoc.lines.should.deep.equal(doc.updatedLines);
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
}), done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,90 +1,112 @@
|
||||
sinon = require "sinon"
|
||||
chai = require("chai")
|
||||
chai.should()
|
||||
expect = chai.expect
|
||||
async = require "async"
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require("sinon");
|
||||
const chai = require("chai");
|
||||
chai.should();
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
const async = require("async");
|
||||
|
||||
MockWebApi = require "./helpers/MockWebApi"
|
||||
DocUpdaterClient = require "./helpers/DocUpdaterClient"
|
||||
DocUpdaterApp = require "./helpers/DocUpdaterApp"
|
||||
const MockWebApi = require("./helpers/MockWebApi");
|
||||
const DocUpdaterClient = require("./helpers/DocUpdaterClient");
|
||||
const DocUpdaterApp = require("./helpers/DocUpdaterApp");
|
||||
|
||||
describe "Flushing a doc to Mongo", ->
|
||||
before (done) ->
|
||||
@lines = ["one", "two", "three"]
|
||||
@version = 42
|
||||
@update =
|
||||
doc: @doc_id
|
||||
meta: { user_id: 'last-author-fake-id' }
|
||||
describe("Flushing a doc to Mongo", function() {
|
||||
before(function(done) {
|
||||
this.lines = ["one", "two", "three"];
|
||||
this.version = 42;
|
||||
this.update = {
|
||||
doc: this.doc_id,
|
||||
meta: { user_id: 'last-author-fake-id' },
|
||||
op: [{
|
||||
i: "one and a half\n"
|
||||
i: "one and a half\n",
|
||||
p: 4
|
||||
}]
|
||||
v: @version
|
||||
@result = ["one", "one and a half", "two", "three"]
|
||||
DocUpdaterApp.ensureRunning(done)
|
||||
}],
|
||||
v: this.version
|
||||
};
|
||||
this.result = ["one", "one and a half", "two", "three"];
|
||||
return DocUpdaterApp.ensureRunning(done);
|
||||
});
|
||||
|
||||
describe "when the updated doc exists in the doc updater", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
sinon.spy MockWebApi, "setDocument"
|
||||
describe("when the updated doc exists in the doc updater", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
sinon.spy(MockWebApi, "setDocument");
|
||||
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
DocUpdaterClient.sendUpdates @project_id, @doc_id, [@update], (error) =>
|
||||
throw error if error?
|
||||
setTimeout () =>
|
||||
DocUpdaterClient.flushDoc @project_id, @doc_id, done
|
||||
, 200
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
return DocUpdaterClient.sendUpdates(this.project_id, this.doc_id, [this.update], error => {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(() => {
|
||||
return DocUpdaterClient.flushDoc(this.project_id, this.doc_id, done);
|
||||
}
|
||||
, 200);
|
||||
});
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.setDocument.restore()
|
||||
after(() => MockWebApi.setDocument.restore());
|
||||
|
||||
it "should flush the updated doc lines and version to the web api", ->
|
||||
MockWebApi.setDocument
|
||||
.calledWith(@project_id, @doc_id, @result, @version + 1)
|
||||
.should.equal true
|
||||
it("should flush the updated doc lines and version to the web api", function() {
|
||||
return MockWebApi.setDocument
|
||||
.calledWith(this.project_id, this.doc_id, this.result, this.version + 1)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should flush the last update author and time to the web api", ->
|
||||
lastUpdatedAt = MockWebApi.setDocument.lastCall.args[5]
|
||||
parseInt(lastUpdatedAt).should.be.closeTo((new Date()).getTime(), 30000)
|
||||
return it("should flush the last update author and time to the web api", function() {
|
||||
const lastUpdatedAt = MockWebApi.setDocument.lastCall.args[5];
|
||||
parseInt(lastUpdatedAt).should.be.closeTo((new Date()).getTime(), 30000);
|
||||
|
||||
lastUpdatedBy = MockWebApi.setDocument.lastCall.args[6]
|
||||
lastUpdatedBy.should.equal 'last-author-fake-id'
|
||||
const lastUpdatedBy = MockWebApi.setDocument.lastCall.args[6];
|
||||
return lastUpdatedBy.should.equal('last-author-fake-id');
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the doc does not exist in the doc updater", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {
|
||||
lines: @lines
|
||||
}
|
||||
sinon.spy MockWebApi, "setDocument"
|
||||
DocUpdaterClient.flushDoc @project_id, @doc_id, done
|
||||
describe("when the doc does not exist in the doc updater", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
||||
lines: this.lines
|
||||
});
|
||||
sinon.spy(MockWebApi, "setDocument");
|
||||
return DocUpdaterClient.flushDoc(this.project_id, this.doc_id, done);
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.setDocument.restore()
|
||||
after(() => MockWebApi.setDocument.restore());
|
||||
|
||||
it "should not flush the doc to the web api", ->
|
||||
MockWebApi.setDocument.called.should.equal false
|
||||
return it("should not flush the doc to the web api", () => MockWebApi.setDocument.called.should.equal(false));
|
||||
});
|
||||
|
||||
describe "when the web api http request takes a long time on first request", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {
|
||||
lines: @lines
|
||||
version: @version
|
||||
}
|
||||
t = 30000
|
||||
sinon.stub MockWebApi, "setDocument", (project_id, doc_id, lines, version, ranges, lastUpdatedAt, lastUpdatedBy, callback = (error) ->) ->
|
||||
setTimeout callback, t
|
||||
t = 0
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc_id, done
|
||||
return describe("when the web api http request takes a long time on first request", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
||||
lines: this.lines,
|
||||
version: this.version
|
||||
});
|
||||
let t = 30000;
|
||||
sinon.stub(MockWebApi, "setDocument", function(project_id, doc_id, lines, version, ranges, lastUpdatedAt, lastUpdatedBy, callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
setTimeout(callback, t);
|
||||
return t = 0;
|
||||
});
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, done);
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.setDocument.restore()
|
||||
after(() => MockWebApi.setDocument.restore());
|
||||
|
||||
it "should still work", (done) ->
|
||||
start = Date.now()
|
||||
DocUpdaterClient.flushDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
res.statusCode.should.equal 204
|
||||
delta = Date.now() - start
|
||||
expect(delta).to.be.below 20000
|
||||
done()
|
||||
return it("should still work", function(done) {
|
||||
const start = Date.now();
|
||||
return DocUpdaterClient.flushDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
res.statusCode.should.equal(204);
|
||||
const delta = Date.now() - start;
|
||||
expect(delta).to.be.below(20000);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,138 +1,188 @@
|
||||
sinon = require "sinon"
|
||||
chai = require("chai")
|
||||
chai.should()
|
||||
expect = chai.expect
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require("sinon");
|
||||
const chai = require("chai");
|
||||
chai.should();
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
|
||||
MockWebApi = require "./helpers/MockWebApi"
|
||||
DocUpdaterClient = require "./helpers/DocUpdaterClient"
|
||||
DocUpdaterApp = require "./helpers/DocUpdaterApp"
|
||||
const MockWebApi = require("./helpers/MockWebApi");
|
||||
const DocUpdaterClient = require("./helpers/DocUpdaterClient");
|
||||
const DocUpdaterApp = require("./helpers/DocUpdaterApp");
|
||||
|
||||
describe "Getting a document", ->
|
||||
before (done) ->
|
||||
@lines = ["one", "two", "three"]
|
||||
@version = 42
|
||||
DocUpdaterApp.ensureRunning(done)
|
||||
describe("Getting a document", function() {
|
||||
before(function(done) {
|
||||
this.lines = ["one", "two", "three"];
|
||||
this.version = 42;
|
||||
return DocUpdaterApp.ensureRunning(done);
|
||||
});
|
||||
|
||||
describe "when the document is not loaded", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
sinon.spy MockWebApi, "getDocument"
|
||||
describe("when the document is not loaded", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
sinon.spy(MockWebApi, "getDocument");
|
||||
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, @returnedDoc) => done()
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, returnedDoc) => { this.returnedDoc = returnedDoc; return done(); });
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.getDocument.restore()
|
||||
after(() => MockWebApi.getDocument.restore());
|
||||
|
||||
it "should load the document from the web API", ->
|
||||
MockWebApi.getDocument
|
||||
.calledWith(@project_id, @doc_id)
|
||||
.should.equal true
|
||||
it("should load the document from the web API", function() {
|
||||
return MockWebApi.getDocument
|
||||
.calledWith(this.project_id, this.doc_id)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should return the document lines", ->
|
||||
@returnedDoc.lines.should.deep.equal @lines
|
||||
it("should return the document lines", function() {
|
||||
return this.returnedDoc.lines.should.deep.equal(this.lines);
|
||||
});
|
||||
|
||||
it "should return the document at its current version", ->
|
||||
@returnedDoc.version.should.equal @version
|
||||
return it("should return the document at its current version", function() {
|
||||
return this.returnedDoc.version.should.equal(this.version);
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the document is already loaded", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
describe("when the document is already loaded", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||
throw error if error?
|
||||
sinon.spy MockWebApi, "getDocument"
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, @returnedDoc) => done()
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => {
|
||||
if (error != null) { throw error; }
|
||||
sinon.spy(MockWebApi, "getDocument");
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, returnedDoc) => { this.returnedDoc = returnedDoc; return done(); });
|
||||
});
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.getDocument.restore()
|
||||
after(() => MockWebApi.getDocument.restore());
|
||||
|
||||
it "should not load the document from the web API", ->
|
||||
MockWebApi.getDocument.called.should.equal false
|
||||
it("should not load the document from the web API", () => MockWebApi.getDocument.called.should.equal(false));
|
||||
|
||||
it "should return the document lines", ->
|
||||
@returnedDoc.lines.should.deep.equal @lines
|
||||
return it("should return the document lines", function() {
|
||||
return this.returnedDoc.lines.should.deep.equal(this.lines);
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the request asks for some recent ops", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {
|
||||
lines: @lines = ["one", "two", "three"]
|
||||
}
|
||||
describe("when the request asks for some recent ops", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
||||
lines: (this.lines = ["one", "two", "three"])
|
||||
});
|
||||
|
||||
@updates = for v in [0..199]
|
||||
doc_id: @doc_id,
|
||||
op: [i: v.toString(), p: 0]
|
||||
v: v
|
||||
this.updates = __range__(0, 199, true).map((v) => ({
|
||||
doc_id: this.doc_id,
|
||||
op: [{i: v.toString(), p: 0}],
|
||||
v
|
||||
}));
|
||||
|
||||
DocUpdaterClient.sendUpdates @project_id, @doc_id, @updates, (error) =>
|
||||
throw error if error?
|
||||
sinon.spy MockWebApi, "getDocument"
|
||||
done()
|
||||
return DocUpdaterClient.sendUpdates(this.project_id, this.doc_id, this.updates, error => {
|
||||
if (error != null) { throw error; }
|
||||
sinon.spy(MockWebApi, "getDocument");
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.getDocument.restore()
|
||||
after(() => MockWebApi.getDocument.restore());
|
||||
|
||||
describe "when the ops are loaded", ->
|
||||
before (done) ->
|
||||
DocUpdaterClient.getDocAndRecentOps @project_id, @doc_id, 190, (error, res, @returnedDoc) => done()
|
||||
describe("when the ops are loaded", function() {
|
||||
before(function(done) {
|
||||
return DocUpdaterClient.getDocAndRecentOps(this.project_id, this.doc_id, 190, (error, res, returnedDoc) => { this.returnedDoc = returnedDoc; return done(); });
|
||||
});
|
||||
|
||||
it "should return the recent ops", ->
|
||||
@returnedDoc.ops.length.should.equal 10
|
||||
for update, i in @updates.slice(190, -1)
|
||||
@returnedDoc.ops[i].op.should.deep.equal update.op
|
||||
return it("should return the recent ops", function() {
|
||||
this.returnedDoc.ops.length.should.equal(10);
|
||||
return Array.from(this.updates.slice(190, -1)).map((update, i) =>
|
||||
this.returnedDoc.ops[i].op.should.deep.equal(update.op));
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the ops are not all loaded", ->
|
||||
before (done) ->
|
||||
# We only track 100 ops
|
||||
DocUpdaterClient.getDocAndRecentOps @project_id, @doc_id, 10, (error, @res, @returnedDoc) => done()
|
||||
return describe("when the ops are not all loaded", function() {
|
||||
before(function(done) {
|
||||
// We only track 100 ops
|
||||
return DocUpdaterClient.getDocAndRecentOps(this.project_id, this.doc_id, 10, (error, res, returnedDoc) => { this.res = res; this.returnedDoc = returnedDoc; return done(); });
|
||||
});
|
||||
|
||||
it "should return UnprocessableEntity", ->
|
||||
@res.statusCode.should.equal 422
|
||||
return it("should return UnprocessableEntity", function() {
|
||||
return this.res.statusCode.should.equal(422);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the document does not exist", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
@statusCode = res.statusCode
|
||||
done()
|
||||
describe("when the document does not exist", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it "should return 404", ->
|
||||
@statusCode.should.equal 404
|
||||
return it("should return 404", function() {
|
||||
return this.statusCode.should.equal(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the web api returns an error", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
sinon.stub MockWebApi, "getDocument", (project_id, doc_id, callback = (error, doc) ->) ->
|
||||
callback new Error("oops")
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
@statusCode = res.statusCode
|
||||
done()
|
||||
describe("when the web api returns an error", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
sinon.stub(MockWebApi, "getDocument", function(project_id, doc_id, callback) {
|
||||
if (callback == null) { callback = function(error, doc) {}; }
|
||||
return callback(new Error("oops"));
|
||||
});
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.getDocument.restore()
|
||||
after(() => MockWebApi.getDocument.restore());
|
||||
|
||||
it "should return 500", ->
|
||||
@statusCode.should.equal 500
|
||||
return it("should return 500", function() {
|
||||
return this.statusCode.should.equal(500);
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the web api http request takes a long time", ->
|
||||
before (done) ->
|
||||
@timeout = 10000
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
sinon.stub MockWebApi, "getDocument", (project_id, doc_id, callback = (error, doc) ->) ->
|
||||
setTimeout callback, 30000
|
||||
done()
|
||||
return describe("when the web api http request takes a long time", function() {
|
||||
before(function(done) {
|
||||
this.timeout = 10000;
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
sinon.stub(MockWebApi, "getDocument", function(project_id, doc_id, callback) {
|
||||
if (callback == null) { callback = function(error, doc) {}; }
|
||||
return setTimeout(callback, 30000);
|
||||
});
|
||||
return done();
|
||||
});
|
||||
|
||||
after ->
|
||||
MockWebApi.getDocument.restore()
|
||||
after(() => MockWebApi.getDocument.restore());
|
||||
|
||||
it "should return quickly(ish)", (done) ->
|
||||
start = Date.now()
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
res.statusCode.should.equal 500
|
||||
delta = Date.now() - start
|
||||
expect(delta).to.be.below 20000
|
||||
done()
|
||||
return it("should return quickly(ish)", function(done) {
|
||||
const start = Date.now();
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
res.statusCode.should.equal(500);
|
||||
const delta = Date.now() - start;
|
||||
expect(delta).to.be.below(20000);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function __range__(left, right, inclusive) {
|
||||
let range = [];
|
||||
let ascending = left < right;
|
||||
let end = !inclusive ? right : ascending ? right + 1 : right - 1;
|
||||
for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
|
||||
range.push(i);
|
||||
}
|
||||
return range;
|
||||
}
|
||||
@@ -1,69 +1,109 @@
|
||||
sinon = require "sinon"
|
||||
chai = require("chai")
|
||||
chai.should()
|
||||
expect = chai.expect
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require("sinon");
|
||||
const chai = require("chai");
|
||||
chai.should();
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
|
||||
MockWebApi = require "./helpers/MockWebApi"
|
||||
DocUpdaterClient = require "./helpers/DocUpdaterClient"
|
||||
DocUpdaterApp = require "./helpers/DocUpdaterApp"
|
||||
const MockWebApi = require("./helpers/MockWebApi");
|
||||
const DocUpdaterClient = require("./helpers/DocUpdaterClient");
|
||||
const DocUpdaterApp = require("./helpers/DocUpdaterApp");
|
||||
|
||||
describe "Getting documents for project", ->
|
||||
before (done) ->
|
||||
@lines = ["one", "two", "three"]
|
||||
@version = 42
|
||||
DocUpdaterApp.ensureRunning(done)
|
||||
describe("Getting documents for project", function() {
|
||||
before(function(done) {
|
||||
this.lines = ["one", "two", "three"];
|
||||
this.version = 42;
|
||||
return DocUpdaterApp.ensureRunning(done);
|
||||
});
|
||||
|
||||
describe "when project state hash does not match", ->
|
||||
before (done) ->
|
||||
@projectStateHash = DocUpdaterClient.randomId()
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
describe("when project state hash does not match", function() {
|
||||
before(function(done) {
|
||||
this.projectStateHash = DocUpdaterClient.randomId();
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.getProjectDocs @project_id, @projectStateHash, (error, @res, @returnedDocs) =>
|
||||
done()
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.getProjectDocs(this.project_id, this.projectStateHash, (error, res, returnedDocs) => {
|
||||
this.res = res;
|
||||
this.returnedDocs = returnedDocs;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it "should return a 409 Conflict response", ->
|
||||
@res.statusCode.should.equal 409
|
||||
return it("should return a 409 Conflict response", function() {
|
||||
return this.res.statusCode.should.equal(409);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe "when project state hash matches", ->
|
||||
before (done) ->
|
||||
@projectStateHash = DocUpdaterClient.randomId()
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
describe("when project state hash matches", function() {
|
||||
before(function(done) {
|
||||
this.projectStateHash = DocUpdaterClient.randomId();
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.getProjectDocs @project_id, @projectStateHash, (error, @res0, @returnedDocs0) =>
|
||||
# set the hash
|
||||
DocUpdaterClient.getProjectDocs @project_id, @projectStateHash, (error, @res, @returnedDocs) =>
|
||||
# the hash should now match
|
||||
done()
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.getProjectDocs(this.project_id, this.projectStateHash, (error, res0, returnedDocs0) => {
|
||||
// set the hash
|
||||
this.res0 = res0;
|
||||
this.returnedDocs0 = returnedDocs0;
|
||||
return DocUpdaterClient.getProjectDocs(this.project_id, this.projectStateHash, (error, res, returnedDocs) => {
|
||||
// the hash should now match
|
||||
this.res = res;
|
||||
this.returnedDocs = returnedDocs;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it "should return a 200 response", ->
|
||||
@res.statusCode.should.equal 200
|
||||
it("should return a 200 response", function() {
|
||||
return this.res.statusCode.should.equal(200);
|
||||
});
|
||||
|
||||
it "should return the documents", ->
|
||||
@returnedDocs.should.deep.equal [ {_id: @doc_id, lines: @lines, v: @version} ]
|
||||
return it("should return the documents", function() {
|
||||
return this.returnedDocs.should.deep.equal([ {_id: this.doc_id, lines: this.lines, v: this.version} ]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe "when the doc has been removed", ->
|
||||
before (done) ->
|
||||
@projectStateHash = DocUpdaterClient.randomId()
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
return describe("when the doc has been removed", function() {
|
||||
before(function(done) {
|
||||
this.projectStateHash = DocUpdaterClient.randomId();
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.getProjectDocs @project_id, @projectStateHash, (error, @res0, @returnedDocs0) =>
|
||||
# set the hash
|
||||
DocUpdaterClient.deleteDoc @project_id, @doc_id, (error, res, body) =>
|
||||
# delete the doc
|
||||
DocUpdaterClient.getProjectDocs @project_id, @projectStateHash, (error, @res, @returnedDocs) =>
|
||||
# the hash would match, but the doc has been deleted
|
||||
done()
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.getProjectDocs(this.project_id, this.projectStateHash, (error, res0, returnedDocs0) => {
|
||||
// set the hash
|
||||
this.res0 = res0;
|
||||
this.returnedDocs0 = returnedDocs0;
|
||||
return DocUpdaterClient.deleteDoc(this.project_id, this.doc_id, (error, res, body) => {
|
||||
// delete the doc
|
||||
return DocUpdaterClient.getProjectDocs(this.project_id, this.projectStateHash, (error, res1, returnedDocs) => {
|
||||
// the hash would match, but the doc has been deleted
|
||||
this.res = res1;
|
||||
this.returnedDocs = returnedDocs;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it "should return a 409 Conflict response", ->
|
||||
@res.statusCode.should.equal 409
|
||||
return it("should return a 409 Conflict response", function() {
|
||||
return this.res.statusCode.should.equal(409);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,372 +1,467 @@
|
||||
sinon = require "sinon"
|
||||
chai = require("chai")
|
||||
chai.should()
|
||||
expect = chai.expect
|
||||
async = require "async"
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require("sinon");
|
||||
const chai = require("chai");
|
||||
chai.should();
|
||||
const {
|
||||
expect
|
||||
} = chai;
|
||||
const async = require("async");
|
||||
|
||||
{db, ObjectId} = require "../../../app/js/mongojs"
|
||||
MockWebApi = require "./helpers/MockWebApi"
|
||||
DocUpdaterClient = require "./helpers/DocUpdaterClient"
|
||||
DocUpdaterApp = require "./helpers/DocUpdaterApp"
|
||||
const {db, ObjectId} = require("../../../app/js/mongojs");
|
||||
const MockWebApi = require("./helpers/MockWebApi");
|
||||
const DocUpdaterClient = require("./helpers/DocUpdaterClient");
|
||||
const DocUpdaterApp = require("./helpers/DocUpdaterApp");
|
||||
|
||||
describe "Ranges", ->
|
||||
before (done) ->
|
||||
DocUpdaterApp.ensureRunning done
|
||||
describe("Ranges", function() {
|
||||
before(done => DocUpdaterApp.ensureRunning(done));
|
||||
|
||||
describe "tracking changes from ops", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@user_id = DocUpdaterClient.randomId()
|
||||
@id_seed = "587357bd35e64f6157"
|
||||
@doc = {
|
||||
id: DocUpdaterClient.randomId()
|
||||
describe("tracking changes from ops", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.user_id = DocUpdaterClient.randomId();
|
||||
this.id_seed = "587357bd35e64f6157";
|
||||
this.doc = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
lines: ["aaa"]
|
||||
}
|
||||
@updates = [{
|
||||
doc: @doc.id
|
||||
op: [{ i: "123", p: 1 }]
|
||||
v: 0
|
||||
meta: { user_id: @user_id }
|
||||
};
|
||||
this.updates = [{
|
||||
doc: this.doc.id,
|
||||
op: [{ i: "123", p: 1 }],
|
||||
v: 0,
|
||||
meta: { user_id: this.user_id }
|
||||
}, {
|
||||
doc: @doc.id
|
||||
op: [{ i: "456", p: 5 }]
|
||||
v: 1
|
||||
meta: { user_id: @user_id, tc: @id_seed }
|
||||
doc: this.doc.id,
|
||||
op: [{ i: "456", p: 5 }],
|
||||
v: 1,
|
||||
meta: { user_id: this.user_id, tc: this.id_seed }
|
||||
}, {
|
||||
doc: @doc.id
|
||||
op: [{ d: "12", p: 1 }]
|
||||
v: 2
|
||||
meta: { user_id: @user_id }
|
||||
}]
|
||||
MockWebApi.insertDoc @project_id, @doc.id, {
|
||||
lines: @doc.lines
|
||||
doc: this.doc.id,
|
||||
op: [{ d: "12", p: 1 }],
|
||||
v: 2,
|
||||
meta: { user_id: this.user_id }
|
||||
}];
|
||||
MockWebApi.insertDoc(this.project_id, this.doc.id, {
|
||||
lines: this.doc.lines,
|
||||
version: 0
|
||||
});
|
||||
const jobs = [];
|
||||
for (let update of Array.from(this.updates)) {
|
||||
(update => {
|
||||
return jobs.push(callback => DocUpdaterClient.sendUpdate(this.project_id, this.doc.id, update, callback));
|
||||
})(update);
|
||||
}
|
||||
jobs = []
|
||||
for update in @updates
|
||||
do (update) =>
|
||||
jobs.push (callback) => DocUpdaterClient.sendUpdate @project_id, @doc.id, update, callback
|
||||
|
||||
DocUpdaterApp.ensureRunning (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc.id, (error) =>
|
||||
throw error if error?
|
||||
async.series jobs, (error) ->
|
||||
throw error if error?
|
||||
done()
|
||||
return DocUpdaterApp.ensureRunning(error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc.id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return async.series(jobs, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it "should update the ranges", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc.id, (error, res, data) =>
|
||||
throw error if error?
|
||||
ranges = data.ranges
|
||||
change = ranges.changes[0]
|
||||
change.op.should.deep.equal { i: "456", p: 3 }
|
||||
change.id.should.equal @id_seed + "000001"
|
||||
change.metadata.user_id.should.equal @user_id
|
||||
done()
|
||||
it("should update the ranges", function(done) {
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc.id, (error, res, data) => {
|
||||
if (error != null) { throw error; }
|
||||
const {
|
||||
ranges
|
||||
} = data;
|
||||
const change = ranges.changes[0];
|
||||
change.op.should.deep.equal({ i: "456", p: 3 });
|
||||
change.id.should.equal(this.id_seed + "000001");
|
||||
change.metadata.user_id.should.equal(this.user_id);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
describe "Adding comments", ->
|
||||
describe "standalone", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@user_id = DocUpdaterClient.randomId()
|
||||
@doc = {
|
||||
id: DocUpdaterClient.randomId()
|
||||
return describe("Adding comments", function() {
|
||||
describe("standalone", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.user_id = DocUpdaterClient.randomId();
|
||||
this.doc = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
lines: ["foo bar baz"]
|
||||
}
|
||||
@updates = [{
|
||||
doc: @doc.id
|
||||
op: [{ c: "bar", p: 4, t: @tid = DocUpdaterClient.randomId() }]
|
||||
};
|
||||
this.updates = [{
|
||||
doc: this.doc.id,
|
||||
op: [{ c: "bar", p: 4, t: (this.tid = DocUpdaterClient.randomId()) }],
|
||||
v: 0
|
||||
}]
|
||||
MockWebApi.insertDoc @project_id, @doc.id, {
|
||||
lines: @doc.lines
|
||||
}];
|
||||
MockWebApi.insertDoc(this.project_id, this.doc.id, {
|
||||
lines: this.doc.lines,
|
||||
version: 0
|
||||
});
|
||||
const jobs = [];
|
||||
for (let update of Array.from(this.updates)) {
|
||||
(update => {
|
||||
return jobs.push(callback => DocUpdaterClient.sendUpdate(this.project_id, this.doc.id, update, callback));
|
||||
})(update);
|
||||
}
|
||||
jobs = []
|
||||
for update in @updates
|
||||
do (update) =>
|
||||
jobs.push (callback) => DocUpdaterClient.sendUpdate @project_id, @doc.id, update, callback
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc.id, (error) =>
|
||||
throw error if error?
|
||||
async.series jobs, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc.id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return async.series(jobs, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it "should update the ranges", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc.id, (error, res, data) =>
|
||||
throw error if error?
|
||||
ranges = data.ranges
|
||||
comment = ranges.comments[0]
|
||||
comment.op.should.deep.equal { c: "bar", p: 4, t: @tid }
|
||||
comment.id.should.equal @tid
|
||||
done()
|
||||
return it("should update the ranges", function(done) {
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc.id, (error, res, data) => {
|
||||
if (error != null) { throw error; }
|
||||
const {
|
||||
ranges
|
||||
} = data;
|
||||
const comment = ranges.comments[0];
|
||||
comment.op.should.deep.equal({ c: "bar", p: 4, t: this.tid });
|
||||
comment.id.should.equal(this.tid);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "with conflicting ops needing OT", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@user_id = DocUpdaterClient.randomId()
|
||||
@doc = {
|
||||
id: DocUpdaterClient.randomId()
|
||||
return describe("with conflicting ops needing OT", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.user_id = DocUpdaterClient.randomId();
|
||||
this.doc = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
lines: ["foo bar baz"]
|
||||
}
|
||||
@updates = [{
|
||||
doc: @doc.id
|
||||
op: [{ i: "ABC", p: 3 }]
|
||||
v: 0
|
||||
meta: { user_id: @user_id }
|
||||
};
|
||||
this.updates = [{
|
||||
doc: this.doc.id,
|
||||
op: [{ i: "ABC", p: 3 }],
|
||||
v: 0,
|
||||
meta: { user_id: this.user_id }
|
||||
}, {
|
||||
doc: @doc.id
|
||||
op: [{ c: "bar", p: 4, t: @tid = DocUpdaterClient.randomId() }]
|
||||
doc: this.doc.id,
|
||||
op: [{ c: "bar", p: 4, t: (this.tid = DocUpdaterClient.randomId()) }],
|
||||
v: 0
|
||||
}]
|
||||
MockWebApi.insertDoc @project_id, @doc.id, {
|
||||
lines: @doc.lines
|
||||
}];
|
||||
MockWebApi.insertDoc(this.project_id, this.doc.id, {
|
||||
lines: this.doc.lines,
|
||||
version: 0
|
||||
});
|
||||
const jobs = [];
|
||||
for (let update of Array.from(this.updates)) {
|
||||
(update => {
|
||||
return jobs.push(callback => DocUpdaterClient.sendUpdate(this.project_id, this.doc.id, update, callback));
|
||||
})(update);
|
||||
}
|
||||
jobs = []
|
||||
for update in @updates
|
||||
do (update) =>
|
||||
jobs.push (callback) => DocUpdaterClient.sendUpdate @project_id, @doc.id, update, callback
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc.id, (error) =>
|
||||
throw error if error?
|
||||
async.series jobs, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc.id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return async.series(jobs, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it "should update the comments with the OT shifted comment", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc.id, (error, res, data) =>
|
||||
throw error if error?
|
||||
ranges = data.ranges
|
||||
comment = ranges.comments[0]
|
||||
comment.op.should.deep.equal { c: "bar", p: 7, t: @tid }
|
||||
done()
|
||||
return it("should update the comments with the OT shifted comment", function(done) {
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc.id, (error, res, data) => {
|
||||
if (error != null) { throw error; }
|
||||
const {
|
||||
ranges
|
||||
} = data;
|
||||
const comment = ranges.comments[0];
|
||||
comment.op.should.deep.equal({ c: "bar", p: 7, t: this.tid });
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "Loading ranges from persistence layer", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@user_id = DocUpdaterClient.randomId()
|
||||
@id_seed = "587357bd35e64f6157"
|
||||
@doc = {
|
||||
id: DocUpdaterClient.randomId()
|
||||
describe("Loading ranges from persistence layer", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.user_id = DocUpdaterClient.randomId();
|
||||
this.id_seed = "587357bd35e64f6157";
|
||||
this.doc = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
lines: ["a123aa"]
|
||||
}
|
||||
@update = {
|
||||
doc: @doc.id
|
||||
op: [{ i: "456", p: 5 }]
|
||||
v: 0
|
||||
meta: { user_id: @user_id, tc: @id_seed }
|
||||
}
|
||||
MockWebApi.insertDoc @project_id, @doc.id, {
|
||||
lines: @doc.lines
|
||||
version: 0
|
||||
};
|
||||
this.update = {
|
||||
doc: this.doc.id,
|
||||
op: [{ i: "456", p: 5 }],
|
||||
v: 0,
|
||||
meta: { user_id: this.user_id, tc: this.id_seed }
|
||||
};
|
||||
MockWebApi.insertDoc(this.project_id, this.doc.id, {
|
||||
lines: this.doc.lines,
|
||||
version: 0,
|
||||
ranges: {
|
||||
changes: [{
|
||||
op: { i: "123", p: 1 }
|
||||
metadata:
|
||||
user_id: @user_id
|
||||
op: { i: "123", p: 1 },
|
||||
metadata: {
|
||||
user_id: this.user_id,
|
||||
ts: new Date()
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc.id, (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc.id, @update, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
});
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc.id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.sendUpdate(this.project_id, this.doc.id, this.update, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it "should have preloaded the existing ranges", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc.id, (error, res, data) =>
|
||||
throw error if error?
|
||||
{changes} = data.ranges
|
||||
changes[0].op.should.deep.equal { i: "123", p: 1 }
|
||||
changes[1].op.should.deep.equal { i: "456", p: 5 }
|
||||
done()
|
||||
it("should have preloaded the existing ranges", function(done) {
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc.id, (error, res, data) => {
|
||||
if (error != null) { throw error; }
|
||||
const {changes} = data.ranges;
|
||||
changes[0].op.should.deep.equal({ i: "123", p: 1 });
|
||||
changes[1].op.should.deep.equal({ i: "456", p: 5 });
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it "should flush the ranges to the persistence layer again", (done) ->
|
||||
DocUpdaterClient.flushDoc @project_id, @doc.id, (error) =>
|
||||
throw error if error?
|
||||
MockWebApi.getDocument @project_id, @doc.id, (error, doc) =>
|
||||
{changes} = doc.ranges
|
||||
changes[0].op.should.deep.equal { i: "123", p: 1 }
|
||||
changes[1].op.should.deep.equal { i: "456", p: 5 }
|
||||
done()
|
||||
return it("should flush the ranges to the persistence layer again", function(done) {
|
||||
return DocUpdaterClient.flushDoc(this.project_id, this.doc.id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return MockWebApi.getDocument(this.project_id, this.doc.id, (error, doc) => {
|
||||
const {changes} = doc.ranges;
|
||||
changes[0].op.should.deep.equal({ i: "123", p: 1 });
|
||||
changes[1].op.should.deep.equal({ i: "456", p: 5 });
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "accepting a change", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@user_id = DocUpdaterClient.randomId()
|
||||
@id_seed = "587357bd35e64f6157"
|
||||
@doc = {
|
||||
id: DocUpdaterClient.randomId()
|
||||
describe("accepting a change", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.user_id = DocUpdaterClient.randomId();
|
||||
this.id_seed = "587357bd35e64f6157";
|
||||
this.doc = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
lines: ["aaa"]
|
||||
}
|
||||
@update = {
|
||||
doc: @doc.id
|
||||
op: [{ i: "456", p: 1 }]
|
||||
v: 0
|
||||
meta: { user_id: @user_id, tc: @id_seed }
|
||||
}
|
||||
MockWebApi.insertDoc @project_id, @doc.id, {
|
||||
lines: @doc.lines
|
||||
};
|
||||
this.update = {
|
||||
doc: this.doc.id,
|
||||
op: [{ i: "456", p: 1 }],
|
||||
v: 0,
|
||||
meta: { user_id: this.user_id, tc: this.id_seed }
|
||||
};
|
||||
MockWebApi.insertDoc(this.project_id, this.doc.id, {
|
||||
lines: this.doc.lines,
|
||||
version: 0
|
||||
}
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc.id, (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc.id, @update, (error) =>
|
||||
throw error if error?
|
||||
setTimeout () =>
|
||||
DocUpdaterClient.getDoc @project_id, @doc.id, (error, res, data) =>
|
||||
throw error if error?
|
||||
ranges = data.ranges
|
||||
change = ranges.changes[0]
|
||||
change.op.should.deep.equal { i: "456", p: 1 }
|
||||
change.id.should.equal @id_seed + "000001"
|
||||
change.metadata.user_id.should.equal @user_id
|
||||
done()
|
||||
, 200
|
||||
});
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc.id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.sendUpdate(this.project_id, this.doc.id, this.update, error => {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(() => {
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc.id, (error, res, data) => {
|
||||
if (error != null) { throw error; }
|
||||
const {
|
||||
ranges
|
||||
} = data;
|
||||
const change = ranges.changes[0];
|
||||
change.op.should.deep.equal({ i: "456", p: 1 });
|
||||
change.id.should.equal(this.id_seed + "000001");
|
||||
change.metadata.user_id.should.equal(this.user_id);
|
||||
return done();
|
||||
});
|
||||
}
|
||||
, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it "should remove the change after accepting", (done) ->
|
||||
DocUpdaterClient.acceptChange @project_id, @doc.id, @id_seed + "000001", (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.getDoc @project_id, @doc.id, (error, res, data) =>
|
||||
throw error if error?
|
||||
expect(data.ranges.changes).to.be.undefined
|
||||
done()
|
||||
return it("should remove the change after accepting", function(done) {
|
||||
return DocUpdaterClient.acceptChange(this.project_id, this.doc.id, this.id_seed + "000001", error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc.id, (error, res, data) => {
|
||||
if (error != null) { throw error; }
|
||||
expect(data.ranges.changes).to.be.undefined;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "deleting a comment range", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@user_id = DocUpdaterClient.randomId()
|
||||
@doc = {
|
||||
id: DocUpdaterClient.randomId()
|
||||
describe("deleting a comment range", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.user_id = DocUpdaterClient.randomId();
|
||||
this.doc = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
lines: ["foo bar"]
|
||||
}
|
||||
@update = {
|
||||
doc: @doc.id
|
||||
op: [{ c: "bar", p: 4, t: @tid = DocUpdaterClient.randomId() }]
|
||||
};
|
||||
this.update = {
|
||||
doc: this.doc.id,
|
||||
op: [{ c: "bar", p: 4, t: (this.tid = DocUpdaterClient.randomId()) }],
|
||||
v: 0
|
||||
}
|
||||
MockWebApi.insertDoc @project_id, @doc.id, {
|
||||
lines: @doc.lines
|
||||
};
|
||||
MockWebApi.insertDoc(this.project_id, this.doc.id, {
|
||||
lines: this.doc.lines,
|
||||
version: 0
|
||||
}
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc.id, (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc.id, @update, (error) =>
|
||||
throw error if error?
|
||||
setTimeout () =>
|
||||
DocUpdaterClient.getDoc @project_id, @doc.id, (error, res, data) =>
|
||||
throw error if error?
|
||||
ranges = data.ranges
|
||||
change = ranges.comments[0]
|
||||
change.op.should.deep.equal { c: "bar", p: 4, t: @tid }
|
||||
change.id.should.equal @tid
|
||||
done()
|
||||
, 200
|
||||
});
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc.id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.sendUpdate(this.project_id, this.doc.id, this.update, error => {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(() => {
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc.id, (error, res, data) => {
|
||||
if (error != null) { throw error; }
|
||||
const {
|
||||
ranges
|
||||
} = data;
|
||||
const change = ranges.comments[0];
|
||||
change.op.should.deep.equal({ c: "bar", p: 4, t: this.tid });
|
||||
change.id.should.equal(this.tid);
|
||||
return done();
|
||||
});
|
||||
}
|
||||
, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it "should remove the comment range", (done) ->
|
||||
DocUpdaterClient.removeComment @project_id, @doc.id, @tid, (error, res) =>
|
||||
throw error if error?
|
||||
expect(res.statusCode).to.equal 204
|
||||
DocUpdaterClient.getDoc @project_id, @doc.id, (error, res, data) =>
|
||||
throw error if error?
|
||||
expect(data.ranges.comments).to.be.undefined
|
||||
done()
|
||||
return it("should remove the comment range", function(done) {
|
||||
return DocUpdaterClient.removeComment(this.project_id, this.doc.id, this.tid, (error, res) => {
|
||||
if (error != null) { throw error; }
|
||||
expect(res.statusCode).to.equal(204);
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc.id, (error, res, data) => {
|
||||
if (error != null) { throw error; }
|
||||
expect(data.ranges.comments).to.be.undefined;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "tripping range size limit", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@user_id = DocUpdaterClient.randomId()
|
||||
@id_seed = DocUpdaterClient.randomId()
|
||||
@doc = {
|
||||
id: DocUpdaterClient.randomId()
|
||||
describe("tripping range size limit", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.user_id = DocUpdaterClient.randomId();
|
||||
this.id_seed = DocUpdaterClient.randomId();
|
||||
this.doc = {
|
||||
id: DocUpdaterClient.randomId(),
|
||||
lines: ["aaa"]
|
||||
}
|
||||
@i = new Array(3 * 1024 * 1024).join("a")
|
||||
@updates = [{
|
||||
doc: @doc.id
|
||||
op: [{ i: @i, p: 1 }]
|
||||
v: 0
|
||||
meta: { user_id: @user_id, tc: @id_seed }
|
||||
}]
|
||||
MockWebApi.insertDoc @project_id, @doc.id, {
|
||||
lines: @doc.lines
|
||||
};
|
||||
this.i = new Array(3 * 1024 * 1024).join("a");
|
||||
this.updates = [{
|
||||
doc: this.doc.id,
|
||||
op: [{ i: this.i, p: 1 }],
|
||||
v: 0,
|
||||
meta: { user_id: this.user_id, tc: this.id_seed }
|
||||
}];
|
||||
MockWebApi.insertDoc(this.project_id, this.doc.id, {
|
||||
lines: this.doc.lines,
|
||||
version: 0
|
||||
});
|
||||
const jobs = [];
|
||||
for (let update of Array.from(this.updates)) {
|
||||
(update => {
|
||||
return jobs.push(callback => DocUpdaterClient.sendUpdate(this.project_id, this.doc.id, update, callback));
|
||||
})(update);
|
||||
}
|
||||
jobs = []
|
||||
for update in @updates
|
||||
do (update) =>
|
||||
jobs.push (callback) => DocUpdaterClient.sendUpdate @project_id, @doc.id, update, callback
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc.id, (error) =>
|
||||
throw error if error?
|
||||
async.series jobs, (error) ->
|
||||
throw error if error?
|
||||
setTimeout done, 200
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc.id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return async.series(jobs, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it "should not update the ranges", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc.id, (error, res, data) =>
|
||||
throw error if error?
|
||||
ranges = data.ranges
|
||||
expect(ranges.changes).to.be.undefined
|
||||
done()
|
||||
return it("should not update the ranges", function(done) {
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc.id, (error, res, data) => {
|
||||
if (error != null) { throw error; }
|
||||
const {
|
||||
ranges
|
||||
} = data;
|
||||
expect(ranges.changes).to.be.undefined;
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe "deleting text surrounding a comment", ->
|
||||
before (done) ->
|
||||
@project_id = DocUpdaterClient.randomId()
|
||||
@user_id = DocUpdaterClient.randomId()
|
||||
@doc_id = DocUpdaterClient.randomId()
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {
|
||||
lines: ["foo bar baz"]
|
||||
version: 0
|
||||
return describe("deleting text surrounding a comment", function() {
|
||||
before(function(done) {
|
||||
this.project_id = DocUpdaterClient.randomId();
|
||||
this.user_id = DocUpdaterClient.randomId();
|
||||
this.doc_id = DocUpdaterClient.randomId();
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
||||
lines: ["foo bar baz"],
|
||||
version: 0,
|
||||
ranges: {
|
||||
comments: [{
|
||||
op: { c: "a", p: 5, tid: @tid = DocUpdaterClient.randomId() }
|
||||
metadata:
|
||||
user_id: @user_id
|
||||
op: { c: "a", p: 5, tid: (this.tid = DocUpdaterClient.randomId()) },
|
||||
metadata: {
|
||||
user_id: this.user_id,
|
||||
ts: new Date()
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
@updates = [{
|
||||
doc: @doc_id
|
||||
op: [{ d: "foo ", p: 0 }]
|
||||
v: 0
|
||||
meta: { user_id: @user_id }
|
||||
});
|
||||
this.updates = [{
|
||||
doc: this.doc_id,
|
||||
op: [{ d: "foo ", p: 0 }],
|
||||
v: 0,
|
||||
meta: { user_id: this.user_id }
|
||||
}, {
|
||||
doc: @doc_id
|
||||
op: [{ d: "bar ", p: 0 }]
|
||||
v: 1
|
||||
meta: { user_id: @user_id }
|
||||
}]
|
||||
jobs = []
|
||||
for update in @updates
|
||||
do (update) =>
|
||||
jobs.push (callback) => DocUpdaterClient.sendUpdate @project_id, @doc_id, update, callback
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||
throw error if error?
|
||||
async.series jobs, (error) ->
|
||||
throw error if error?
|
||||
setTimeout () =>
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, data) =>
|
||||
throw error if error?
|
||||
done()
|
||||
, 200
|
||||
doc: this.doc_id,
|
||||
op: [{ d: "bar ", p: 0 }],
|
||||
v: 1,
|
||||
meta: { user_id: this.user_id }
|
||||
}];
|
||||
const jobs = [];
|
||||
for (let update of Array.from(this.updates)) {
|
||||
(update => {
|
||||
return jobs.push(callback => DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, update, callback));
|
||||
})(update);
|
||||
}
|
||||
return DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return async.series(jobs, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(() => {
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, data) => {
|
||||
if (error != null) { throw error; }
|
||||
return done();
|
||||
});
|
||||
}
|
||||
, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it "should write a snapshot from before the destructive change", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, data) =>
|
||||
return done(error) if error?
|
||||
db.docSnapshots.find {
|
||||
project_id: ObjectId(@project_id),
|
||||
doc_id: ObjectId(@doc_id)
|
||||
}, (error, docSnapshots) =>
|
||||
return done(error) if error?
|
||||
expect(docSnapshots.length).to.equal 1
|
||||
expect(docSnapshots[0].version).to.equal 1
|
||||
expect(docSnapshots[0].lines).to.deep.equal ["bar baz"]
|
||||
expect(docSnapshots[0].ranges.comments[0].op).to.deep.equal {
|
||||
return it("should write a snapshot from before the destructive change", function(done) {
|
||||
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, data) => {
|
||||
if (error != null) { return done(error); }
|
||||
return db.docSnapshots.find({
|
||||
project_id: ObjectId(this.project_id),
|
||||
doc_id: ObjectId(this.doc_id)
|
||||
}, (error, docSnapshots) => {
|
||||
if (error != null) { return done(error); }
|
||||
expect(docSnapshots.length).to.equal(1);
|
||||
expect(docSnapshots[0].version).to.equal(1);
|
||||
expect(docSnapshots[0].lines).to.deep.equal(["bar baz"]);
|
||||
expect(docSnapshots[0].ranges.comments[0].op).to.deep.equal({
|
||||
c: "a",
|
||||
p: 1,
|
||||
tid: @tid
|
||||
}
|
||||
done()
|
||||
tid: this.tid
|
||||
});
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,248 +1,320 @@
|
||||
sinon = require "sinon"
|
||||
chai = require("chai")
|
||||
chai.should()
|
||||
expect = require("chai").expect
|
||||
Settings = require('settings-sharelatex')
|
||||
rclient_du = require("redis-sharelatex").createClient(Settings.redis.documentupdater)
|
||||
Keys = Settings.redis.documentupdater.key_schema
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require("sinon");
|
||||
const chai = require("chai");
|
||||
chai.should();
|
||||
const {
|
||||
expect
|
||||
} = require("chai");
|
||||
const Settings = require('settings-sharelatex');
|
||||
const rclient_du = require("redis-sharelatex").createClient(Settings.redis.documentupdater);
|
||||
const Keys = Settings.redis.documentupdater.key_schema;
|
||||
|
||||
MockTrackChangesApi = require "./helpers/MockTrackChangesApi"
|
||||
MockProjectHistoryApi = require "./helpers/MockProjectHistoryApi"
|
||||
MockWebApi = require "./helpers/MockWebApi"
|
||||
DocUpdaterClient = require "./helpers/DocUpdaterClient"
|
||||
DocUpdaterApp = require "./helpers/DocUpdaterApp"
|
||||
const MockTrackChangesApi = require("./helpers/MockTrackChangesApi");
|
||||
const MockProjectHistoryApi = require("./helpers/MockProjectHistoryApi");
|
||||
const MockWebApi = require("./helpers/MockWebApi");
|
||||
const DocUpdaterClient = require("./helpers/DocUpdaterClient");
|
||||
const DocUpdaterApp = require("./helpers/DocUpdaterApp");
|
||||
|
||||
describe "Setting a document", ->
|
||||
before (done) ->
|
||||
@lines = ["one", "two", "three"]
|
||||
@version = 42
|
||||
@update =
|
||||
doc: @doc_id
|
||||
describe("Setting a document", function() {
|
||||
before(function(done) {
|
||||
this.lines = ["one", "two", "three"];
|
||||
this.version = 42;
|
||||
this.update = {
|
||||
doc: this.doc_id,
|
||||
op: [{
|
||||
i: "one and a half\n"
|
||||
i: "one and a half\n",
|
||||
p: 4
|
||||
}]
|
||||
v: @version
|
||||
@result = ["one", "one and a half", "two", "three"]
|
||||
@newLines = ["these", "are", "the", "new", "lines"]
|
||||
@source = "dropbox"
|
||||
@user_id = "user-id-123"
|
||||
}],
|
||||
v: this.version
|
||||
};
|
||||
this.result = ["one", "one and a half", "two", "three"];
|
||||
this.newLines = ["these", "are", "the", "new", "lines"];
|
||||
this.source = "dropbox";
|
||||
this.user_id = "user-id-123";
|
||||
|
||||
sinon.spy MockTrackChangesApi, "flushDoc"
|
||||
sinon.spy MockProjectHistoryApi, "flushProject"
|
||||
sinon.spy MockWebApi, "setDocument"
|
||||
DocUpdaterApp.ensureRunning(done)
|
||||
sinon.spy(MockTrackChangesApi, "flushDoc");
|
||||
sinon.spy(MockProjectHistoryApi, "flushProject");
|
||||
sinon.spy(MockWebApi, "setDocument");
|
||||
return DocUpdaterApp.ensureRunning(done);
|
||||
});
|
||||
|
||||
after ->
|
||||
MockTrackChangesApi.flushDoc.restore()
|
||||
MockProjectHistoryApi.flushProject.restore()
|
||||
MockWebApi.setDocument.restore()
|
||||
after(function() {
|
||||
MockTrackChangesApi.flushDoc.restore();
|
||||
MockProjectHistoryApi.flushProject.restore();
|
||||
return MockWebApi.setDocument.restore();
|
||||
});
|
||||
|
||||
describe "when the updated doc exists in the doc updater", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, lines: @lines, version: @version
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) =>
|
||||
throw error if error?
|
||||
setTimeout () =>
|
||||
DocUpdaterClient.setDocLines @project_id, @doc_id, @newLines, @source, @user_id, false, (error, res, body) =>
|
||||
@statusCode = res.statusCode
|
||||
done()
|
||||
, 200
|
||||
return null
|
||||
describe("when the updated doc exists in the doc updater", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.update, error => {
|
||||
if (error != null) { throw error; }
|
||||
return setTimeout(() => {
|
||||
return DocUpdaterClient.setDocLines(this.project_id, this.doc_id, this.newLines, this.source, this.user_id, false, (error, res, body) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return done();
|
||||
});
|
||||
}
|
||||
, 200);
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
after ->
|
||||
MockTrackChangesApi.flushDoc.reset()
|
||||
MockProjectHistoryApi.flushProject.reset()
|
||||
MockWebApi.setDocument.reset()
|
||||
after(function() {
|
||||
MockTrackChangesApi.flushDoc.reset();
|
||||
MockProjectHistoryApi.flushProject.reset();
|
||||
return MockWebApi.setDocument.reset();
|
||||
});
|
||||
|
||||
it "should return a 204 status code", ->
|
||||
@statusCode.should.equal 204
|
||||
it("should return a 204 status code", function() {
|
||||
return this.statusCode.should.equal(204);
|
||||
});
|
||||
|
||||
it "should send the updated doc lines and version to the web api", ->
|
||||
MockWebApi.setDocument
|
||||
.calledWith(@project_id, @doc_id, @newLines)
|
||||
.should.equal true
|
||||
it("should send the updated doc lines and version to the web api", function() {
|
||||
return MockWebApi.setDocument
|
||||
.calledWith(this.project_id, this.doc_id, this.newLines)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should update the lines in the doc updater", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
doc.lines.should.deep.equal @newLines
|
||||
done()
|
||||
return null
|
||||
it("should update the lines in the doc updater", function(done) {
|
||||
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
doc.lines.should.deep.equal(this.newLines);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should bump the version in the doc updater", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
|
||||
doc.version.should.equal @version + 2
|
||||
done()
|
||||
return null
|
||||
it("should bump the version in the doc updater", function(done) {
|
||||
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => {
|
||||
doc.version.should.equal(this.version + 2);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
it "should leave the document in redis", (done) ->
|
||||
rclient_du.get Keys.docLines({doc_id: @doc_id}), (error, lines) =>
|
||||
throw error if error?
|
||||
expect(JSON.parse(lines)).to.deep.equal @newLines
|
||||
done()
|
||||
return null
|
||||
return it("should leave the document in redis", function(done) {
|
||||
rclient_du.get(Keys.docLines({doc_id: this.doc_id}), (error, lines) => {
|
||||
if (error != null) { throw error; }
|
||||
expect(JSON.parse(lines)).to.deep.equal(this.newLines);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the updated doc does not exist in the doc updater", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
DocUpdaterClient.setDocLines @project_id, @doc_id, @newLines, @source, @user_id, false, (error, res, body) =>
|
||||
@statusCode = res.statusCode
|
||||
setTimeout done, 200
|
||||
return null
|
||||
describe("when the updated doc does not exist in the doc updater", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
DocUpdaterClient.setDocLines(this.project_id, this.doc_id, this.newLines, this.source, this.user_id, false, (error, res, body) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
after ->
|
||||
MockTrackChangesApi.flushDoc.reset()
|
||||
MockProjectHistoryApi.flushProject.reset()
|
||||
MockWebApi.setDocument.reset()
|
||||
after(function() {
|
||||
MockTrackChangesApi.flushDoc.reset();
|
||||
MockProjectHistoryApi.flushProject.reset();
|
||||
return MockWebApi.setDocument.reset();
|
||||
});
|
||||
|
||||
it "should return a 204 status code", ->
|
||||
@statusCode.should.equal 204
|
||||
it("should return a 204 status code", function() {
|
||||
return this.statusCode.should.equal(204);
|
||||
});
|
||||
|
||||
it "should send the updated doc lines to the web api", ->
|
||||
MockWebApi.setDocument
|
||||
.calledWith(@project_id, @doc_id, @newLines)
|
||||
.should.equal true
|
||||
it("should send the updated doc lines to the web api", function() {
|
||||
return MockWebApi.setDocument
|
||||
.calledWith(this.project_id, this.doc_id, this.newLines)
|
||||
.should.equal(true);
|
||||
});
|
||||
|
||||
it "should flush track changes", ->
|
||||
MockTrackChangesApi.flushDoc.calledWith(@doc_id).should.equal true
|
||||
it("should flush track changes", function() {
|
||||
return MockTrackChangesApi.flushDoc.calledWith(this.doc_id).should.equal(true);
|
||||
});
|
||||
|
||||
it "should flush project history", ->
|
||||
MockProjectHistoryApi.flushProject.calledWith(@project_id).should.equal true
|
||||
it("should flush project history", function() {
|
||||
return MockProjectHistoryApi.flushProject.calledWith(this.project_id).should.equal(true);
|
||||
});
|
||||
|
||||
it "should remove the document from redis", (done) ->
|
||||
rclient_du.get Keys.docLines({doc_id: @doc_id}), (error, lines) =>
|
||||
throw error if error?
|
||||
expect(lines).to.not.exist
|
||||
done()
|
||||
return null
|
||||
return it("should remove the document from redis", function(done) {
|
||||
rclient_du.get(Keys.docLines({doc_id: this.doc_id}), (error, lines) => {
|
||||
if (error != null) { throw error; }
|
||||
expect(lines).to.not.exist;
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "when the updated doc is too large for the body parser", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
@newLines = []
|
||||
while JSON.stringify(@newLines).length < Settings.max_doc_length + 64 * 1024
|
||||
@newLines.push("(a long line of text)".repeat(10000))
|
||||
DocUpdaterClient.setDocLines @project_id, @doc_id, @newLines, @source, @user_id, false, (error, res, body) =>
|
||||
@statusCode = res.statusCode
|
||||
setTimeout done, 200
|
||||
return null
|
||||
describe("when the updated doc is too large for the body parser", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
this.newLines = [];
|
||||
while (JSON.stringify(this.newLines).length < (Settings.max_doc_length + (64 * 1024))) {
|
||||
this.newLines.push("(a long line of text)".repeat(10000));
|
||||
}
|
||||
DocUpdaterClient.setDocLines(this.project_id, this.doc_id, this.newLines, this.source, this.user_id, false, (error, res, body) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
after ->
|
||||
MockTrackChangesApi.flushDoc.reset()
|
||||
MockProjectHistoryApi.flushProject.reset()
|
||||
MockWebApi.setDocument.reset()
|
||||
after(function() {
|
||||
MockTrackChangesApi.flushDoc.reset();
|
||||
MockProjectHistoryApi.flushProject.reset();
|
||||
return MockWebApi.setDocument.reset();
|
||||
});
|
||||
|
||||
it "should return a 413 status code", ->
|
||||
@statusCode.should.equal 413
|
||||
it("should return a 413 status code", function() {
|
||||
return this.statusCode.should.equal(413);
|
||||
});
|
||||
|
||||
it "should not send the updated doc lines to the web api", ->
|
||||
MockWebApi.setDocument.called.should.equal false
|
||||
it("should not send the updated doc lines to the web api", () => MockWebApi.setDocument.called.should.equal(false));
|
||||
|
||||
it "should not flush track changes", ->
|
||||
MockTrackChangesApi.flushDoc.called.should.equal false
|
||||
it("should not flush track changes", () => MockTrackChangesApi.flushDoc.called.should.equal(false));
|
||||
|
||||
it "should not flush project history", ->
|
||||
MockProjectHistoryApi.flushProject.called.should.equal false
|
||||
return it("should not flush project history", () => MockProjectHistoryApi.flushProject.called.should.equal(false));
|
||||
});
|
||||
|
||||
describe "when the updated doc is large but under the bodyParser and HTTPController size limit", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
describe("when the updated doc is large but under the bodyParser and HTTPController size limit", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
|
||||
@newLines = []
|
||||
while JSON.stringify(@newLines).length < 2 * 1024 * 1024 # limit in HTTPController
|
||||
@newLines.push("(a long line of text)".repeat(10000))
|
||||
@newLines.pop() # remove the line which took it over the limit
|
||||
DocUpdaterClient.setDocLines @project_id, @doc_id, @newLines, @source, @user_id, false, (error, res, body) =>
|
||||
@statusCode = res.statusCode
|
||||
setTimeout done, 200
|
||||
return null
|
||||
this.newLines = [];
|
||||
while (JSON.stringify(this.newLines).length < (2 * 1024 * 1024)) { // limit in HTTPController
|
||||
this.newLines.push("(a long line of text)".repeat(10000));
|
||||
}
|
||||
this.newLines.pop(); // remove the line which took it over the limit
|
||||
DocUpdaterClient.setDocLines(this.project_id, this.doc_id, this.newLines, this.source, this.user_id, false, (error, res, body) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
after ->
|
||||
MockTrackChangesApi.flushDoc.reset()
|
||||
MockProjectHistoryApi.flushProject.reset()
|
||||
MockWebApi.setDocument.reset()
|
||||
after(function() {
|
||||
MockTrackChangesApi.flushDoc.reset();
|
||||
MockProjectHistoryApi.flushProject.reset();
|
||||
return MockWebApi.setDocument.reset();
|
||||
});
|
||||
|
||||
it "should return a 204 status code", ->
|
||||
@statusCode.should.equal 204
|
||||
it("should return a 204 status code", function() {
|
||||
return this.statusCode.should.equal(204);
|
||||
});
|
||||
|
||||
it "should send the updated doc lines to the web api", ->
|
||||
MockWebApi.setDocument
|
||||
.calledWith(@project_id, @doc_id, @newLines)
|
||||
.should.equal true
|
||||
return it("should send the updated doc lines to the web api", function() {
|
||||
return MockWebApi.setDocument
|
||||
.calledWith(this.project_id, this.doc_id, this.newLines)
|
||||
.should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe "with track changes", ->
|
||||
before ->
|
||||
@lines = ["one", "one and a half", "two", "three"]
|
||||
@id_seed = "587357bd35e64f6157"
|
||||
@update =
|
||||
doc: @doc_id
|
||||
return describe("with track changes", function() {
|
||||
before(function() {
|
||||
this.lines = ["one", "one and a half", "two", "three"];
|
||||
this.id_seed = "587357bd35e64f6157";
|
||||
return this.update = {
|
||||
doc: this.doc_id,
|
||||
op: [{
|
||||
d: "one and a half\n"
|
||||
d: "one and a half\n",
|
||||
p: 4
|
||||
}]
|
||||
meta:
|
||||
tc: @id_seed
|
||||
user_id: @user_id
|
||||
v: @version
|
||||
}],
|
||||
meta: {
|
||||
tc: this.id_seed,
|
||||
user_id: this.user_id
|
||||
},
|
||||
v: this.version
|
||||
};
|
||||
});
|
||||
|
||||
describe "with the undo flag", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) =>
|
||||
throw error if error?
|
||||
# Go back to old lines, with undo flag
|
||||
DocUpdaterClient.setDocLines @project_id, @doc_id, @lines, @source, @user_id, true, (error, res, body) =>
|
||||
@statusCode = res.statusCode
|
||||
setTimeout done, 200
|
||||
return null
|
||||
describe("with the undo flag", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.update, error => {
|
||||
if (error != null) { throw error; }
|
||||
// Go back to old lines, with undo flag
|
||||
return DocUpdaterClient.setDocLines(this.project_id, this.doc_id, this.lines, this.source, this.user_id, true, (error, res, body) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
after ->
|
||||
MockTrackChangesApi.flushDoc.reset()
|
||||
MockProjectHistoryApi.flushProject.reset()
|
||||
MockWebApi.setDocument.reset()
|
||||
after(function() {
|
||||
MockTrackChangesApi.flushDoc.reset();
|
||||
MockProjectHistoryApi.flushProject.reset();
|
||||
return MockWebApi.setDocument.reset();
|
||||
});
|
||||
|
||||
it "should undo the tracked changes", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, data) =>
|
||||
throw error if error?
|
||||
ranges = data.ranges
|
||||
expect(ranges.changes).to.be.undefined
|
||||
done()
|
||||
return null
|
||||
return it("should undo the tracked changes", function(done) {
|
||||
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, data) => {
|
||||
if (error != null) { throw error; }
|
||||
const {
|
||||
ranges
|
||||
} = data;
|
||||
expect(ranges.changes).to.be.undefined;
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
describe "without the undo flag", ->
|
||||
before (done) ->
|
||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||
throw error if error?
|
||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) =>
|
||||
throw error if error?
|
||||
# Go back to old lines, without undo flag
|
||||
DocUpdaterClient.setDocLines @project_id, @doc_id, @lines, @source, @user_id, false, (error, res, body) =>
|
||||
@statusCode = res.statusCode
|
||||
setTimeout done, 200
|
||||
return null
|
||||
return describe("without the undo flag", function() {
|
||||
before(function(done) {
|
||||
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]);
|
||||
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version});
|
||||
DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => {
|
||||
if (error != null) { throw error; }
|
||||
return DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.update, error => {
|
||||
if (error != null) { throw error; }
|
||||
// Go back to old lines, without undo flag
|
||||
return DocUpdaterClient.setDocLines(this.project_id, this.doc_id, this.lines, this.source, this.user_id, false, (error, res, body) => {
|
||||
this.statusCode = res.statusCode;
|
||||
return setTimeout(done, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
after ->
|
||||
MockTrackChangesApi.flushDoc.reset()
|
||||
MockProjectHistoryApi.flushProject.reset()
|
||||
MockWebApi.setDocument.reset()
|
||||
after(function() {
|
||||
MockTrackChangesApi.flushDoc.reset();
|
||||
MockProjectHistoryApi.flushProject.reset();
|
||||
return MockWebApi.setDocument.reset();
|
||||
});
|
||||
|
||||
it "should not undo the tracked changes", (done) ->
|
||||
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, data) =>
|
||||
throw error if error?
|
||||
ranges = data.ranges
|
||||
expect(ranges.changes.length).to.equal 1
|
||||
done()
|
||||
return null
|
||||
return it("should not undo the tracked changes", function(done) {
|
||||
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, data) => {
|
||||
if (error != null) { throw error; }
|
||||
const {
|
||||
ranges
|
||||
} = data;
|
||||
expect(ranges.changes.length).to.equal(1);
|
||||
return done();
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,20 +1,38 @@
|
||||
app = require('../../../../app')
|
||||
require("logger-sharelatex").logger.level("fatal")
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS205: Consider reworking code to avoid use of IIFEs
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const app = require('../../../../app');
|
||||
require("logger-sharelatex").logger.level("fatal");
|
||||
|
||||
module.exports =
|
||||
running: false
|
||||
initing: false
|
||||
callbacks: []
|
||||
ensureRunning: (callback = (error) ->) ->
|
||||
if @running
|
||||
return callback()
|
||||
else if @initing
|
||||
@callbacks.push callback
|
||||
else
|
||||
@initing = true
|
||||
@callbacks.push callback
|
||||
app.listen 3003, "localhost", (error) =>
|
||||
throw error if error?
|
||||
@running = true
|
||||
for callback in @callbacks
|
||||
callback()
|
||||
module.exports = {
|
||||
running: false,
|
||||
initing: false,
|
||||
callbacks: [],
|
||||
ensureRunning(callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
if (this.running) {
|
||||
return callback();
|
||||
} else if (this.initing) {
|
||||
return this.callbacks.push(callback);
|
||||
} else {
|
||||
this.initing = true;
|
||||
this.callbacks.push(callback);
|
||||
return app.listen(3003, "localhost", error => {
|
||||
if (error != null) { throw error; }
|
||||
this.running = true;
|
||||
return (() => {
|
||||
const result = [];
|
||||
for (callback of Array.from(this.callbacks)) {
|
||||
result.push(callback());
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,111 +1,171 @@
|
||||
Settings = require('settings-sharelatex')
|
||||
rclient = require("redis-sharelatex").createClient(Settings.redis.documentupdater)
|
||||
keys = Settings.redis.documentupdater.key_schema
|
||||
request = require("request").defaults(jar: false)
|
||||
async = require "async"
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
let DocUpdaterClient;
|
||||
const Settings = require('settings-sharelatex');
|
||||
const rclient = require("redis-sharelatex").createClient(Settings.redis.documentupdater);
|
||||
const keys = Settings.redis.documentupdater.key_schema;
|
||||
const request = require("request").defaults({jar: false});
|
||||
const async = require("async");
|
||||
|
||||
rclient_sub = require("redis-sharelatex").createClient(Settings.redis.pubsub)
|
||||
rclient_sub.subscribe "applied-ops"
|
||||
rclient_sub.setMaxListeners(0)
|
||||
const rclient_sub = require("redis-sharelatex").createClient(Settings.redis.pubsub);
|
||||
rclient_sub.subscribe("applied-ops");
|
||||
rclient_sub.setMaxListeners(0);
|
||||
|
||||
module.exports = DocUpdaterClient =
|
||||
randomId: () ->
|
||||
chars = for i in [1..24]
|
||||
Math.random().toString(16)[2]
|
||||
return chars.join("")
|
||||
module.exports = (DocUpdaterClient = {
|
||||
randomId() {
|
||||
const chars = __range__(1, 24, true).map((i) =>
|
||||
Math.random().toString(16)[2]);
|
||||
return chars.join("");
|
||||
},
|
||||
|
||||
subscribeToAppliedOps: (callback = (message) ->) ->
|
||||
rclient_sub.on "message", callback
|
||||
subscribeToAppliedOps(callback) {
|
||||
if (callback == null) { callback = function(message) {}; }
|
||||
return rclient_sub.on("message", callback);
|
||||
},
|
||||
|
||||
sendUpdate: (project_id, doc_id, update, callback = (error) ->) ->
|
||||
rclient.rpush keys.pendingUpdates({doc_id}), JSON.stringify(update), (error)->
|
||||
return callback(error) if error?
|
||||
doc_key = "#{project_id}:#{doc_id}"
|
||||
rclient.sadd "DocsWithPendingUpdates", doc_key, (error) ->
|
||||
return callback(error) if error?
|
||||
rclient.rpush "pending-updates-list", doc_key, callback
|
||||
sendUpdate(project_id, doc_id, update, callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
return rclient.rpush(keys.pendingUpdates({doc_id}), JSON.stringify(update), function(error){
|
||||
if (error != null) { return callback(error); }
|
||||
const doc_key = `${project_id}:${doc_id}`;
|
||||
return rclient.sadd("DocsWithPendingUpdates", doc_key, function(error) {
|
||||
if (error != null) { return callback(error); }
|
||||
return rclient.rpush("pending-updates-list", doc_key, callback);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
sendUpdates: (project_id, doc_id, updates, callback = (error) ->) ->
|
||||
DocUpdaterClient.preloadDoc project_id, doc_id, (error) ->
|
||||
return callback(error) if error?
|
||||
jobs = []
|
||||
for update in updates
|
||||
do (update) ->
|
||||
jobs.push (callback) ->
|
||||
DocUpdaterClient.sendUpdate project_id, doc_id, update, callback
|
||||
async.series jobs, (err) ->
|
||||
DocUpdaterClient.waitForPendingUpdates project_id, doc_id, callback
|
||||
sendUpdates(project_id, doc_id, updates, callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
return DocUpdaterClient.preloadDoc(project_id, doc_id, function(error) {
|
||||
if (error != null) { return callback(error); }
|
||||
const jobs = [];
|
||||
for (let update of Array.from(updates)) {
|
||||
((update => jobs.push(callback => DocUpdaterClient.sendUpdate(project_id, doc_id, update, callback))))(update);
|
||||
}
|
||||
return async.series(jobs, err => DocUpdaterClient.waitForPendingUpdates(project_id, doc_id, callback));
|
||||
});
|
||||
},
|
||||
|
||||
waitForPendingUpdates: (project_id, doc_id, callback) ->
|
||||
async.retry {times: 30, interval: 100}, (cb) ->
|
||||
rclient.llen keys.pendingUpdates({doc_id}), (err, length) ->
|
||||
if length > 0
|
||||
cb(new Error("updates still pending"))
|
||||
else
|
||||
cb()
|
||||
, callback
|
||||
waitForPendingUpdates(project_id, doc_id, callback) {
|
||||
return async.retry({times: 30, interval: 100}, cb => rclient.llen(keys.pendingUpdates({doc_id}), function(err, length) {
|
||||
if (length > 0) {
|
||||
return cb(new Error("updates still pending"));
|
||||
} else {
|
||||
return cb();
|
||||
}
|
||||
})
|
||||
, callback);
|
||||
},
|
||||
|
||||
getDoc: (project_id, doc_id, callback = (error, res, body) ->) ->
|
||||
request.get "http://localhost:3003/project/#{project_id}/doc/#{doc_id}", (error, res, body) ->
|
||||
if body? and res.statusCode >= 200 and res.statusCode < 300
|
||||
body = JSON.parse(body)
|
||||
callback error, res, body
|
||||
getDoc(project_id, doc_id, callback) {
|
||||
if (callback == null) { callback = function(error, res, body) {}; }
|
||||
return request.get(`http://localhost:3003/project/${project_id}/doc/${doc_id}`, function(error, res, body) {
|
||||
if ((body != null) && (res.statusCode >= 200) && (res.statusCode < 300)) {
|
||||
body = JSON.parse(body);
|
||||
}
|
||||
return callback(error, res, body);
|
||||
});
|
||||
},
|
||||
|
||||
getDocAndRecentOps: (project_id, doc_id, fromVersion, callback = (error, res, body) ->) ->
|
||||
request.get "http://localhost:3003/project/#{project_id}/doc/#{doc_id}?fromVersion=#{fromVersion}", (error, res, body) ->
|
||||
if body? and res.statusCode >= 200 and res.statusCode < 300
|
||||
body = JSON.parse(body)
|
||||
callback error, res, body
|
||||
getDocAndRecentOps(project_id, doc_id, fromVersion, callback) {
|
||||
if (callback == null) { callback = function(error, res, body) {}; }
|
||||
return request.get(`http://localhost:3003/project/${project_id}/doc/${doc_id}?fromVersion=${fromVersion}`, function(error, res, body) {
|
||||
if ((body != null) && (res.statusCode >= 200) && (res.statusCode < 300)) {
|
||||
body = JSON.parse(body);
|
||||
}
|
||||
return callback(error, res, body);
|
||||
});
|
||||
},
|
||||
|
||||
preloadDoc: (project_id, doc_id, callback = (error) ->) ->
|
||||
DocUpdaterClient.getDoc project_id, doc_id, callback
|
||||
preloadDoc(project_id, doc_id, callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
return DocUpdaterClient.getDoc(project_id, doc_id, callback);
|
||||
},
|
||||
|
||||
flushDoc: (project_id, doc_id, callback = (error) ->) ->
|
||||
request.post "http://localhost:3003/project/#{project_id}/doc/#{doc_id}/flush", (error, res, body) ->
|
||||
callback error, res, body
|
||||
flushDoc(project_id, doc_id, callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
return request.post(`http://localhost:3003/project/${project_id}/doc/${doc_id}/flush`, (error, res, body) => callback(error, res, body));
|
||||
},
|
||||
|
||||
setDocLines: (project_id, doc_id, lines, source, user_id, undoing, callback = (error) ->) ->
|
||||
request.post {
|
||||
url: "http://localhost:3003/project/#{project_id}/doc/#{doc_id}"
|
||||
json:
|
||||
lines: lines
|
||||
source: source
|
||||
user_id: user_id
|
||||
undoing: undoing
|
||||
}, (error, res, body) ->
|
||||
callback error, res, body
|
||||
setDocLines(project_id, doc_id, lines, source, user_id, undoing, callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
return request.post({
|
||||
url: `http://localhost:3003/project/${project_id}/doc/${doc_id}`,
|
||||
json: {
|
||||
lines,
|
||||
source,
|
||||
user_id,
|
||||
undoing
|
||||
}
|
||||
}, (error, res, body) => callback(error, res, body));
|
||||
},
|
||||
|
||||
deleteDoc: (project_id, doc_id, callback = (error) ->) ->
|
||||
request.del "http://localhost:3003/project/#{project_id}/doc/#{doc_id}", (error, res, body) ->
|
||||
callback error, res, body
|
||||
deleteDoc(project_id, doc_id, callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
return request.del(`http://localhost:3003/project/${project_id}/doc/${doc_id}`, (error, res, body) => callback(error, res, body));
|
||||
},
|
||||
|
||||
flushProject: (project_id, callback = () ->) ->
|
||||
request.post "http://localhost:3003/project/#{project_id}/flush", callback
|
||||
flushProject(project_id, callback) {
|
||||
if (callback == null) { callback = function() {}; }
|
||||
return request.post(`http://localhost:3003/project/${project_id}/flush`, callback);
|
||||
},
|
||||
|
||||
deleteProject: (project_id, callback = () ->) ->
|
||||
request.del "http://localhost:3003/project/#{project_id}", callback
|
||||
deleteProject(project_id, callback) {
|
||||
if (callback == null) { callback = function() {}; }
|
||||
return request.del(`http://localhost:3003/project/${project_id}`, callback);
|
||||
},
|
||||
|
||||
deleteProjectOnShutdown: (project_id, callback = () ->) ->
|
||||
request.del "http://localhost:3003/project/#{project_id}?background=true&shutdown=true", callback
|
||||
deleteProjectOnShutdown(project_id, callback) {
|
||||
if (callback == null) { callback = function() {}; }
|
||||
return request.del(`http://localhost:3003/project/${project_id}?background=true&shutdown=true`, callback);
|
||||
},
|
||||
|
||||
flushOldProjects: (callback = () ->) ->
|
||||
request.get "http://localhost:3003/flush_queued_projects?min_delete_age=1", callback
|
||||
flushOldProjects(callback) {
|
||||
if (callback == null) { callback = function() {}; }
|
||||
return request.get("http://localhost:3003/flush_queued_projects?min_delete_age=1", callback);
|
||||
},
|
||||
|
||||
acceptChange: (project_id, doc_id, change_id, callback = () ->) ->
|
||||
request.post "http://localhost:3003/project/#{project_id}/doc/#{doc_id}/change/#{change_id}/accept", callback
|
||||
acceptChange(project_id, doc_id, change_id, callback) {
|
||||
if (callback == null) { callback = function() {}; }
|
||||
return request.post(`http://localhost:3003/project/${project_id}/doc/${doc_id}/change/${change_id}/accept`, callback);
|
||||
},
|
||||
|
||||
removeComment: (project_id, doc_id, comment, callback = () ->) ->
|
||||
request.del "http://localhost:3003/project/#{project_id}/doc/#{doc_id}/comment/#{comment}", callback
|
||||
removeComment(project_id, doc_id, comment, callback) {
|
||||
if (callback == null) { callback = function() {}; }
|
||||
return request.del(`http://localhost:3003/project/${project_id}/doc/${doc_id}/comment/${comment}`, callback);
|
||||
},
|
||||
|
||||
getProjectDocs: (project_id, projectStateHash, callback = () ->) ->
|
||||
request.get "http://localhost:3003/project/#{project_id}/doc?state=#{projectStateHash}", (error, res, body) ->
|
||||
if body? and res.statusCode >= 200 and res.statusCode < 300
|
||||
body = JSON.parse(body)
|
||||
callback error, res, body
|
||||
getProjectDocs(project_id, projectStateHash, callback) {
|
||||
if (callback == null) { callback = function() {}; }
|
||||
return request.get(`http://localhost:3003/project/${project_id}/doc?state=${projectStateHash}`, function(error, res, body) {
|
||||
if ((body != null) && (res.statusCode >= 200) && (res.statusCode < 300)) {
|
||||
body = JSON.parse(body);
|
||||
}
|
||||
return callback(error, res, body);
|
||||
});
|
||||
},
|
||||
|
||||
sendProjectUpdate: (project_id, userId, docUpdates, fileUpdates, version, callback = (error) ->) ->
|
||||
request.post {
|
||||
url: "http://localhost:3003/project/#{project_id}"
|
||||
sendProjectUpdate(project_id, userId, docUpdates, fileUpdates, version, callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
return request.post({
|
||||
url: `http://localhost:3003/project/${project_id}`,
|
||||
json: { userId, docUpdates, fileUpdates, version }
|
||||
}, (error, res, body) ->
|
||||
callback error, res, body
|
||||
}, (error, res, body) => callback(error, res, body));
|
||||
}
|
||||
});
|
||||
|
||||
function __range__(left, right, inclusive) {
|
||||
let range = [];
|
||||
let ascending = left < right;
|
||||
let end = !inclusive ? right : ascending ? right + 1 : right - 1;
|
||||
for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
|
||||
range.push(i);
|
||||
}
|
||||
return range;
|
||||
}
|
||||
@@ -1,19 +1,34 @@
|
||||
express = require("express")
|
||||
app = express()
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
let MockProjectHistoryApi;
|
||||
const express = require("express");
|
||||
const app = express();
|
||||
|
||||
module.exports = MockProjectHistoryApi =
|
||||
flushProject: (doc_id, callback = (error) ->) ->
|
||||
callback()
|
||||
module.exports = (MockProjectHistoryApi = {
|
||||
flushProject(doc_id, callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
return callback();
|
||||
},
|
||||
|
||||
run: () ->
|
||||
app.post "/project/:project_id/flush", (req, res, next) =>
|
||||
@flushProject req.params.project_id, (error) ->
|
||||
if error?
|
||||
res.sendStatus 500
|
||||
else
|
||||
res.sendStatus 204
|
||||
run() {
|
||||
app.post("/project/:project_id/flush", (req, res, next) => {
|
||||
return this.flushProject(req.params.project_id, function(error) {
|
||||
if (error != null) {
|
||||
return res.sendStatus(500);
|
||||
} else {
|
||||
return res.sendStatus(204);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen 3054, (error) ->
|
||||
throw error if error?
|
||||
return app.listen(3054, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
MockProjectHistoryApi.run()
|
||||
MockProjectHistoryApi.run();
|
||||
|
||||
@@ -1,23 +1,38 @@
|
||||
express = require("express")
|
||||
app = express()
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
let MockTrackChangesApi;
|
||||
const express = require("express");
|
||||
const app = express();
|
||||
|
||||
module.exports = MockTrackChangesApi =
|
||||
flushDoc: (doc_id, callback = (error) ->) ->
|
||||
callback()
|
||||
module.exports = (MockTrackChangesApi = {
|
||||
flushDoc(doc_id, callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
return callback();
|
||||
},
|
||||
|
||||
run: () ->
|
||||
app.post "/project/:project_id/doc/:doc_id/flush", (req, res, next) =>
|
||||
@flushDoc req.params.doc_id, (error) ->
|
||||
if error?
|
||||
res.sendStatus 500
|
||||
else
|
||||
res.sendStatus 204
|
||||
run() {
|
||||
app.post("/project/:project_id/doc/:doc_id/flush", (req, res, next) => {
|
||||
return this.flushDoc(req.params.doc_id, function(error) {
|
||||
if (error != null) {
|
||||
return res.sendStatus(500);
|
||||
} else {
|
||||
return res.sendStatus(204);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen 3015, (error) ->
|
||||
throw error if error?
|
||||
.on "error", (error) ->
|
||||
console.error "error starting MockTrackChangesApi:", error.message
|
||||
process.exit(1)
|
||||
return app.listen(3015, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
}).on("error", function(error) {
|
||||
console.error("error starting MockTrackChangesApi:", error.message);
|
||||
return process.exit(1);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
MockTrackChangesApi.run()
|
||||
MockTrackChangesApi.run();
|
||||
|
||||
|
||||
@@ -1,54 +1,75 @@
|
||||
express = require("express")
|
||||
bodyParser = require("body-parser")
|
||||
app = express()
|
||||
MAX_REQUEST_SIZE = 2*(2*1024*1024 + 64*1024)
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
let MockWebApi;
|
||||
const express = require("express");
|
||||
const bodyParser = require("body-parser");
|
||||
const app = express();
|
||||
const MAX_REQUEST_SIZE = 2*((2*1024*1024) + (64*1024));
|
||||
|
||||
module.exports = MockWebApi =
|
||||
docs: {}
|
||||
module.exports = (MockWebApi = {
|
||||
docs: {},
|
||||
|
||||
clearDocs: () -> @docs = {}
|
||||
clearDocs() { return this.docs = {}; },
|
||||
|
||||
insertDoc: (project_id, doc_id, doc) ->
|
||||
doc.version ?= 0
|
||||
doc.lines ?= []
|
||||
doc.pathname = '/a/b/c.tex'
|
||||
@docs["#{project_id}:#{doc_id}"] = doc
|
||||
insertDoc(project_id, doc_id, doc) {
|
||||
if (doc.version == null) { doc.version = 0; }
|
||||
if (doc.lines == null) { doc.lines = []; }
|
||||
doc.pathname = '/a/b/c.tex';
|
||||
return this.docs[`${project_id}:${doc_id}`] = doc;
|
||||
},
|
||||
|
||||
setDocument: (project_id, doc_id, lines, version, ranges, lastUpdatedAt, lastUpdatedBy, callback = (error) ->) ->
|
||||
doc = @docs["#{project_id}:#{doc_id}"] ||= {}
|
||||
doc.lines = lines
|
||||
doc.version = version
|
||||
doc.ranges = ranges
|
||||
doc.pathname = '/a/b/c.tex'
|
||||
doc.lastUpdatedAt = lastUpdatedAt
|
||||
doc.lastUpdatedBy = lastUpdatedBy
|
||||
callback null
|
||||
setDocument(project_id, doc_id, lines, version, ranges, lastUpdatedAt, lastUpdatedBy, callback) {
|
||||
if (callback == null) { callback = function(error) {}; }
|
||||
const doc = this.docs[`${project_id}:${doc_id}`] || (this.docs[`${project_id}:${doc_id}`] = {});
|
||||
doc.lines = lines;
|
||||
doc.version = version;
|
||||
doc.ranges = ranges;
|
||||
doc.pathname = '/a/b/c.tex';
|
||||
doc.lastUpdatedAt = lastUpdatedAt;
|
||||
doc.lastUpdatedBy = lastUpdatedBy;
|
||||
return callback(null);
|
||||
},
|
||||
|
||||
getDocument: (project_id, doc_id, callback = (error, doc) ->) ->
|
||||
callback null, @docs["#{project_id}:#{doc_id}"]
|
||||
getDocument(project_id, doc_id, callback) {
|
||||
if (callback == null) { callback = function(error, doc) {}; }
|
||||
return callback(null, this.docs[`${project_id}:${doc_id}`]);
|
||||
},
|
||||
|
||||
run: () ->
|
||||
app.get "/project/:project_id/doc/:doc_id", (req, res, next) =>
|
||||
@getDocument req.params.project_id, req.params.doc_id, (error, doc) ->
|
||||
if error?
|
||||
res.sendStatus 500
|
||||
else if doc?
|
||||
res.send JSON.stringify doc
|
||||
else
|
||||
res.sendStatus 404
|
||||
run() {
|
||||
app.get("/project/:project_id/doc/:doc_id", (req, res, next) => {
|
||||
return this.getDocument(req.params.project_id, req.params.doc_id, function(error, doc) {
|
||||
if (error != null) {
|
||||
return res.sendStatus(500);
|
||||
} else if (doc != null) {
|
||||
return res.send(JSON.stringify(doc));
|
||||
} else {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.post "/project/:project_id/doc/:doc_id", bodyParser.json({limit: MAX_REQUEST_SIZE}), (req, res, next) =>
|
||||
MockWebApi.setDocument req.params.project_id, req.params.doc_id, req.body.lines, req.body.version, req.body.ranges, req.body.lastUpdatedAt, req.body.lastUpdatedBy, (error) ->
|
||||
if error?
|
||||
res.sendStatus 500
|
||||
else
|
||||
res.sendStatus 204
|
||||
app.post("/project/:project_id/doc/:doc_id", bodyParser.json({limit: MAX_REQUEST_SIZE}), (req, res, next) => {
|
||||
return MockWebApi.setDocument(req.params.project_id, req.params.doc_id, req.body.lines, req.body.version, req.body.ranges, req.body.lastUpdatedAt, req.body.lastUpdatedBy, function(error) {
|
||||
if (error != null) {
|
||||
return res.sendStatus(500);
|
||||
} else {
|
||||
return res.sendStatus(204);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen 3000, (error) ->
|
||||
throw error if error?
|
||||
.on "error", (error) ->
|
||||
console.error "error starting MockWebApi:", error.message
|
||||
process.exit(1)
|
||||
return app.listen(3000, function(error) {
|
||||
if (error != null) { throw error; }
|
||||
}).on("error", function(error) {
|
||||
console.error("error starting MockWebApi:", error.message);
|
||||
return process.exit(1);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
MockWebApi.run()
|
||||
MockWebApi.run();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user