prettier: convert test/acceptance decaffeinated files to Prettier format

This commit is contained in:
Tim Alby 2020-05-06 12:12:47 +02:00
parent 9680e62bb1
commit d15738cb98
15 changed files with 3810 additions and 2486 deletions

View file

@ -11,360 +11,519 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
const sinon = require("sinon"); const sinon = require('sinon')
const chai = require("chai"); const chai = require('chai')
chai.should(); chai.should()
const Settings = require('settings-sharelatex'); const Settings = require('settings-sharelatex')
const rclient_project_history = require("redis-sharelatex").createClient(Settings.redis.project_history); const rclient_project_history = require('redis-sharelatex').createClient(
const ProjectHistoryKeys = Settings.redis.project_history.key_schema; Settings.redis.project_history
)
const ProjectHistoryKeys = Settings.redis.project_history.key_schema
const MockProjectHistoryApi = require("./helpers/MockProjectHistoryApi"); const MockProjectHistoryApi = require('./helpers/MockProjectHistoryApi')
const MockWebApi = require("./helpers/MockWebApi"); const MockWebApi = require('./helpers/MockWebApi')
const DocUpdaterClient = require("./helpers/DocUpdaterClient"); const DocUpdaterClient = require('./helpers/DocUpdaterClient')
const DocUpdaterApp = require("./helpers/DocUpdaterApp"); const DocUpdaterApp = require('./helpers/DocUpdaterApp')
describe("Applying updates to a project's structure", function() { describe("Applying updates to a project's structure", function () {
before(function() { before(function () {
this.user_id = 'user-id-123'; this.user_id = 'user-id-123'
return this.version = 1234; return (this.version = 1234)
}); })
describe("renaming a file", function() { describe('renaming a file', function () {
before(function(done) { before(function (done) {
this.project_id = DocUpdaterClient.randomId(); this.project_id = DocUpdaterClient.randomId()
this.fileUpdate = { this.fileUpdate = {
id: DocUpdaterClient.randomId(), id: DocUpdaterClient.randomId(),
pathname: '/file-path', pathname: '/file-path',
newPathname: '/new-file-path' newPathname: '/new-file-path'
}; }
this.fileUpdates = [ this.fileUpdate ]; this.fileUpdates = [this.fileUpdate]
return DocUpdaterApp.ensureRunning(error => { return DocUpdaterApp.ensureRunning((error) => {
if (error != null) { throw error; } if (error != null) {
return DocUpdaterClient.sendProjectUpdate(this.project_id, this.user_id, [], this.fileUpdates, this.version, (error) => { throw error
if (error != null) { throw error; } }
return setTimeout(done, 200); return DocUpdaterClient.sendProjectUpdate(
}); this.project_id,
}); this.user_id,
}); [],
this.fileUpdates,
this.version,
(error) => {
if (error != null) {
throw error
}
return setTimeout(done, 200)
}
)
})
})
return it("should push the applied file renames to the project history api", function(done) { 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) => { rclient_project_history.lrange(
if (error != null) { throw error; } ProjectHistoryKeys.projectHistoryOps({ project_id: this.project_id }),
0,
-1,
(error, updates) => {
if (error != null) {
throw error
}
const update = JSON.parse(updates[0]); const update = JSON.parse(updates[0])
update.file.should.equal(this.fileUpdate.id); update.file.should.equal(this.fileUpdate.id)
update.pathname.should.equal('/file-path'); update.pathname.should.equal('/file-path')
update.new_pathname.should.equal('/new-file-path'); update.new_pathname.should.equal('/new-file-path')
update.meta.user_id.should.equal(this.user_id); update.meta.user_id.should.equal(this.user_id)
update.meta.ts.should.be.a('string'); update.meta.ts.should.be.a('string')
update.version.should.equal(`${this.version}.0`); update.version.should.equal(`${this.version}.0`)
return done(); return done()
}); }
return null; )
}); return null
}); })
})
describe("renaming a document", function() { describe('renaming a document', function () {
before(function() { before(function () {
this.docUpdate = { this.docUpdate = {
id: DocUpdaterClient.randomId(), id: DocUpdaterClient.randomId(),
pathname: '/doc-path', pathname: '/doc-path',
newPathname: '/new-doc-path' newPathname: '/new-doc-path'
}; }
return this.docUpdates = [ this.docUpdate ];}); return (this.docUpdates = [this.docUpdate])
})
describe("when the document is not loaded", function() { describe('when the document is not loaded', function () {
before(function(done) { before(function (done) {
this.project_id = DocUpdaterClient.randomId(); this.project_id = DocUpdaterClient.randomId()
DocUpdaterClient.sendProjectUpdate(this.project_id, this.user_id, this.docUpdates, [], this.version, (error) => { DocUpdaterClient.sendProjectUpdate(
if (error != null) { throw error; } this.project_id,
return setTimeout(done, 200); this.user_id,
}); this.docUpdates,
return null; [],
}); this.version,
(error) => {
if (error != null) {
throw error
}
return setTimeout(done, 200)
}
)
return null
})
return it("should push the applied doc renames to the project history api", function(done) { 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) => { rclient_project_history.lrange(
if (error != null) { throw error; } ProjectHistoryKeys.projectHistoryOps({ project_id: this.project_id }),
0,
-1,
(error, updates) => {
if (error != null) {
throw error
}
const update = JSON.parse(updates[0]); const update = JSON.parse(updates[0])
update.doc.should.equal(this.docUpdate.id); update.doc.should.equal(this.docUpdate.id)
update.pathname.should.equal('/doc-path'); update.pathname.should.equal('/doc-path')
update.new_pathname.should.equal('/new-doc-path'); update.new_pathname.should.equal('/new-doc-path')
update.meta.user_id.should.equal(this.user_id); update.meta.user_id.should.equal(this.user_id)
update.meta.ts.should.be.a('string'); update.meta.ts.should.be.a('string')
update.version.should.equal(`${this.version}.0`); update.version.should.equal(`${this.version}.0`)
return done(); return done()
}); }
return null; )
}); return null
}); })
})
return describe("when the document is loaded", function() { return describe('when the document is loaded', function () {
before(function(done) { before(function (done) {
this.project_id = DocUpdaterClient.randomId(); this.project_id = DocUpdaterClient.randomId()
MockWebApi.insertDoc(this.project_id, this.docUpdate.id, {}); MockWebApi.insertDoc(this.project_id, this.docUpdate.id, {})
DocUpdaterClient.preloadDoc(this.project_id, this.docUpdate.id, error => { DocUpdaterClient.preloadDoc(
if (error != null) { throw error; } this.project_id,
sinon.spy(MockWebApi, "getDocument"); this.docUpdate.id,
return DocUpdaterClient.sendProjectUpdate(this.project_id, this.user_id, this.docUpdates, [], this.version, (error) => { (error) => {
if (error != null) { throw error; } if (error != null) {
return setTimeout(done, 200); throw error
}); }
}); sinon.spy(MockWebApi, 'getDocument')
return null; return DocUpdaterClient.sendProjectUpdate(
}); this.project_id,
this.user_id,
this.docUpdates,
[],
this.version,
(error) => {
if (error != null) {
throw error
}
return setTimeout(done, 200)
}
)
}
)
return null
})
after(function() { return MockWebApi.getDocument.restore(); }); after(function () {
return MockWebApi.getDocument.restore()
})
it("should update the doc", function(done) { it('should update the doc', function (done) {
DocUpdaterClient.getDoc(this.project_id, this.docUpdate.id, (error, res, doc) => { DocUpdaterClient.getDoc(
doc.pathname.should.equal(this.docUpdate.newPathname); this.project_id,
return done(); this.docUpdate.id,
}); (error, res, doc) => {
return null; doc.pathname.should.equal(this.docUpdate.newPathname)
}); return done()
}
)
return null
})
return it("should push the applied doc renames to the project history api", function(done) { 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) => { rclient_project_history.lrange(
if (error != null) { throw error; } ProjectHistoryKeys.projectHistoryOps({ project_id: this.project_id }),
0,
-1,
(error, updates) => {
if (error != null) {
throw error
}
const update = JSON.parse(updates[0]); const update = JSON.parse(updates[0])
update.doc.should.equal(this.docUpdate.id); update.doc.should.equal(this.docUpdate.id)
update.pathname.should.equal('/doc-path'); update.pathname.should.equal('/doc-path')
update.new_pathname.should.equal('/new-doc-path'); update.new_pathname.should.equal('/new-doc-path')
update.meta.user_id.should.equal(this.user_id); update.meta.user_id.should.equal(this.user_id)
update.meta.ts.should.be.a('string'); update.meta.ts.should.be.a('string')
update.version.should.equal(`${this.version}.0`); update.version.should.equal(`${this.version}.0`)
return done(); return done()
}); }
return null; )
}); return null
}); })
}); })
})
describe("renaming multiple documents and files", function() { describe('renaming multiple documents and files', function () {
before(function() { before(function () {
this.docUpdate0 = { this.docUpdate0 = {
id: DocUpdaterClient.randomId(), id: DocUpdaterClient.randomId(),
pathname: '/doc-path0', pathname: '/doc-path0',
newPathname: '/new-doc-path0' newPathname: '/new-doc-path0'
}; }
this.docUpdate1 = { this.docUpdate1 = {
id: DocUpdaterClient.randomId(), id: DocUpdaterClient.randomId(),
pathname: '/doc-path1', pathname: '/doc-path1',
newPathname: '/new-doc-path1' newPathname: '/new-doc-path1'
}; }
this.docUpdates = [ this.docUpdate0, this.docUpdate1 ]; this.docUpdates = [this.docUpdate0, this.docUpdate1]
this.fileUpdate0 = { this.fileUpdate0 = {
id: DocUpdaterClient.randomId(), id: DocUpdaterClient.randomId(),
pathname: '/file-path0', pathname: '/file-path0',
newPathname: '/new-file-path0' newPathname: '/new-file-path0'
}; }
this.fileUpdate1 = { this.fileUpdate1 = {
id: DocUpdaterClient.randomId(), id: DocUpdaterClient.randomId(),
pathname: '/file-path1', pathname: '/file-path1',
newPathname: '/new-file-path1' newPathname: '/new-file-path1'
}; }
return this.fileUpdates = [ this.fileUpdate0, this.fileUpdate1 ];}); return (this.fileUpdates = [this.fileUpdate0, this.fileUpdate1])
})
return describe("when the documents are not loaded", function() { return describe('when the documents are not loaded', function () {
before(function(done) { before(function (done) {
this.project_id = DocUpdaterClient.randomId(); this.project_id = DocUpdaterClient.randomId()
DocUpdaterClient.sendProjectUpdate(this.project_id, this.user_id, this.docUpdates, this.fileUpdates, this.version, (error) => { DocUpdaterClient.sendProjectUpdate(
if (error != null) { throw error; } this.project_id,
return setTimeout(done, 200); this.user_id,
}); this.docUpdates,
return null; this.fileUpdates,
}); this.version,
(error) => {
if (error != null) {
throw error
}
return setTimeout(done, 200)
}
)
return null
})
return it("should push the applied doc renames to the project history api", function(done) { 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) => { rclient_project_history.lrange(
if (error != null) { throw error; } ProjectHistoryKeys.projectHistoryOps({ project_id: this.project_id }),
0,
-1,
(error, updates) => {
if (error != null) {
throw error
}
let update = JSON.parse(updates[0]); let update = JSON.parse(updates[0])
update.doc.should.equal(this.docUpdate0.id); update.doc.should.equal(this.docUpdate0.id)
update.pathname.should.equal('/doc-path0'); update.pathname.should.equal('/doc-path0')
update.new_pathname.should.equal('/new-doc-path0'); update.new_pathname.should.equal('/new-doc-path0')
update.meta.user_id.should.equal(this.user_id); update.meta.user_id.should.equal(this.user_id)
update.meta.ts.should.be.a('string'); update.meta.ts.should.be.a('string')
update.version.should.equal(`${this.version}.0`); update.version.should.equal(`${this.version}.0`)
update = JSON.parse(updates[1]); update = JSON.parse(updates[1])
update.doc.should.equal(this.docUpdate1.id); update.doc.should.equal(this.docUpdate1.id)
update.pathname.should.equal('/doc-path1'); update.pathname.should.equal('/doc-path1')
update.new_pathname.should.equal('/new-doc-path1'); update.new_pathname.should.equal('/new-doc-path1')
update.meta.user_id.should.equal(this.user_id); update.meta.user_id.should.equal(this.user_id)
update.meta.ts.should.be.a('string'); update.meta.ts.should.be.a('string')
update.version.should.equal(`${this.version}.1`); update.version.should.equal(`${this.version}.1`)
update = JSON.parse(updates[2]); update = JSON.parse(updates[2])
update.file.should.equal(this.fileUpdate0.id); update.file.should.equal(this.fileUpdate0.id)
update.pathname.should.equal('/file-path0'); update.pathname.should.equal('/file-path0')
update.new_pathname.should.equal('/new-file-path0'); update.new_pathname.should.equal('/new-file-path0')
update.meta.user_id.should.equal(this.user_id); update.meta.user_id.should.equal(this.user_id)
update.meta.ts.should.be.a('string'); update.meta.ts.should.be.a('string')
update.version.should.equal(`${this.version}.2`); update.version.should.equal(`${this.version}.2`)
update = JSON.parse(updates[3]); update = JSON.parse(updates[3])
update.file.should.equal(this.fileUpdate1.id); update.file.should.equal(this.fileUpdate1.id)
update.pathname.should.equal('/file-path1'); update.pathname.should.equal('/file-path1')
update.new_pathname.should.equal('/new-file-path1'); update.new_pathname.should.equal('/new-file-path1')
update.meta.user_id.should.equal(this.user_id); update.meta.user_id.should.equal(this.user_id)
update.meta.ts.should.be.a('string'); update.meta.ts.should.be.a('string')
update.version.should.equal(`${this.version}.3`); update.version.should.equal(`${this.version}.3`)
return done(); return done()
}); }
return null; )
}); return null
}); })
}); })
})
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'
}
this.fileUpdates = [this.fileUpdate]
DocUpdaterClient.sendProjectUpdate(
this.project_id,
this.user_id,
[],
this.fileUpdates,
this.version,
(error) => {
if (error != null) {
throw error
}
return setTimeout(done, 200)
}
)
return null
})
describe("adding a file", function() { return it('should push the file addition to the project history api', function (done) {
before(function(done) { rclient_project_history.lrange(
this.project_id = DocUpdaterClient.randomId(); ProjectHistoryKeys.projectHistoryOps({ project_id: this.project_id }),
this.fileUpdate = { 0,
id: DocUpdaterClient.randomId(), -1,
pathname: '/file-path', (error, updates) => {
url: 'filestore.example.com' if (error != null) {
}; throw error
this.fileUpdates = [ this.fileUpdate ]; }
DocUpdaterClient.sendProjectUpdate(this.project_id, this.user_id, [], this.fileUpdates, this.version, (error) => {
if (error != null) { throw error; }
return setTimeout(done, 200);
});
return null;
});
return it("should push the file addition to the project history api", function(done) { const update = JSON.parse(updates[0])
rclient_project_history.lrange(ProjectHistoryKeys.projectHistoryOps({project_id: this.project_id}), 0, -1, (error, updates) => { update.file.should.equal(this.fileUpdate.id)
if (error != null) { throw error; } 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`)
const update = JSON.parse(updates[0]); return done()
update.file.should.equal(this.fileUpdate.id); }
update.pathname.should.equal('/file-path'); )
update.url.should.equal('filestore.example.com'); return null
update.meta.user_id.should.equal(this.user_id); })
update.meta.ts.should.be.a('string'); })
update.version.should.equal(`${this.version}.0`);
return done(); describe('adding a doc', function () {
}); before(function (done) {
return null; this.project_id = DocUpdaterClient.randomId()
}); this.docUpdate = {
}); id: DocUpdaterClient.randomId(),
pathname: '/file-path',
docLines: 'a\nb'
}
this.docUpdates = [this.docUpdate]
DocUpdaterClient.sendProjectUpdate(
this.project_id,
this.user_id,
this.docUpdates,
[],
this.version,
(error) => {
if (error != null) {
throw error
}
return setTimeout(done, 200)
}
)
return null
})
describe("adding a doc", function() { return it('should push the doc addition to the project history api', function (done) {
before(function(done) { rclient_project_history.lrange(
this.project_id = DocUpdaterClient.randomId(); ProjectHistoryKeys.projectHistoryOps({ project_id: this.project_id }),
this.docUpdate = { 0,
id: DocUpdaterClient.randomId(), -1,
pathname: '/file-path', (error, updates) => {
docLines: 'a\nb' if (error != null) {
}; throw error
this.docUpdates = [ this.docUpdate ]; }
DocUpdaterClient.sendProjectUpdate(this.project_id, this.user_id, this.docUpdates, [], this.version, (error) => {
if (error != null) { throw error; }
return setTimeout(done, 200);
});
return null;
});
return it("should push the doc addition to the project history api", function(done) { const update = JSON.parse(updates[0])
rclient_project_history.lrange(ProjectHistoryKeys.projectHistoryOps({project_id: this.project_id}), 0, -1, (error, updates) => { update.doc.should.equal(this.docUpdate.id)
if (error != null) { throw error; } 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`)
const update = JSON.parse(updates[0]); return done()
update.doc.should.equal(this.docUpdate.id); }
update.pathname.should.equal('/file-path'); )
update.docLines.should.equal('a\nb'); return null
update.meta.user_id.should.equal(this.user_id); })
update.meta.ts.should.be.a('string'); })
update.version.should.equal(`${this.version}.0`);
return done(); describe('with enough updates to flush to the history service', function () {
}); before(function (done) {
return null; 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,
docLines: 'a\nb'
})
}
describe("with enough updates to flush to the history service", function() { sinon.spy(MockProjectHistoryApi, 'flushProject')
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,
docLines: 'a\nb'
});
}
sinon.spy(MockProjectHistoryApi, "flushProject"); // 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,
(error) => {
if (error != null) {
throw error
}
return setTimeout(done, 2000)
}
)
}
)
return null
})
// Send updates in chunks to causes multiple flushes after(function () {
const projectId = this.project_id; return MockProjectHistoryApi.flushProject.restore()
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, (error) => {
if (error != null) { throw error; }
return setTimeout(done, 2000);
});
});
return null;
});
after(function() { return MockProjectHistoryApi.flushProject.restore(); }); return it('should flush project history', function () {
return MockProjectHistoryApi.flushProject
.calledWith(this.project_id)
.should.equal(true)
})
})
return it("should flush project history", function() { return describe('with too few updates to flush to the history service', function () {
return MockProjectHistoryApi.flushProject.calledWith(this.project_id).should.equal(true); before(function (done) {
}); this.project_id = DocUpdaterClient.randomId()
}); this.user_id = DocUpdaterClient.randomId()
this.version0 = 12345
this.version1 = this.version0 + 1
return describe("with too few updates to flush to the history service", function() { const updates = []
before(function(done) { for (let v = 0; v <= 42; v++) {
this.project_id = DocUpdaterClient.randomId(); // Should flush after 500 ops
this.user_id = DocUpdaterClient.randomId(); updates.push({
this.version0 = 12345; id: DocUpdaterClient.randomId(),
this.version1 = this.version0 + 1; pathname: '/file-' + v,
docLines: 'a\nb'
})
}
const updates = []; sinon.spy(MockProjectHistoryApi, 'flushProject')
for (let v = 0; v <= 42; v++) { // Should flush after 500 ops
updates.push({
id: DocUpdaterClient.randomId(),
pathname: '/file-' + v,
docLines: 'a\nb'
});
}
sinon.spy(MockProjectHistoryApi, "flushProject"); // 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,
(error) => {
if (error != null) {
throw error
}
return setTimeout(done, 2000)
}
)
}
)
return null
})
// Send updates in chunks after(function () {
const projectId = this.project_id; return MockProjectHistoryApi.flushProject.restore()
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, (error) => {
if (error != null) { throw error; }
return setTimeout(done, 2000);
});
});
return null;
});
after(function() { return MockProjectHistoryApi.flushProject.restore(); }); return it('should not flush project history', function () {
return MockProjectHistoryApi.flushProject
return it("should not flush project history", function() { .calledWith(this.project_id)
return MockProjectHistoryApi.flushProject.calledWith(this.project_id).should.equal(false); .should.equal(false)
}); })
}); })
}); })

View file

@ -10,137 +10,186 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
const sinon = require("sinon"); const sinon = require('sinon')
const chai = require("chai"); const chai = require('chai')
chai.should(); chai.should()
const MockTrackChangesApi = require("./helpers/MockTrackChangesApi"); const MockTrackChangesApi = require('./helpers/MockTrackChangesApi')
const MockProjectHistoryApi = require("./helpers/MockProjectHistoryApi"); const MockProjectHistoryApi = require('./helpers/MockProjectHistoryApi')
const MockWebApi = require("./helpers/MockWebApi"); const MockWebApi = require('./helpers/MockWebApi')
const DocUpdaterClient = require("./helpers/DocUpdaterClient"); const DocUpdaterClient = require('./helpers/DocUpdaterClient')
const DocUpdaterApp = require("./helpers/DocUpdaterApp"); const DocUpdaterApp = require('./helpers/DocUpdaterApp')
describe("Deleting a document", function() { describe('Deleting a document', function () {
before(function(done) { before(function (done) {
this.lines = ["one", "two", "three"]; this.lines = ['one', 'two', 'three']
this.version = 42; this.version = 42
this.update = { this.update = {
doc: this.doc_id, doc: this.doc_id,
op: [{ op: [
i: "one and a half\n", {
p: 4 i: 'one and a half\n',
}], p: 4
v: this.version }
}; ],
this.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(MockTrackChangesApi, 'flushDoc')
sinon.spy(MockProjectHistoryApi, "flushProject"); sinon.spy(MockProjectHistoryApi, 'flushProject')
return DocUpdaterApp.ensureRunning(done); return DocUpdaterApp.ensureRunning(done)
}); })
after(function() { after(function () {
MockTrackChangesApi.flushDoc.restore(); MockTrackChangesApi.flushDoc.restore()
return MockProjectHistoryApi.flushProject.restore(); return MockProjectHistoryApi.flushProject.restore()
}); })
describe("when the updated doc exists in the doc updater", function() { describe('when the updated doc exists in the doc updater', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[this.project_id, this.doc_id] = Array.from([
sinon.spy(MockWebApi, "setDocument"); DocUpdaterClient.randomId(),
sinon.spy(MockWebApi, "getDocument"); DocUpdaterClient.randomId()
])
sinon.spy(MockWebApi, 'setDocument')
sinon.spy(MockWebApi, 'getDocument')
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version}); MockWebApi.insertDoc(this.project_id, this.doc_id, {
return DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => { lines: this.lines,
if (error != null) { throw error; } version: this.version
return DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.update, error => { })
if (error != null) { throw error; } return DocUpdaterClient.preloadDoc(
return setTimeout(() => { this.project_id,
return DocUpdaterClient.deleteDoc(this.project_id, this.doc_id, (error, res, body) => { this.doc_id,
this.statusCode = res.statusCode; (error) => {
return setTimeout(done, 200); if (error != null) {
}); throw error
} }
, 200); 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(function() { after(function () {
MockWebApi.setDocument.restore(); MockWebApi.setDocument.restore()
return MockWebApi.getDocument.restore(); return MockWebApi.getDocument.restore()
}); })
it("should return a 204 status code", function() { it('should return a 204 status code', function () {
return this.statusCode.should.equal(204); return this.statusCode.should.equal(204)
}); })
it("should send the updated document and version to the web api", function() { it('should send the updated document and version to the web api', function () {
return MockWebApi.setDocument return MockWebApi.setDocument
.calledWith(this.project_id, this.doc_id, this.result, this.version + 1) .calledWith(this.project_id, this.doc_id, this.result, this.version + 1)
.should.equal(true); .should.equal(true)
}); })
it("should need to reload the doc if read again", function(done) { it('should need to reload the doc if read again', function (done) {
MockWebApi.getDocument.called.should.equal.false; MockWebApi.getDocument.called.should.equal.false
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => { return DocUpdaterClient.getDoc(
MockWebApi.getDocument this.project_id,
.calledWith(this.project_id, this.doc_id) this.doc_id,
.should.equal(true); (error, res, doc) => {
return done(); MockWebApi.getDocument
}); .calledWith(this.project_id, this.doc_id)
}); .should.equal(true)
return done()
}
)
})
it("should flush track changes", function() { it('should flush track changes', function () {
return MockTrackChangesApi.flushDoc.calledWith(this.doc_id).should.equal(true); return MockTrackChangesApi.flushDoc
}); .calledWith(this.doc_id)
.should.equal(true)
})
return it("should flush project history", function() { return it('should flush project history', function () {
return MockProjectHistoryApi.flushProject.calledWith(this.project_id).should.equal(true); return MockProjectHistoryApi.flushProject
}); .calledWith(this.project_id)
}); .should.equal(true)
})
})
return describe("when the doc is not in the doc updater", function() { return describe('when the doc is not in the doc updater', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[this.project_id, this.doc_id] = Array.from([
MockWebApi.insertDoc(this.project_id, this.doc_id, { DocUpdaterClient.randomId(),
lines: this.lines DocUpdaterClient.randomId()
}); ])
sinon.spy(MockWebApi, "setDocument"); MockWebApi.insertDoc(this.project_id, this.doc_id, {
sinon.spy(MockWebApi, "getDocument"); lines: this.lines
return DocUpdaterClient.deleteDoc(this.project_id, this.doc_id, (error, res, body) => { })
this.statusCode = res.statusCode; sinon.spy(MockWebApi, 'setDocument')
return setTimeout(done, 200); 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(function() { after(function () {
MockWebApi.setDocument.restore(); MockWebApi.setDocument.restore()
return MockWebApi.getDocument.restore(); return MockWebApi.getDocument.restore()
}); })
it("should return a 204 status code", function() { it('should return a 204 status code', function () {
return this.statusCode.should.equal(204); return this.statusCode.should.equal(204)
}); })
it("should not need to send the updated document to the web api", function() { return MockWebApi.setDocument.called.should.equal(false); }); it('should not need to send the updated document to the web api', function () {
return MockWebApi.setDocument.called.should.equal(false)
})
it("should need to reload the doc if read again", function(done) { it('should need to reload the doc if read again', function (done) {
MockWebApi.getDocument.called.should.equal.false; MockWebApi.getDocument.called.should.equal.false
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => { return DocUpdaterClient.getDoc(
MockWebApi.getDocument this.project_id,
.calledWith(this.project_id, this.doc_id) this.doc_id,
.should.equal(true); (error, res, doc) => {
return done(); MockWebApi.getDocument
}); .calledWith(this.project_id, this.doc_id)
}); .should.equal(true)
return done()
}
)
})
it("should flush track changes", function() { it('should flush track changes', function () {
return MockTrackChangesApi.flushDoc.calledWith(this.doc_id).should.equal(true); return MockTrackChangesApi.flushDoc
}); .calledWith(this.doc_id)
.should.equal(true)
})
return it("should flush project history", function() { return it('should flush project history', function () {
return MockProjectHistoryApi.flushProject.calledWith(this.project_id).should.equal(true); return MockProjectHistoryApi.flushProject
}); .calledWith(this.project_id)
}); .should.equal(true)
}); })
})
})

View file

@ -11,213 +11,285 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
const sinon = require("sinon"); const sinon = require('sinon')
const chai = require("chai"); const chai = require('chai')
chai.should(); chai.should()
const async = require("async"); const async = require('async')
const MockTrackChangesApi = require("./helpers/MockTrackChangesApi"); const MockTrackChangesApi = require('./helpers/MockTrackChangesApi')
const MockProjectHistoryApi = require("./helpers/MockProjectHistoryApi"); const MockProjectHistoryApi = require('./helpers/MockProjectHistoryApi')
const MockWebApi = require("./helpers/MockWebApi"); const MockWebApi = require('./helpers/MockWebApi')
const DocUpdaterClient = require("./helpers/DocUpdaterClient"); const DocUpdaterClient = require('./helpers/DocUpdaterClient')
const DocUpdaterApp = require("./helpers/DocUpdaterApp"); const DocUpdaterApp = require('./helpers/DocUpdaterApp')
describe("Deleting a project", function() { describe('Deleting a project', function () {
before(function(done) { before(function (done) {
let doc_id0, doc_id1; let doc_id0, doc_id1
this.project_id = DocUpdaterClient.randomId(); this.project_id = DocUpdaterClient.randomId()
this.docs = [{ this.docs = [
id: (doc_id0 = DocUpdaterClient.randomId()), {
lines: ["one", "two", "three"], id: (doc_id0 = DocUpdaterClient.randomId()),
update: { lines: ['one', 'two', 'three'],
doc: doc_id0, update: {
op: [{ doc: doc_id0,
i: "one and a half\n", op: [
p: 4 {
}], i: 'one and a half\n',
v: 0 p: 4
}, }
updatedLines: ["one", "one and a half", "two", "three"] ],
}, { v: 0
id: (doc_id1 = DocUpdaterClient.randomId()), },
lines: ["four", "five", "six"], updatedLines: ['one', 'one and a half', 'two', 'three']
update: { },
doc: doc_id1, {
op: [{ id: (doc_id1 = DocUpdaterClient.randomId()),
i: "four and a half\n", lines: ['four', 'five', 'six'],
p: 5 update: {
}], doc: doc_id1,
v: 0 op: [
}, {
updatedLines: ["four", "four and a half", "five", "six"] i: 'four and a half\n',
}]; p: 5
for (const doc of Array.from(this.docs)) { }
MockWebApi.insertDoc(this.project_id, doc.id, { ],
lines: doc.lines, v: 0
version: doc.update.v },
}); updatedLines: ['four', 'four and a half', 'five', 'six']
} }
]
for (const doc of Array.from(this.docs)) {
MockWebApi.insertDoc(this.project_id, doc.id, {
lines: doc.lines,
version: doc.update.v
})
}
return DocUpdaterApp.ensureRunning(done); return DocUpdaterApp.ensureRunning(done)
}); })
describe('with documents which have been updated', function () {
before(function (done) {
sinon.spy(MockWebApi, 'setDocument')
sinon.spy(MockTrackChangesApi, 'flushDoc')
sinon.spy(MockProjectHistoryApi, 'flushProject')
describe("with documents which have been updated", function() { return async.series(
before(function(done) { this.docs.map((doc) => {
sinon.spy(MockWebApi, "setDocument"); return (callback) => {
sinon.spy(MockTrackChangesApi, "flushDoc"); return DocUpdaterClient.preloadDoc(
sinon.spy(MockProjectHistoryApi, "flushProject"); 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)
}
)
})
return async.series(this.docs.map(doc => { after(function () {
return callback => { MockWebApi.setDocument.restore()
return DocUpdaterClient.preloadDoc(this.project_id, doc.id, error => { MockTrackChangesApi.flushDoc.restore()
if (error != null) { return callback(error); } return MockProjectHistoryApi.flushProject.restore()
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(function() { it('should return a 204 status code', function () {
MockWebApi.setDocument.restore(); return this.statusCode.should.equal(204)
MockTrackChangesApi.flushDoc.restore(); })
return MockProjectHistoryApi.flushProject.restore();
});
it("should return a 204 status code", function() { it('should send each document to the web api', function () {
return this.statusCode.should.equal(204); return Array.from(this.docs).map((doc) =>
}); MockWebApi.setDocument
.calledWith(this.project_id, doc.id, doc.updatedLines)
.should.equal(true)
)
})
it("should send each document to the web api", function() { it('should need to reload the docs if read again', function (done) {
return Array.from(this.docs).map((doc) => sinon.spy(MockWebApi, 'getDocument')
MockWebApi.setDocument return async.series(
.calledWith(this.project_id, doc.id, doc.updatedLines) this.docs.map((doc) => {
.should.equal(true)); 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()
}
)
}
}),
() => {
MockWebApi.getDocument.restore()
return done()
}
)
})
it("should need to reload the docs if read again", function(done) { it('should flush each doc in track changes', function () {
sinon.spy(MockWebApi, "getDocument"); return Array.from(this.docs).map((doc) =>
return async.series(this.docs.map(doc => { MockTrackChangesApi.flushDoc.calledWith(doc.id).should.equal(true)
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();
});
};
}), () => {
MockWebApi.getDocument.restore();
return done();
});
});
it("should flush each doc in track changes", function() { return it('should flush each doc in project history', function () {
return Array.from(this.docs).map((doc) => return MockProjectHistoryApi.flushProject
MockTrackChangesApi.flushDoc.calledWith(doc.id).should.equal(true)); .calledWith(this.project_id)
}); .should.equal(true)
})
})
return it("should flush each doc in project history", function() { describe('with the background=true parameter from realtime and no request to flush the queue', function () {
return MockProjectHistoryApi.flushProject.calledWith(this.project_id).should.equal(true); before(function (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() { return async.series(
before(function(done) { this.docs.map((doc) => {
sinon.spy(MockWebApi, "setDocument"); return (callback) => {
sinon.spy(MockTrackChangesApi, "flushDoc"); return DocUpdaterClient.preloadDoc(
sinon.spy(MockProjectHistoryApi, "flushProject"); 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)
}
)
})
return async.series(this.docs.map(doc => { after(function () {
return callback => { MockWebApi.setDocument.restore()
return DocUpdaterClient.preloadDoc(this.project_id, doc.id, callback); MockTrackChangesApi.flushDoc.restore()
}; return MockProjectHistoryApi.flushProject.restore()
}), 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(function() { it('should return a 204 status code', function () {
MockWebApi.setDocument.restore(); return this.statusCode.should.equal(204)
MockTrackChangesApi.flushDoc.restore(); })
return MockProjectHistoryApi.flushProject.restore();
});
it("should return a 204 status code", function() { it('should not send any documents to the web api', function () {
return this.statusCode.should.equal(204); return MockWebApi.setDocument.called.should.equal(false)
}); })
it("should not send any documents to the web api", function() { return MockWebApi.setDocument.called.should.equal(false); }); it('should not flush any docs in track changes', function () {
return MockTrackChangesApi.flushDoc.called.should.equal(false)
})
it("should not flush any docs in track changes", function() { return MockTrackChangesApi.flushDoc.called.should.equal(false); }); return it('should not flush to project history', function () {
return MockProjectHistoryApi.flushProject.called.should.equal(false)
})
})
return it("should not flush to project history", function() { return MockProjectHistoryApi.flushProject.called.should.equal(false); }); 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')
return describe("with the background=true parameter from realtime and a request to flush the queue", function() { return async.series(
before(function(done) { this.docs.map((doc) => {
sinon.spy(MockWebApi, "setDocument"); return (callback) => {
sinon.spy(MockTrackChangesApi, "flushDoc"); return DocUpdaterClient.preloadDoc(
sinon.spy(MockProjectHistoryApi, "flushProject"); 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)
}
)
})
return async.series(this.docs.map(doc => { after(function () {
return callback => { MockWebApi.setDocument.restore()
return DocUpdaterClient.preloadDoc(this.project_id, doc.id, callback); MockTrackChangesApi.flushDoc.restore()
}; return MockProjectHistoryApi.flushProject.restore()
}), 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(function() { it('should return a 204 status code', function () {
MockWebApi.setDocument.restore(); return this.statusCode.should.equal(204)
MockTrackChangesApi.flushDoc.restore(); })
return MockProjectHistoryApi.flushProject.restore();
});
it("should return a 204 status code", function() { it('should send each document to the web api', function () {
return this.statusCode.should.equal(204); return Array.from(this.docs).map((doc) =>
}); MockWebApi.setDocument
.calledWith(this.project_id, doc.id, doc.updatedLines)
.should.equal(true)
)
})
it("should send each document to the web api", function() { it('should flush each doc in track changes', function () {
return Array.from(this.docs).map((doc) => return Array.from(this.docs).map((doc) =>
MockWebApi.setDocument MockTrackChangesApi.flushDoc.calledWith(doc.id).should.equal(true)
.calledWith(this.project_id, doc.id, doc.updatedLines) )
.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));
});
return it("should flush to project history", function() { return MockProjectHistoryApi.flushProject.called.should.equal(true); });
});
});
return it('should flush to project history', function () {
return MockProjectHistoryApi.flushProject.called.should.equal(true)
})
})
})

View file

@ -11,101 +11,135 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
const sinon = require("sinon"); const sinon = require('sinon')
const chai = require("chai"); const chai = require('chai')
chai.should(); chai.should()
const async = require("async"); const async = require('async')
const MockWebApi = require("./helpers/MockWebApi"); const MockWebApi = require('./helpers/MockWebApi')
const DocUpdaterClient = require("./helpers/DocUpdaterClient"); const DocUpdaterClient = require('./helpers/DocUpdaterClient')
const DocUpdaterApp = require("./helpers/DocUpdaterApp"); const DocUpdaterApp = require('./helpers/DocUpdaterApp')
describe("Flushing a project", function() { describe('Flushing a project', function () {
before(function(done) { before(function (done) {
let doc_id0, doc_id1; let doc_id0, doc_id1
this.project_id = DocUpdaterClient.randomId(); this.project_id = DocUpdaterClient.randomId()
this.docs = [{ this.docs = [
id: (doc_id0 = DocUpdaterClient.randomId()), {
lines: ["one", "two", "three"], id: (doc_id0 = DocUpdaterClient.randomId()),
update: { lines: ['one', 'two', 'three'],
doc: doc_id0, update: {
op: [{ doc: doc_id0,
i: "one and a half\n", op: [
p: 4 {
}], i: 'one and a half\n',
v: 0 p: 4
}, }
updatedLines: ["one", "one and a half", "two", "three"] ],
}, { v: 0
id: (doc_id1 = DocUpdaterClient.randomId()), },
lines: ["four", "five", "six"], updatedLines: ['one', 'one and a half', 'two', 'three']
update: { },
doc: doc_id1, {
op: [{ id: (doc_id1 = DocUpdaterClient.randomId()),
i: "four and a half\n", lines: ['four', 'five', 'six'],
p: 5 update: {
}], doc: doc_id1,
v: 0 op: [
}, {
updatedLines: ["four", "four and a half", "five", "six"] i: 'four and a half\n',
}]; p: 5
for (const doc of Array.from(this.docs)) { }
MockWebApi.insertDoc(this.project_id, doc.id, { ],
lines: doc.lines, v: 0
version: doc.update.v },
}); updatedLines: ['four', 'four and a half', 'five', 'six']
} }
return DocUpdaterApp.ensureRunning(done); ]
}); for (const doc of Array.from(this.docs)) {
MockWebApi.insertDoc(this.project_id, doc.id, {
lines: doc.lines,
version: doc.update.v
})
}
return DocUpdaterApp.ensureRunning(done)
})
return describe("with documents which have been updated", function() { return describe('with documents which have been updated', function () {
before(function(done) { before(function (done) {
sinon.spy(MockWebApi, "setDocument"); sinon.spy(MockWebApi, 'setDocument')
return async.series(this.docs.map(doc => { return async.series(
return callback => { this.docs.map((doc) => {
return DocUpdaterClient.preloadDoc(this.project_id, doc.id, error => { return (callback) => {
if (error != null) { return callback(error); } return DocUpdaterClient.preloadDoc(
return DocUpdaterClient.sendUpdate(this.project_id, doc.id, doc.update, error => { this.project_id,
return callback(error); doc.id,
}); (error) => {
}); if (error != null) {
}; return callback(error)
}), error => { }
if (error != null) { throw error; } return DocUpdaterClient.sendUpdate(
return setTimeout(() => { this.project_id,
return DocUpdaterClient.flushProject(this.project_id, (error, res, body) => { doc.id,
this.statusCode = res.statusCode; doc.update,
return done(); (error) => {
}); return callback(error)
} }
, 200); )
}); }
}); )
}
}),
(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(function() { return MockWebApi.setDocument.restore(); }); after(function () {
return MockWebApi.setDocument.restore()
})
it("should return a 204 status code", function() { it('should return a 204 status code', function () {
return this.statusCode.should.equal(204); return this.statusCode.should.equal(204)
}); })
it("should send each document to the web api", function() { it('should send each document to the web api', function () {
return Array.from(this.docs).map((doc) => return Array.from(this.docs).map((doc) =>
MockWebApi.setDocument MockWebApi.setDocument
.calledWith(this.project_id, doc.id, doc.updatedLines) .calledWith(this.project_id, doc.id, doc.updatedLines)
.should.equal(true)); .should.equal(true)
}); )
})
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);
});
});
});
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
)
})
})
})

View file

@ -13,108 +13,153 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
const sinon = require("sinon"); const sinon = require('sinon')
const chai = require("chai"); const chai = require('chai')
chai.should(); chai.should()
const { const { expect } = chai
expect const async = require('async')
} = chai;
const async = require("async");
const MockWebApi = require("./helpers/MockWebApi"); const MockWebApi = require('./helpers/MockWebApi')
const DocUpdaterClient = require("./helpers/DocUpdaterClient"); const DocUpdaterClient = require('./helpers/DocUpdaterClient')
const DocUpdaterApp = require("./helpers/DocUpdaterApp"); const DocUpdaterApp = require('./helpers/DocUpdaterApp')
describe("Flushing a doc to Mongo", function() { describe('Flushing a doc to Mongo', function () {
before(function(done) { before(function (done) {
this.lines = ["one", "two", "three"]; this.lines = ['one', 'two', 'three']
this.version = 42; this.version = 42
this.update = { this.update = {
doc: this.doc_id, doc: this.doc_id,
meta: { user_id: 'last-author-fake-id' }, meta: { user_id: 'last-author-fake-id' },
op: [{ op: [
i: "one and a half\n", {
p: 4 i: 'one and a half\n',
}], p: 4
v: this.version }
}; ],
this.result = ["one", "one and a half", "two", "three"]; v: this.version
return DocUpdaterApp.ensureRunning(done); }
}); this.result = ['one', 'one and a half', 'two', 'three']
return DocUpdaterApp.ensureRunning(done)
})
describe("when the updated doc exists in the doc updater", function() { describe('when the updated doc exists in the doc updater', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[this.project_id, this.doc_id] = Array.from([
sinon.spy(MockWebApi, "setDocument"); DocUpdaterClient.randomId(),
DocUpdaterClient.randomId()
])
sinon.spy(MockWebApi, 'setDocument')
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version}); MockWebApi.insertDoc(this.project_id, this.doc_id, {
return DocUpdaterClient.sendUpdates(this.project_id, this.doc_id, [this.update], error => { lines: this.lines,
if (error != null) { throw error; } version: this.version
return setTimeout(() => { })
return DocUpdaterClient.flushDoc(this.project_id, this.doc_id, done); return DocUpdaterClient.sendUpdates(
} this.project_id,
, 200); 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(function() { return MockWebApi.setDocument.restore(); }); after(function () {
return MockWebApi.setDocument.restore()
})
it("should flush the updated doc lines and version to the web api", function() { it('should flush the updated doc lines and version to the web api', function () {
return MockWebApi.setDocument return MockWebApi.setDocument
.calledWith(this.project_id, this.doc_id, this.result, this.version + 1) .calledWith(this.project_id, this.doc_id, this.result, this.version + 1)
.should.equal(true); .should.equal(true)
}); })
return it("should flush the last update author and time to the web api", function() { return it('should flush the last update author and time to the web api', function () {
const lastUpdatedAt = MockWebApi.setDocument.lastCall.args[5]; const lastUpdatedAt = MockWebApi.setDocument.lastCall.args[5]
parseInt(lastUpdatedAt).should.be.closeTo((new Date()).getTime(), 30000); parseInt(lastUpdatedAt).should.be.closeTo(new Date().getTime(), 30000)
const lastUpdatedBy = MockWebApi.setDocument.lastCall.args[6]; const lastUpdatedBy = MockWebApi.setDocument.lastCall.args[6]
return lastUpdatedBy.should.equal('last-author-fake-id'); return lastUpdatedBy.should.equal('last-author-fake-id')
}); })
}); })
describe("when the doc does not exist in the doc updater", function() { describe('when the doc does not exist in the doc updater', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[this.project_id, this.doc_id] = Array.from([
MockWebApi.insertDoc(this.project_id, this.doc_id, { DocUpdaterClient.randomId(),
lines: this.lines DocUpdaterClient.randomId()
}); ])
sinon.spy(MockWebApi, "setDocument"); MockWebApi.insertDoc(this.project_id, this.doc_id, {
return DocUpdaterClient.flushDoc(this.project_id, this.doc_id, done); lines: this.lines
}); })
sinon.spy(MockWebApi, 'setDocument')
return DocUpdaterClient.flushDoc(this.project_id, this.doc_id, done)
})
after(function() { return MockWebApi.setDocument.restore(); }); after(function () {
return MockWebApi.setDocument.restore()
})
return it("should not flush the doc to the web api", function() { return MockWebApi.setDocument.called.should.equal(false); }); return it('should not flush the doc to the web api', function () {
}); return MockWebApi.setDocument.called.should.equal(false)
})
})
return describe("when the web api http request takes a long time on first request", function() { return describe('when the web api http request takes a long time on first request', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[this.project_id, this.doc_id] = Array.from([
MockWebApi.insertDoc(this.project_id, this.doc_id, { DocUpdaterClient.randomId(),
lines: this.lines, DocUpdaterClient.randomId()
version: this.version ])
}); MockWebApi.insertDoc(this.project_id, this.doc_id, {
let t = 30000; lines: this.lines,
sinon.stub(MockWebApi, "setDocument", (project_id, doc_id, lines, version, ranges, lastUpdatedAt, lastUpdatedBy, callback) => { version: this.version
if (callback == null) { callback = function(error) {}; } })
setTimeout(callback, t); let t = 30000
return t = 0; sinon.stub(
}); MockWebApi,
return DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, done); 'setDocument',
}); (
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(function() { return MockWebApi.setDocument.restore(); }); after(function () {
return MockWebApi.setDocument.restore()
return it("should still work", function(done) { })
const start = Date.now();
return DocUpdaterClient.flushDoc(this.project_id, this.doc_id, (error, res, doc) => { return it('should still work', function (done) {
res.statusCode.should.equal(204); const start = Date.now()
const delta = Date.now() - start; return DocUpdaterClient.flushDoc(
expect(delta).to.be.below(20000); this.project_id,
return done(); this.doc_id,
}); (error, res, doc) => {
}); res.statusCode.should.equal(204)
}); const delta = Date.now() - start
}); expect(delta).to.be.below(20000)
return done()
}
)
})
})
})

View file

@ -11,184 +11,278 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
const sinon = require("sinon"); const sinon = require('sinon')
const chai = require("chai"); const chai = require('chai')
chai.should(); chai.should()
const { const { expect } = chai
expect
} = chai;
const MockWebApi = require("./helpers/MockWebApi"); const MockWebApi = require('./helpers/MockWebApi')
const DocUpdaterClient = require("./helpers/DocUpdaterClient"); const DocUpdaterClient = require('./helpers/DocUpdaterClient')
const DocUpdaterApp = require("./helpers/DocUpdaterApp"); const DocUpdaterApp = require('./helpers/DocUpdaterApp')
describe("Getting a document", function() { describe('Getting a document', function () {
before(function(done) { before(function (done) {
this.lines = ["one", "two", "three"]; this.lines = ['one', 'two', 'three']
this.version = 42; this.version = 42
return DocUpdaterApp.ensureRunning(done); return DocUpdaterApp.ensureRunning(done)
}); })
describe("when the document is not loaded", function() { describe('when the document is not loaded', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[this.project_id, this.doc_id] = Array.from([
sinon.spy(MockWebApi, "getDocument"); DocUpdaterClient.randomId(),
DocUpdaterClient.randomId()
])
sinon.spy(MockWebApi, 'getDocument')
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version}); MockWebApi.insertDoc(this.project_id, this.doc_id, {
lines: this.lines,
version: this.version
})
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, returnedDoc) => { this.returnedDoc = returnedDoc; return done(); }); return DocUpdaterClient.getDoc(
}); this.project_id,
this.doc_id,
(error, res, returnedDoc) => {
this.returnedDoc = returnedDoc
return done()
}
)
})
after(function() { return MockWebApi.getDocument.restore(); }); after(function () {
return MockWebApi.getDocument.restore()
})
it("should load the document from the web API", function() { it('should load the document from the web API', function () {
return MockWebApi.getDocument return MockWebApi.getDocument
.calledWith(this.project_id, this.doc_id) .calledWith(this.project_id, this.doc_id)
.should.equal(true); .should.equal(true)
}); })
it("should return the document lines", function() {
return this.returnedDoc.lines.should.deep.equal(this.lines);
});
return it("should return the document at its current version", function() { it('should return the document lines', function () {
return this.returnedDoc.version.should.equal(this.version); return this.returnedDoc.lines.should.deep.equal(this.lines)
}); })
});
describe("when the document is already loaded", function() { return it('should return the document at its current version', function () {
before(function(done) { return this.returnedDoc.version.should.equal(this.version)
[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});
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(function() { return MockWebApi.getDocument.restore(); }); describe('when the document is already loaded', function () {
before(function (done) {
;[this.project_id, this.doc_id] = Array.from([
DocUpdaterClient.randomId(),
DocUpdaterClient.randomId()
])
it("should not load the document from the web API", function() { return MockWebApi.getDocument.called.should.equal(false); }); 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()
}
)
}
)
})
return it("should return the document lines", function() { after(function () {
return this.returnedDoc.lines.should.deep.equal(this.lines); return MockWebApi.getDocument.restore()
}); })
});
describe("when the request asks for some recent ops", function() { it('should not load the document from the web API', function () {
before(function(done) { return MockWebApi.getDocument.called.should.equal(false)
[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"])
});
this.updates = __range__(0, 199, true).map((v) => ({ return it('should return the document lines', function () {
doc_id: this.doc_id, return this.returnedDoc.lines.should.deep.equal(this.lines)
op: [{i: v.toString(), p: 0}], })
v })
}));
return DocUpdaterClient.sendUpdates(this.project_id, this.doc_id, this.updates, error => { describe('when the request asks for some recent ops', function () {
if (error != null) { throw error; } before(function (done) {
sinon.spy(MockWebApi, "getDocument"); ;[this.project_id, this.doc_id] = Array.from([
return done(); DocUpdaterClient.randomId(),
}); DocUpdaterClient.randomId()
}); ])
MockWebApi.insertDoc(this.project_id, this.doc_id, {
lines: (this.lines = ['one', 'two', 'three'])
})
after(function() { return MockWebApi.getDocument.restore(); }); this.updates = __range__(0, 199, true).map((v) => ({
doc_id: this.doc_id,
describe("when the ops are loaded", function() { op: [{ i: v.toString(), p: 0 }],
before(function(done) { v
return DocUpdaterClient.getDocAndRecentOps(this.project_id, this.doc_id, 190, (error, res, returnedDoc) => { this.returnedDoc = returnedDoc; return done(); }); }))
});
return it("should return the recent ops", function() { return DocUpdaterClient.sendUpdates(
this.returnedDoc.ops.length.should.equal(10); this.project_id,
return Array.from(this.updates.slice(190, -1)).map((update, i) => this.doc_id,
this.returnedDoc.ops[i].op.should.deep.equal(update.op)); this.updates,
}); (error) => {
}); if (error != null) {
throw error
return describe("when the ops are not all loaded", function() { }
before(function(done) { sinon.spy(MockWebApi, 'getDocument')
// We only track 100 ops return done()
return DocUpdaterClient.getDocAndRecentOps(this.project_id, this.doc_id, 10, (error, res, returnedDoc) => { this.res = res; this.returnedDoc = returnedDoc; return done(); }); }
}); )
})
return it("should return UnprocessableEntity", function() { after(function () {
return this.res.statusCode.should.equal(422); return MockWebApi.getDocument.restore()
}); })
});
});
describe("when the document does not exist", function() { describe('when the ops are loaded', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); return DocUpdaterClient.getDocAndRecentOps(
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => { this.project_id,
this.statusCode = res.statusCode; this.doc_id,
return done(); 190,
}); (error, res, returnedDoc) => {
}); this.returnedDoc = returnedDoc
return done()
}
)
})
return it("should return 404", function() { return it('should return the recent ops', function () {
return this.statusCode.should.equal(404); 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 web api returns an error", function() { return describe('when the ops are not all loaded', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); // We only track 100 ops
sinon.stub(MockWebApi, "getDocument", (project_id, doc_id, callback) => { return DocUpdaterClient.getDocAndRecentOps(
if (callback == null) { callback = function(error, doc) {}; } this.project_id,
return callback(new Error("oops")); this.doc_id,
}); 10,
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => { (error, res, returnedDoc) => {
this.statusCode = res.statusCode; this.res = res
return done(); this.returnedDoc = returnedDoc
}); return done()
}); }
)
})
after(function() { return MockWebApi.getDocument.restore(); }); return it('should return UnprocessableEntity', function () {
return this.res.statusCode.should.equal(422)
})
})
})
return it("should return 500", function() { describe('when the document does not exist', function () {
return this.statusCode.should.equal(500); 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()
}
)
})
return describe("when the web api http request takes a long time", function() { return it('should return 404', function () {
before(function(done) { return this.statusCode.should.equal(404)
this.timeout = 10000; })
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); })
sinon.stub(MockWebApi, "getDocument", (project_id, doc_id, callback) => {
if (callback == null) { callback = function(error, doc) {}; }
return setTimeout(callback, 30000);
});
return done();
});
after(function() { return MockWebApi.getDocument.restore(); }); describe('when the web api returns an error', function () {
before(function (done) {
return it("should return quickly(ish)", function(done) { ;[this.project_id, this.doc_id] = Array.from([
const start = Date.now(); DocUpdaterClient.randomId(),
return DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => { DocUpdaterClient.randomId()
res.statusCode.should.equal(500); ])
const delta = Date.now() - start; sinon.stub(MockWebApi, 'getDocument', (project_id, doc_id, callback) => {
expect(delta).to.be.below(20000); if (callback == null) {
return done(); 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(function () {
return MockWebApi.getDocument.restore()
})
return it('should return 500', function () {
return this.statusCode.should.equal(500)
})
})
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', (project_id, doc_id, callback) => {
if (callback == null) {
callback = function (error, doc) {}
}
return setTimeout(callback, 30000)
})
return done()
})
after(function () {
return MockWebApi.getDocument.restore()
})
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) { function __range__(left, right, inclusive) {
const range = []; const range = []
const ascending = left < right; const ascending = left < right
const end = !inclusive ? right : ascending ? right + 1 : right - 1; const end = !inclusive ? right : ascending ? right + 1 : right - 1
for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) { for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
range.push(i); range.push(i)
} }
return range; return range
} }

View file

@ -11,105 +11,163 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
const sinon = require("sinon"); const sinon = require('sinon')
const chai = require("chai"); const chai = require('chai')
chai.should(); chai.should()
const { const { expect } = chai
expect
} = chai;
const MockWebApi = require("./helpers/MockWebApi"); const MockWebApi = require('./helpers/MockWebApi')
const DocUpdaterClient = require("./helpers/DocUpdaterClient"); const DocUpdaterClient = require('./helpers/DocUpdaterClient')
const DocUpdaterApp = require("./helpers/DocUpdaterApp"); const DocUpdaterApp = require('./helpers/DocUpdaterApp')
describe("Getting documents for project", function() { describe('Getting documents for project', function () {
before(function(done) { before(function (done) {
this.lines = ["one", "two", "three"]; this.lines = ['one', 'two', 'three']
this.version = 42; this.version = 42
return DocUpdaterApp.ensureRunning(done); return DocUpdaterApp.ensureRunning(done)
}); })
describe("when project state hash does not match", function() { describe('when project state hash does not match', function () {
before(function(done) { before(function (done) {
this.projectStateHash = DocUpdaterClient.randomId(); this.projectStateHash = DocUpdaterClient.randomId()
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[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}); MockWebApi.insertDoc(this.project_id, this.doc_id, {
return DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => { lines: this.lines,
if (error != null) { throw error; } version: this.version
return DocUpdaterClient.getProjectDocs(this.project_id, this.projectStateHash, (error, res, returnedDocs) => { })
this.res = res; return DocUpdaterClient.preloadDoc(
this.returnedDocs = returnedDocs; this.project_id,
return done(); 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()
}
)
}
)
})
return it("should return a 409 Conflict response", function() { return it('should return a 409 Conflict response', function () {
return this.res.statusCode.should.equal(409); return this.res.statusCode.should.equal(409)
}); })
}); })
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()
])
describe("when project state hash matches", function() { MockWebApi.insertDoc(this.project_id, this.doc_id, {
before(function(done) { lines: this.lines,
this.projectStateHash = DocUpdaterClient.randomId(); version: this.version
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); })
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()
}
)
}
)
}
)
})
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version}); it('should return a 200 response', function () {
return DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => { return this.res.statusCode.should.equal(200)
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", function() { return it('should return the documents', function () {
return this.res.statusCode.should.equal(200); return this.returnedDocs.should.deep.equal([
}); { _id: this.doc_id, lines: this.lines, v: this.version }
])
})
})
return it("should return the documents", function() { return describe('when the doc has been removed', function () {
return this.returnedDocs.should.deep.equal([ {_id: this.doc_id, lines: this.lines, v: this.version} ]); before(function (done) {
}); this.projectStateHash = DocUpdaterClient.randomId()
}); ;[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
})
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()
}
)
}
)
}
)
}
)
})
return describe("when the doc has been removed", function() { return it('should return a 409 Conflict response', function () {
before(function(done) { return this.res.statusCode.should.equal(409)
this.projectStateHash = DocUpdaterClient.randomId(); })
[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});
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();
});
});
});
});
});
return it("should return a 409 Conflict response", function() {
return this.res.statusCode.should.equal(409);
});
});
});

File diff suppressed because it is too large Load diff

View file

@ -12,316 +12,462 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
const sinon = require("sinon"); const sinon = require('sinon')
const chai = require("chai"); const chai = require('chai')
chai.should(); chai.should()
const { const { expect } = require('chai')
expect const Settings = require('settings-sharelatex')
} = require("chai"); const rclient_du = require('redis-sharelatex').createClient(
const Settings = require('settings-sharelatex'); Settings.redis.documentupdater
const rclient_du = require("redis-sharelatex").createClient(Settings.redis.documentupdater); )
const Keys = Settings.redis.documentupdater.key_schema; const Keys = Settings.redis.documentupdater.key_schema
const MockTrackChangesApi = require("./helpers/MockTrackChangesApi"); const MockTrackChangesApi = require('./helpers/MockTrackChangesApi')
const MockProjectHistoryApi = require("./helpers/MockProjectHistoryApi"); const MockProjectHistoryApi = require('./helpers/MockProjectHistoryApi')
const MockWebApi = require("./helpers/MockWebApi"); const MockWebApi = require('./helpers/MockWebApi')
const DocUpdaterClient = require("./helpers/DocUpdaterClient"); const DocUpdaterClient = require('./helpers/DocUpdaterClient')
const DocUpdaterApp = require("./helpers/DocUpdaterApp"); const DocUpdaterApp = require('./helpers/DocUpdaterApp')
describe("Setting a document", function() { describe('Setting a document', function () {
before(function(done) { before(function (done) {
this.lines = ["one", "two", "three"]; this.lines = ['one', 'two', 'three']
this.version = 42; this.version = 42
this.update = { this.update = {
doc: this.doc_id, doc: this.doc_id,
op: [{ op: [
i: "one and a half\n", {
p: 4 i: 'one and a half\n',
}], p: 4
v: this.version }
}; ],
this.result = ["one", "one and a half", "two", "three"]; v: this.version
this.newLines = ["these", "are", "the", "new", "lines"]; }
this.source = "dropbox"; this.result = ['one', 'one and a half', 'two', 'three']
this.user_id = "user-id-123"; this.newLines = ['these', 'are', 'the', 'new', 'lines']
this.source = 'dropbox'
this.user_id = 'user-id-123'
sinon.spy(MockTrackChangesApi, "flushDoc"); sinon.spy(MockTrackChangesApi, 'flushDoc')
sinon.spy(MockProjectHistoryApi, "flushProject"); sinon.spy(MockProjectHistoryApi, 'flushProject')
sinon.spy(MockWebApi, "setDocument"); sinon.spy(MockWebApi, 'setDocument')
return DocUpdaterApp.ensureRunning(done); return DocUpdaterApp.ensureRunning(done)
}); })
after(function() { after(function () {
MockTrackChangesApi.flushDoc.restore(); MockTrackChangesApi.flushDoc.restore()
MockProjectHistoryApi.flushProject.restore(); MockProjectHistoryApi.flushProject.restore()
return MockWebApi.setDocument.restore(); return MockWebApi.setDocument.restore()
}); })
describe("when the updated doc exists in the doc updater", function() { describe('when the updated doc exists in the doc updater', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[this.project_id, this.doc_id] = Array.from([
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version}); DocUpdaterClient.randomId(),
DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => { DocUpdaterClient.randomId()
if (error != null) { throw error; } ])
return DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.update, error => { MockWebApi.insertDoc(this.project_id, this.doc_id, {
if (error != null) { throw error; } lines: this.lines,
return setTimeout(() => { version: this.version
return DocUpdaterClient.setDocLines(this.project_id, this.doc_id, this.newLines, this.source, this.user_id, false, (error, res, body) => { })
this.statusCode = res.statusCode; DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, (error) => {
return done(); if (error != null) {
}); throw error
} }
, 200); return DocUpdaterClient.sendUpdate(
}); this.project_id,
}); this.doc_id,
return null; 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(function() { after(function () {
MockTrackChangesApi.flushDoc.reset(); MockTrackChangesApi.flushDoc.reset()
MockProjectHistoryApi.flushProject.reset(); MockProjectHistoryApi.flushProject.reset()
return MockWebApi.setDocument.reset(); return MockWebApi.setDocument.reset()
}); })
it("should return a 204 status code", function() { it('should return a 204 status code', function () {
return this.statusCode.should.equal(204); return this.statusCode.should.equal(204)
}); })
it("should send the updated doc lines and version to the web api", function() { it('should send the updated doc lines and version to the web api', function () {
return MockWebApi.setDocument return MockWebApi.setDocument
.calledWith(this.project_id, this.doc_id, this.newLines) .calledWith(this.project_id, this.doc_id, this.newLines)
.should.equal(true); .should.equal(true)
}); })
it("should update the lines in the doc updater", function(done) { it('should update the lines in the doc updater', function (done) {
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => { DocUpdaterClient.getDoc(
doc.lines.should.deep.equal(this.newLines); this.project_id,
return done(); this.doc_id,
}); (error, res, doc) => {
return null; doc.lines.should.deep.equal(this.newLines)
}); return done()
}
)
return null
})
it("should bump the version in the doc updater", function(done) { it('should bump the version in the doc updater', function (done) {
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, doc) => { DocUpdaterClient.getDoc(
doc.version.should.equal(this.version + 2); this.project_id,
return done(); this.doc_id,
}); (error, res, doc) => {
return null; doc.version.should.equal(this.version + 2)
}); return done()
}
)
return null
})
return it("should leave the document in redis", function(done) { return it('should leave the document in redis', function (done) {
rclient_du.get(Keys.docLines({doc_id: this.doc_id}), (error, lines) => { rclient_du.get(Keys.docLines({ doc_id: this.doc_id }), (error, lines) => {
if (error != null) { throw error; } if (error != null) {
expect(JSON.parse(lines)).to.deep.equal(this.newLines); throw error
return done(); }
}); expect(JSON.parse(lines)).to.deep.equal(this.newLines)
return null; return done()
}); })
}); return null
})
})
describe("when the updated doc does not exist in the doc updater", function() { describe('when the updated doc does not exist in the doc updater', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[this.project_id, this.doc_id] = Array.from([
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version}); DocUpdaterClient.randomId(),
DocUpdaterClient.setDocLines(this.project_id, this.doc_id, this.newLines, this.source, this.user_id, false, (error, res, body) => { DocUpdaterClient.randomId()
this.statusCode = res.statusCode; ])
return setTimeout(done, 200); MockWebApi.insertDoc(this.project_id, this.doc_id, {
}); lines: this.lines,
return null; 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(function() { after(function () {
MockTrackChangesApi.flushDoc.reset(); MockTrackChangesApi.flushDoc.reset()
MockProjectHistoryApi.flushProject.reset(); MockProjectHistoryApi.flushProject.reset()
return MockWebApi.setDocument.reset(); return MockWebApi.setDocument.reset()
}); })
it("should return a 204 status code", function() { it('should return a 204 status code', function () {
return this.statusCode.should.equal(204); return this.statusCode.should.equal(204)
}); })
it("should send the updated doc lines to the web api", function() { it('should send the updated doc lines to the web api', function () {
return MockWebApi.setDocument return MockWebApi.setDocument
.calledWith(this.project_id, this.doc_id, this.newLines) .calledWith(this.project_id, this.doc_id, this.newLines)
.should.equal(true); .should.equal(true)
}); })
it("should flush track changes", function() { it('should flush track changes', function () {
return MockTrackChangesApi.flushDoc.calledWith(this.doc_id).should.equal(true); return MockTrackChangesApi.flushDoc
}); .calledWith(this.doc_id)
.should.equal(true)
})
it("should flush project history", function() { it('should flush project history', function () {
return MockProjectHistoryApi.flushProject.calledWith(this.project_id).should.equal(true); return MockProjectHistoryApi.flushProject
}); .calledWith(this.project_id)
.should.equal(true)
})
return it("should remove the document from redis", function(done) { return it('should remove the document from redis', function (done) {
rclient_du.get(Keys.docLines({doc_id: this.doc_id}), (error, lines) => { rclient_du.get(Keys.docLines({ doc_id: this.doc_id }), (error, lines) => {
if (error != null) { throw error; } if (error != null) {
expect(lines).to.not.exist; throw error
return done(); }
}); expect(lines).to.not.exist
return null; return done()
}); })
}); return null
})
})
describe("when the updated doc is too large for the body parser", function() { describe('when the updated doc is too large for the body parser', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[this.project_id, this.doc_id] = Array.from([
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version}); DocUpdaterClient.randomId(),
this.newLines = []; DocUpdaterClient.randomId()
while (JSON.stringify(this.newLines).length < (Settings.max_doc_length + (64 * 1024))) { ])
this.newLines.push("(a long line of text)".repeat(10000)); MockWebApi.insertDoc(this.project_id, this.doc_id, {
} lines: this.lines,
DocUpdaterClient.setDocLines(this.project_id, this.doc_id, this.newLines, this.source, this.user_id, false, (error, res, body) => { version: this.version
this.statusCode = res.statusCode; })
return setTimeout(done, 200); this.newLines = []
}); while (
return null; 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(function() { after(function () {
MockTrackChangesApi.flushDoc.reset(); MockTrackChangesApi.flushDoc.reset()
MockProjectHistoryApi.flushProject.reset(); MockProjectHistoryApi.flushProject.reset()
return MockWebApi.setDocument.reset(); return MockWebApi.setDocument.reset()
}); })
it("should return a 413 status code", function() { it('should return a 413 status code', function () {
return this.statusCode.should.equal(413); return this.statusCode.should.equal(413)
}); })
it("should not send the updated doc lines to the web api", function() { return MockWebApi.setDocument.called.should.equal(false); }); it('should not send the updated doc lines to the web api', function () {
return MockWebApi.setDocument.called.should.equal(false)
})
it("should not flush track changes", function() { return MockTrackChangesApi.flushDoc.called.should.equal(false); }); it('should not flush track changes', function () {
return MockTrackChangesApi.flushDoc.called.should.equal(false)
})
return it("should not flush project history", function() { return MockProjectHistoryApi.flushProject.called.should.equal(false); }); return it('should not flush project history', function () {
}); return MockProjectHistoryApi.flushProject.called.should.equal(false)
})
})
describe("when the updated doc is large but under the bodyParser and HTTPController size limit", function() { describe('when the updated doc is large but under the bodyParser and HTTPController size limit', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[this.project_id, this.doc_id] = Array.from([
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version}); DocUpdaterClient.randomId(),
DocUpdaterClient.randomId()
])
MockWebApi.insertDoc(this.project_id, this.doc_id, {
lines: this.lines,
version: this.version
})
this.newLines = []; this.newLines = []
while (JSON.stringify(this.newLines).length < (2 * 1024 * 1024)) { // limit in HTTPController while (JSON.stringify(this.newLines).length < 2 * 1024 * 1024) {
this.newLines.push("(a long line of text)".repeat(10000)); // 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.newLines.pop() // remove the line which took it over the limit
this.statusCode = res.statusCode; DocUpdaterClient.setDocLines(
return setTimeout(done, 200); this.project_id,
}); this.doc_id,
return null; this.newLines,
}); this.source,
this.user_id,
false,
(error, res, body) => {
this.statusCode = res.statusCode
return setTimeout(done, 200)
}
)
return null
})
after(function() { after(function () {
MockTrackChangesApi.flushDoc.reset(); MockTrackChangesApi.flushDoc.reset()
MockProjectHistoryApi.flushProject.reset(); MockProjectHistoryApi.flushProject.reset()
return MockWebApi.setDocument.reset(); return MockWebApi.setDocument.reset()
}); })
it("should return a 204 status code", function() { it('should return a 204 status code', function () {
return this.statusCode.should.equal(204); return this.statusCode.should.equal(204)
}); })
return it("should send the updated doc lines to the web api", function() { return it('should send the updated doc lines to the web api', function () {
return MockWebApi.setDocument return MockWebApi.setDocument
.calledWith(this.project_id, this.doc_id, this.newLines) .calledWith(this.project_id, this.doc_id, this.newLines)
.should.equal(true); .should.equal(true)
}); })
}); })
return describe("with track changes", function() { return describe('with track changes', function () {
before(function() { before(function () {
this.lines = ["one", "one and a half", "two", "three"]; this.lines = ['one', 'one and a half', 'two', 'three']
this.id_seed = "587357bd35e64f6157"; this.id_seed = '587357bd35e64f6157'
return this.update = { return (this.update = {
doc: this.doc_id, doc: this.doc_id,
op: [{ op: [
d: "one and a half\n", {
p: 4 d: 'one and a half\n',
}], p: 4
meta: { }
tc: this.id_seed, ],
user_id: this.user_id meta: {
}, tc: this.id_seed,
v: this.version user_id: this.user_id
}; },
}); v: this.version
})
})
describe("with the undo flag", function() { describe('with the undo flag', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[this.project_id, this.doc_id] = Array.from([
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version}); DocUpdaterClient.randomId(),
DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => { DocUpdaterClient.randomId()
if (error != null) { throw error; } ])
return DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.update, error => { MockWebApi.insertDoc(this.project_id, this.doc_id, {
if (error != null) { throw error; } lines: this.lines,
// Go back to old lines, with undo flag version: this.version
return DocUpdaterClient.setDocLines(this.project_id, this.doc_id, this.lines, this.source, this.user_id, true, (error, res, body) => { })
this.statusCode = res.statusCode; DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, (error) => {
return setTimeout(done, 200); if (error != null) {
}); throw error
}); }
}); return DocUpdaterClient.sendUpdate(
return null; 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(function() { after(function () {
MockTrackChangesApi.flushDoc.reset(); MockTrackChangesApi.flushDoc.reset()
MockProjectHistoryApi.flushProject.reset(); MockProjectHistoryApi.flushProject.reset()
return MockWebApi.setDocument.reset(); return MockWebApi.setDocument.reset()
}); })
return it("should undo the tracked changes", function(done) { return it('should undo the tracked changes', function (done) {
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, data) => { DocUpdaterClient.getDoc(
if (error != null) { throw error; } this.project_id,
const { this.doc_id,
ranges (error, res, data) => {
} = data; if (error != null) {
expect(ranges.changes).to.be.undefined; throw error
return done(); }
}); const { ranges } = data
return null; expect(ranges.changes).to.be.undefined
}); return done()
}); }
)
return null
})
})
return describe("without the undo flag", function() { return describe('without the undo flag', function () {
before(function(done) { before(function (done) {
[this.project_id, this.doc_id] = Array.from([DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]); ;[this.project_id, this.doc_id] = Array.from([
MockWebApi.insertDoc(this.project_id, this.doc_id, {lines: this.lines, version: this.version}); DocUpdaterClient.randomId(),
DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, error => { DocUpdaterClient.randomId()
if (error != null) { throw error; } ])
return DocUpdaterClient.sendUpdate(this.project_id, this.doc_id, this.update, error => { MockWebApi.insertDoc(this.project_id, this.doc_id, {
if (error != null) { throw error; } lines: this.lines,
// Go back to old lines, without undo flag version: this.version
return DocUpdaterClient.setDocLines(this.project_id, this.doc_id, this.lines, this.source, this.user_id, false, (error, res, body) => { })
this.statusCode = res.statusCode; DocUpdaterClient.preloadDoc(this.project_id, this.doc_id, (error) => {
return setTimeout(done, 200); if (error != null) {
}); throw error
}); }
}); return DocUpdaterClient.sendUpdate(
return null; this.project_id,
}); this.doc_id,
this.update,
after(function() { (error) => {
MockTrackChangesApi.flushDoc.reset(); if (error != null) {
MockProjectHistoryApi.flushProject.reset(); throw error
return MockWebApi.setDocument.reset(); }
}); // Go back to old lines, without undo flag
return DocUpdaterClient.setDocLines(
return it("should not undo the tracked changes", function(done) { this.project_id,
DocUpdaterClient.getDoc(this.project_id, this.doc_id, (error, res, data) => { this.doc_id,
if (error != null) { throw error; } this.lines,
const { this.source,
ranges this.user_id,
} = data; false,
expect(ranges.changes.length).to.equal(1); (error, res, body) => {
return done(); this.statusCode = res.statusCode
}); return setTimeout(done, 200)
return null; }
}); )
}); }
}); )
}); })
return null
})
after(function () {
MockTrackChangesApi.flushDoc.reset()
MockProjectHistoryApi.flushProject.reset()
return MockWebApi.setDocument.reset()
})
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
})
})
})
})

View file

@ -11,33 +11,37 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
const app = require('../../../../app'); const app = require('../../../../app')
require("logger-sharelatex").logger.level("fatal"); require('logger-sharelatex').logger.level('fatal')
module.exports = { module.exports = {
running: false, running: false,
initing: false, initing: false,
callbacks: [], callbacks: [],
ensureRunning(callback) { ensureRunning(callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
if (this.running) { callback = function (error) {}
return callback(); }
} else if (this.initing) { if (this.running) {
return this.callbacks.push(callback); return callback()
} else { } else if (this.initing) {
this.initing = true; return this.callbacks.push(callback)
this.callbacks.push(callback); } else {
return app.listen(3003, "localhost", error => { this.initing = true
if (error != null) { throw error; } this.callbacks.push(callback)
this.running = true; return app.listen(3003, 'localhost', (error) => {
return (() => { if (error != null) {
const result = []; throw error
for (callback of Array.from(this.callbacks)) { }
result.push(callback()); this.running = true
} return (() => {
return result; const result = []
})(); for (callback of Array.from(this.callbacks)) {
}); result.push(callback())
} }
} return result
}; })()
})
}
}
}

View file

@ -11,167 +11,268 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
let DocUpdaterClient; let DocUpdaterClient
const Settings = require('settings-sharelatex'); const Settings = require('settings-sharelatex')
const rclient = require("redis-sharelatex").createClient(Settings.redis.documentupdater); const rclient = require('redis-sharelatex').createClient(
const keys = Settings.redis.documentupdater.key_schema; Settings.redis.documentupdater
const request = require("request").defaults({jar: false}); )
const async = require("async"); const keys = Settings.redis.documentupdater.key_schema
const request = require('request').defaults({ jar: false })
const async = require('async')
const rclient_sub = require("redis-sharelatex").createClient(Settings.redis.pubsub); const rclient_sub = require('redis-sharelatex').createClient(
rclient_sub.subscribe("applied-ops"); Settings.redis.pubsub
rclient_sub.setMaxListeners(0); )
rclient_sub.subscribe('applied-ops')
module.exports = (DocUpdaterClient = { rclient_sub.setMaxListeners(0)
randomId() {
const chars = __range__(1, 24, true).map((i) =>
Math.random().toString(16)[2]);
return chars.join("");
},
subscribeToAppliedOps(callback) {
if (callback == null) { callback = function(message) {}; }
return rclient_sub.on("message", callback);
},
sendUpdate(project_id, doc_id, update, callback) { module.exports = DocUpdaterClient = {
if (callback == null) { callback = function(error) {}; } randomId() {
return rclient.rpush(keys.pendingUpdates({doc_id}), JSON.stringify(update), (error) => { const chars = __range__(1, 24, true).map(
if (error != null) { return callback(error); } (i) => Math.random().toString(16)[2]
const doc_key = `${project_id}:${doc_id}`; )
return rclient.sadd("DocsWithPendingUpdates", doc_key, (error) => { return chars.join('')
if (error != null) { return callback(error); } },
return rclient.rpush("pending-updates-list", doc_key, callback);
});
});
},
sendUpdates(project_id, doc_id, updates, callback) { subscribeToAppliedOps(callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
return DocUpdaterClient.preloadDoc(project_id, doc_id, (error) => { callback = function (message) {}
if (error != null) { return callback(error); } }
const jobs = []; return rclient_sub.on('message', callback)
for (const 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) { sendUpdate(project_id, doc_id, update, callback) {
return async.retry({times: 30, interval: 100}, cb => rclient.llen(keys.pendingUpdates({doc_id}), (err, length) => { if (callback == null) {
if (length > 0) { callback = function (error) {}
return cb(new Error("updates still pending")); }
} else { return rclient.rpush(
return cb(); keys.pendingUpdates({ doc_id }),
} JSON.stringify(update),
(error) => {
if (error != null) {
return callback(error)
}
const doc_key = `${project_id}:${doc_id}`
return rclient.sadd('DocsWithPendingUpdates', doc_key, (error) => {
if (error != null) {
return callback(error)
}
return rclient.rpush('pending-updates-list', doc_key, callback)
}) })
, callback); }
}, )
},
getDoc(project_id, doc_id, callback) { sendUpdates(project_id, doc_id, updates, callback) {
if (callback == null) { callback = function(error, res, body) {}; } if (callback == null) {
return request.get(`http://localhost:3003/project/${project_id}/doc/${doc_id}`, (error, res, body) => { callback = function (error) {}
if ((body != null) && (res.statusCode >= 200) && (res.statusCode < 300)) { }
body = JSON.parse(body); return DocUpdaterClient.preloadDoc(project_id, doc_id, (error) => {
} if (error != null) {
return callback(error, res, body); return callback(error)
}); }
}, const jobs = []
for (const 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)
)
})
},
getDocAndRecentOps(project_id, doc_id, fromVersion, callback) { waitForPendingUpdates(project_id, doc_id, callback) {
if (callback == null) { callback = function(error, res, body) {}; } return async.retry(
return request.get(`http://localhost:3003/project/${project_id}/doc/${doc_id}?fromVersion=${fromVersion}`, (error, res, body) => { { times: 30, interval: 100 },
if ((body != null) && (res.statusCode >= 200) && (res.statusCode < 300)) { (cb) =>
body = JSON.parse(body); rclient.llen(keys.pendingUpdates({ doc_id }), (err, length) => {
} if (length > 0) {
return callback(error, res, body); return cb(new Error('updates still pending'))
}); } else {
}, return cb()
}
}),
callback
)
},
preloadDoc(project_id, doc_id, callback) { getDoc(project_id, doc_id, callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
return DocUpdaterClient.getDoc(project_id, doc_id, callback); callback = function (error, res, body) {}
}, }
return request.get(
`http://localhost:3003/project/${project_id}/doc/${doc_id}`,
(error, res, body) => {
if (body != null && res.statusCode >= 200 && res.statusCode < 300) {
body = JSON.parse(body)
}
return callback(error, res, body)
}
)
},
flushDoc(project_id, doc_id, callback) { getDocAndRecentOps(project_id, doc_id, fromVersion, callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
return request.post(`http://localhost:3003/project/${project_id}/doc/${doc_id}/flush`, (error, res, body) => callback(error, res, body)); callback = function (error, res, body) {}
}, }
return request.get(
`http://localhost:3003/project/${project_id}/doc/${doc_id}?fromVersion=${fromVersion}`,
(error, res, body) => {
if (body != null && res.statusCode >= 200 && res.statusCode < 300) {
body = JSON.parse(body)
}
return callback(error, res, body)
}
)
},
setDocLines(project_id, doc_id, lines, source, user_id, undoing, callback) { preloadDoc(project_id, doc_id, callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
return request.post({ callback = function (error) {}
url: `http://localhost:3003/project/${project_id}/doc/${doc_id}`, }
json: { return DocUpdaterClient.getDoc(project_id, doc_id, callback)
lines, },
source,
user_id,
undoing
}
}, (error, res, body) => callback(error, res, body));
},
deleteDoc(project_id, doc_id, callback) { flushDoc(project_id, doc_id, callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
return request.del(`http://localhost:3003/project/${project_id}/doc/${doc_id}`, (error, res, body) => callback(error, res, body)); callback = function (error) {}
}, }
return request.post(
`http://localhost:3003/project/${project_id}/doc/${doc_id}/flush`,
(error, res, body) => callback(error, res, body)
)
},
flushProject(project_id, callback) { setDocLines(project_id, doc_id, lines, source, user_id, undoing, callback) {
if (callback == null) { callback = function() {}; } if (callback == null) {
return request.post(`http://localhost:3003/project/${project_id}/flush`, callback); 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)
)
},
deleteProject(project_id, callback) { deleteDoc(project_id, doc_id, callback) {
if (callback == null) { callback = function() {}; } if (callback == null) {
return request.del(`http://localhost:3003/project/${project_id}`, callback); callback = function (error) {}
}, }
return request.del(
`http://localhost:3003/project/${project_id}/doc/${doc_id}`,
(error, res, body) => callback(error, res, body)
)
},
deleteProjectOnShutdown(project_id, callback) { flushProject(project_id, callback) {
if (callback == null) { callback = function() {}; } if (callback == null) {
return request.del(`http://localhost:3003/project/${project_id}?background=true&shutdown=true`, callback); callback = function () {}
}, }
return request.post(
`http://localhost:3003/project/${project_id}/flush`,
callback
)
},
flushOldProjects(callback) { deleteProject(project_id, callback) {
if (callback == null) { callback = function() {}; } if (callback == null) {
return request.get("http://localhost:3003/flush_queued_projects?min_delete_age=1", callback); callback = function () {}
}, }
return request.del(`http://localhost:3003/project/${project_id}`, callback)
},
acceptChange(project_id, doc_id, change_id, callback) { deleteProjectOnShutdown(project_id, callback) {
if (callback == null) { callback = function() {}; } if (callback == null) {
return request.post(`http://localhost:3003/project/${project_id}/doc/${doc_id}/change/${change_id}/accept`, callback); callback = function () {}
}, }
return request.del(
`http://localhost:3003/project/${project_id}?background=true&shutdown=true`,
callback
)
},
removeComment(project_id, doc_id, comment, callback) { flushOldProjects(callback) {
if (callback == null) { callback = function() {}; } if (callback == null) {
return request.del(`http://localhost:3003/project/${project_id}/doc/${doc_id}/comment/${comment}`, callback); callback = function () {}
}, }
return request.get(
'http://localhost:3003/flush_queued_projects?min_delete_age=1',
callback
)
},
getProjectDocs(project_id, projectStateHash, callback) { acceptChange(project_id, doc_id, change_id, callback) {
if (callback == null) { callback = function() {}; } if (callback == null) {
return request.get(`http://localhost:3003/project/${project_id}/doc?state=${projectStateHash}`, (error, res, body) => { callback = function () {}
if ((body != null) && (res.statusCode >= 200) && (res.statusCode < 300)) { }
body = JSON.parse(body); return request.post(
} `http://localhost:3003/project/${project_id}/doc/${doc_id}/change/${change_id}/accept`,
return callback(error, res, body); callback
}); )
}, },
sendProjectUpdate(project_id, userId, docUpdates, fileUpdates, version, callback) { removeComment(project_id, doc_id, comment, callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
return request.post({ callback = function () {}
url: `http://localhost:3003/project/${project_id}`, }
json: { userId, docUpdates, fileUpdates, version } return request.del(
}, (error, res, body) => callback(error, res, body)); `http://localhost:3003/project/${project_id}/doc/${doc_id}/comment/${comment}`,
} callback
}); )
},
getProjectDocs(project_id, projectStateHash, callback) {
if (callback == null) {
callback = function () {}
}
return request.get(
`http://localhost:3003/project/${project_id}/doc?state=${projectStateHash}`,
(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
) {
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)
)
}
}
function __range__(left, right, inclusive) { function __range__(left, right, inclusive) {
const range = []; const range = []
const ascending = left < right; const ascending = left < right
const end = !inclusive ? right : ascending ? right + 1 : right - 1; const end = !inclusive ? right : ascending ? right + 1 : right - 1
for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) { for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
range.push(i); range.push(i)
} }
return range; return range
} }

View file

@ -10,31 +10,35 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
let MockProjectHistoryApi; let MockProjectHistoryApi
const express = require("express"); const express = require('express')
const app = express(); const app = express()
module.exports = (MockProjectHistoryApi = { module.exports = MockProjectHistoryApi = {
flushProject(doc_id, callback) { flushProject(doc_id, callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
return callback(); callback = function (error) {}
}, }
return callback()
},
run() { run() {
app.post("/project/:project_id/flush", (req, res, next) => { app.post('/project/:project_id/flush', (req, res, next) => {
return this.flushProject(req.params.project_id, (error) => { return this.flushProject(req.params.project_id, (error) => {
if (error != null) { if (error != null) {
return res.sendStatus(500); return res.sendStatus(500)
} else { } else {
return res.sendStatus(204); return res.sendStatus(204)
} }
}); })
}); })
return app.listen(3054, (error) => { return app.listen(3054, (error) => {
if (error != null) { throw error; } if (error != null) {
}); throw error
} }
}); })
}
}
MockProjectHistoryApi.run(); MockProjectHistoryApi.run()

View file

@ -10,35 +10,40 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
let MockTrackChangesApi; let MockTrackChangesApi
const express = require("express"); const express = require('express')
const app = express(); const app = express()
module.exports = (MockTrackChangesApi = { module.exports = MockTrackChangesApi = {
flushDoc(doc_id, callback) { flushDoc(doc_id, callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
return callback(); callback = function (error) {}
}, }
return callback()
},
run() { run() {
app.post("/project/:project_id/doc/:doc_id/flush", (req, res, next) => { app.post('/project/:project_id/doc/:doc_id/flush', (req, res, next) => {
return this.flushDoc(req.params.doc_id, (error) => { return this.flushDoc(req.params.doc_id, (error) => {
if (error != null) { if (error != null) {
return res.sendStatus(500); return res.sendStatus(500)
} else { } else {
return res.sendStatus(204); return res.sendStatus(204)
} }
}); })
}); })
return app.listen(3015, (error) => { return app
if (error != null) { throw error; } .listen(3015, (error) => {
}).on("error", (error) => { if (error != null) {
console.error("error starting MockTrackChangesApi:", error.message); throw error
return process.exit(1); }
}); })
} .on('error', (error) => {
}); console.error('error starting MockTrackChangesApi:', error.message)
return process.exit(1)
MockTrackChangesApi.run(); })
}
}
MockTrackChangesApi.run()

View file

@ -11,72 +11,113 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
let MockWebApi; let MockWebApi
const express = require("express"); const express = require('express')
const bodyParser = require("body-parser"); const bodyParser = require('body-parser')
const app = express(); const app = express()
const MAX_REQUEST_SIZE = 2*((2*1024*1024) + (64*1024)); const MAX_REQUEST_SIZE = 2 * (2 * 1024 * 1024 + 64 * 1024)
module.exports = (MockWebApi = { module.exports = MockWebApi = {
docs: {}, docs: {},
clearDocs() { return this.docs = {}; }, clearDocs() {
return (this.docs = {})
},
insertDoc(project_id, doc_id, doc) { insertDoc(project_id, doc_id, doc) {
if (doc.version == null) { doc.version = 0; } if (doc.version == null) {
if (doc.lines == null) { doc.lines = []; } doc.version = 0
doc.pathname = '/a/b/c.tex'; }
return this.docs[`${project_id}:${doc_id}`] = doc; 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) { setDocument(
if (callback == null) { callback = function(error) {}; } project_id,
const doc = this.docs[`${project_id}:${doc_id}`] || (this.docs[`${project_id}:${doc_id}`] = {}); doc_id,
doc.lines = lines; lines,
doc.version = version; version,
doc.ranges = ranges; ranges,
doc.pathname = '/a/b/c.tex'; lastUpdatedAt,
doc.lastUpdatedAt = lastUpdatedAt; lastUpdatedBy,
doc.lastUpdatedBy = lastUpdatedBy; callback
return callback(null); ) {
}, 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) { getDocument(project_id, doc_id, callback) {
if (callback == null) { callback = function(error, doc) {}; } if (callback == null) {
return callback(null, this.docs[`${project_id}:${doc_id}`]); callback = function (error, doc) {}
}, }
return callback(null, this.docs[`${project_id}:${doc_id}`])
},
run() { run() {
app.get("/project/:project_id/doc/:doc_id", (req, res, next) => { app.get('/project/:project_id/doc/:doc_id', (req, res, next) => {
return this.getDocument(req.params.project_id, req.params.doc_id, (error, doc) => { return this.getDocument(
if (error != null) { req.params.project_id,
return res.sendStatus(500); req.params.doc_id,
} else if (doc != null) { (error, doc) => {
return res.send(JSON.stringify(doc)); if (error != null) {
} else { return res.sendStatus(500)
return res.sendStatus(404); } 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) => { app.post(
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, (error) => { '/project/:project_id/doc/:doc_id',
if (error != null) { bodyParser.json({ limit: MAX_REQUEST_SIZE }),
return res.sendStatus(500); (req, res, next) => {
} else { return MockWebApi.setDocument(
return res.sendStatus(204); 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 != null) {
return res.sendStatus(500)
} else {
return res.sendStatus(204)
}
}
)
}
)
return app.listen(3000, (error) => { return app
if (error != null) { throw error; } .listen(3000, (error) => {
}).on("error", (error) => { if (error != null) {
console.error("error starting MockWebApi:", error.message); throw error
return process.exit(1); }
}); })
} .on('error', (error) => {
}); console.error('error starting MockWebApi:', error.message)
return process.exit(1)
MockWebApi.run(); })
}
}
MockWebApi.run()