From 267fc5393acdb76bdaf2f728ab3273ea75b17932 Mon Sep 17 00:00:00 2001 From: Domagoj Kriskovic Date: Tue, 7 Oct 2025 15:10:35 +0200 Subject: [PATCH] Promisify ProjectHistoryClient, ProjectHistoryApp, SyncTests and SendingUpdatesTests (#28890) GitOrigin-RevId: 7bf26c6ed1a172c6506449a821d4e43f424a72bd --- .../test/acceptance/js/DeleteProjectTests.js | 13 +- .../test/acceptance/js/DiffTests.js | 4 +- .../acceptance/js/DiscardingUpdatesTests.js | 8 +- .../test/acceptance/js/FileTreeDiffTests.js | 4 +- .../test/acceptance/js/FlushManagerTests.js | 49 +- .../acceptance/js/GetChangesInChunkSince.js | 5 +- .../test/acceptance/js/HealthCheckTests.js | 4 +- .../test/acceptance/js/LabelsTests.js | 4 +- .../test/acceptance/js/LatestSnapshotTests.js | 4 +- .../acceptance/js/ReadingASnapshotTests.js | 4 +- .../test/acceptance/js/RetryTests.js | 9 +- .../test/acceptance/js/SendingUpdatesTests.js | 1800 ++++------- .../acceptance/js/SummarisedUpdatesTests.js | 4 +- .../test/acceptance/js/SyncTests.js | 2670 +++++++---------- .../js/helpers/ProjectHistoryApp.js | 67 +- .../js/helpers/ProjectHistoryClient.js | 107 +- 16 files changed, 1922 insertions(+), 2834 deletions(-) diff --git a/services/project-history/test/acceptance/js/DeleteProjectTests.js b/services/project-history/test/acceptance/js/DeleteProjectTests.js index a8f449d0ba..0962c0a622 100644 --- a/services/project-history/test/acceptance/js/DeleteProjectTests.js +++ b/services/project-history/test/acceptance/js/DeleteProjectTests.js @@ -23,7 +23,7 @@ describe('Deleting project', function () { .get(`/api/projects/${this.historyId}/latest/history`) .replyWithFile(200, fixture('chunks/0-3.json')) MockHistoryStore().delete(`/api/projects/${this.historyId}`).reply(204) - await ProjectHistoryApp.promises.ensureRunning() + await ProjectHistoryApp.ensureRunning() }) describe('when the project has no pending updates', function () { @@ -34,16 +34,13 @@ describe('Deleting project', function () { describe('when the project has pending updates', function () { beforeEach(async function () { - await ProjectHistoryClient.promises.pushRawUpdate(this.projectId, { + await ProjectHistoryClient.pushRawUpdate(this.projectId, { pathname: '/main.tex', docLines: 'hello', doc: this.docId, meta: { userId: this.userId, ts: new Date() }, }) - await ProjectHistoryClient.promises.setFirstOpTimestamp( - this.projectId, - Date.now() - ) + await ProjectHistoryClient.setFirstOpTimestamp(this.projectId, Date.now()) await ProjectHistoryClient.deleteProject(this.projectId) }) @@ -53,9 +50,7 @@ describe('Deleting project', function () { }) it('clears the first op timestamp', async function () { - const ts = await ProjectHistoryClient.promises.getFirstOpTimestamp( - this.projectId - ) + const ts = await ProjectHistoryClient.getFirstOpTimestamp(this.projectId) expect(ts).to.be.null }) }) diff --git a/services/project-history/test/acceptance/js/DiffTests.js b/services/project-history/test/acceptance/js/DiffTests.js index 38e3b2cc58..aaa59083f7 100644 --- a/services/project-history/test/acceptance/js/DiffTests.js +++ b/services/project-history/test/acceptance/js/DiffTests.js @@ -24,7 +24,7 @@ function createMockBlob(historyId, content) { describe('Diffs', function () { beforeEach(async function () { - await ProjectHistoryApp.promises.ensureRunning() + await ProjectHistoryApp.ensureRunning() this.historyId = new ObjectId().toString() this.projectId = new ObjectId().toString() @@ -39,7 +39,7 @@ describe('Diffs', function () { overleaf: { history: { id: this.historyId } }, }) - await ProjectHistoryClient.promises.initializeProject(this.historyId) + await ProjectHistoryClient.initializeProject(this.historyId) }) afterEach(function () { diff --git a/services/project-history/test/acceptance/js/DiscardingUpdatesTests.js b/services/project-history/test/acceptance/js/DiscardingUpdatesTests.js index c86db7acd8..2adcb64869 100644 --- a/services/project-history/test/acceptance/js/DiscardingUpdatesTests.js +++ b/services/project-history/test/acceptance/js/DiscardingUpdatesTests.js @@ -11,7 +11,7 @@ describe('DiscardingUpdates', function () { beforeEach(async function () { this.timestamp = new Date() - await ProjectHistoryApp.promises.ensureRunning() + await ProjectHistoryApp.ensureRunning() this.user_id = new ObjectId().toString() this.project_id = new ObjectId().toString() this.doc_id = new ObjectId().toString() @@ -22,7 +22,7 @@ describe('DiscardingUpdates', function () { MockWeb() .get(`/project/${this.project_id}/details`) .reply(200, { name: 'Test Project' }) - await ProjectHistoryClient.promises.initializeProject(this.project_id) + await ProjectHistoryClient.initializeProject(this.project_id) }) it('should discard updates', async function () { @@ -32,7 +32,7 @@ describe('DiscardingUpdates', function () { doc: this.doc_id, meta: { user_id: this.user_id, ts: new Date() }, } - await ProjectHistoryClient.promises.pushRawUpdate(this.project_id, update) - await ProjectHistoryClient.promises.flushProject(this.project_id) + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) + await ProjectHistoryClient.flushProject(this.project_id) }) }) diff --git a/services/project-history/test/acceptance/js/FileTreeDiffTests.js b/services/project-history/test/acceptance/js/FileTreeDiffTests.js index 13591de8c4..122f8bb73a 100644 --- a/services/project-history/test/acceptance/js/FileTreeDiffTests.js +++ b/services/project-history/test/acceptance/js/FileTreeDiffTests.js @@ -13,7 +13,7 @@ const sha = data => crypto.createHash('sha1').update(data).digest('hex') describe('FileTree Diffs', function () { beforeEach(async function () { - await ProjectHistoryApp.promises.ensureRunning() + await ProjectHistoryApp.ensureRunning() this.historyId = new ObjectId().toString() this.projectId = new ObjectId().toString() @@ -28,7 +28,7 @@ describe('FileTree Diffs', function () { overleaf: { history: { id: this.historyId } }, }) - await ProjectHistoryClient.promises.initializeProject(this.historyId) + await ProjectHistoryClient.initializeProject(this.historyId) }) afterEach(function () { diff --git a/services/project-history/test/acceptance/js/FlushManagerTests.js b/services/project-history/test/acceptance/js/FlushManagerTests.js index 175ac8613b..c866e30b1f 100644 --- a/services/project-history/test/acceptance/js/FlushManagerTests.js +++ b/services/project-history/test/acceptance/js/FlushManagerTests.js @@ -17,7 +17,7 @@ describe('Flushing old queues', function () { beforeEach(async function () { this.timestamp = new Date() - await ProjectHistoryApp.promises.ensureRunning() + await ProjectHistoryApp.ensureRunning() this.projectId = new ObjectId().toString() this.docId = new ObjectId().toString() this.fileId = new ObjectId().toString() @@ -45,7 +45,7 @@ describe('Flushing old queues', function () { }, }, }) - await ProjectHistoryClient.promises.initializeProject(historyId) + await ProjectHistoryClient.initializeProject(historyId) }) afterEach(function () { @@ -68,11 +68,8 @@ describe('Flushing old queues', function () { doc: this.docId, meta: { user_id: this.user_id, ts: new Date() }, } - await ProjectHistoryClient.promises.pushRawUpdate( - this.projectId, - update - ) - await ProjectHistoryClient.promises.setFirstOpTimestamp( + await ProjectHistoryClient.pushRawUpdate(this.projectId, update) + await ProjectHistoryClient.setFirstOpTimestamp( this.projectId, Date.now() - 24 * 3600 * 1000 ) @@ -131,11 +128,8 @@ describe('Flushing old queues', function () { doc: this.docId, meta: { user_id: this.user_id, ts: new Date() }, } - await ProjectHistoryClient.promises.pushRawUpdate( - this.projectId, - update - ) - await ProjectHistoryClient.promises.setFirstOpTimestamp( + await ProjectHistoryClient.pushRawUpdate(this.projectId, update) + await ProjectHistoryClient.setFirstOpTimestamp( this.projectId, Date.now() - 60 * 1000 ) @@ -177,11 +171,8 @@ describe('Flushing old queues', function () { doc: this.docId, meta: { user_id: this.user_id, ts: new Date() }, } - await ProjectHistoryClient.promises.pushRawUpdate( - this.projectId, - update - ) - await ProjectHistoryClient.promises.setFirstOpTimestamp( + await ProjectHistoryClient.pushRawUpdate(this.projectId, update) + await ProjectHistoryClient.setFirstOpTimestamp( this.projectId, Date.now() - 60 * 1000 ) @@ -241,16 +232,8 @@ describe('Flushing old queues', function () { meta: { user_id: this.user_id, ts: new Date() }, } this.startDate = Date.now() - await ProjectHistoryClient.promises.pushRawUpdate( - this.projectId, - update - ) - await new Promise((resolve, reject) => { - ProjectHistoryClient.clearFirstOpTimestamp(this.projectId, err => { - if (err) reject(err) - else resolve() - }) - }) + await ProjectHistoryClient.pushRawUpdate(this.projectId, update) + await ProjectHistoryClient.clearFirstOpTimestamp(this.projectId) }) it('flushes the project history queue anyway', async function () { @@ -266,15 +249,9 @@ describe('Flushing old queues', function () { 'made calls to history service to store updates' ) - const result = await new Promise((resolve, reject) => { - ProjectHistoryClient.getFirstOpTimestamp( - this.projectId, - (err, result) => { - if (err) reject(err) - else resolve(result) - } - ) - }) + const result = await ProjectHistoryClient.getFirstOpTimestamp( + this.projectId + ) expect(result).to.be.null }) }) diff --git a/services/project-history/test/acceptance/js/GetChangesInChunkSince.js b/services/project-history/test/acceptance/js/GetChangesInChunkSince.js index 94e3fd29ef..249d0d082a 100644 --- a/services/project-history/test/acceptance/js/GetChangesInChunkSince.js +++ b/services/project-history/test/acceptance/js/GetChangesInChunkSince.js @@ -19,14 +19,13 @@ describe('GetChangesInChunkSince', function () { beforeEach(async function () { projectId = new ObjectId().toString() historyId = new ObjectId().toString() - await ProjectHistoryApp.promises.ensureRunning() + await ProjectHistoryApp.ensureRunning() MockHistoryStore().post('/api/projects').reply(200, { projectId: historyId, }) - const olProject = - await ProjectHistoryClient.promises.initializeProject(historyId) + const olProject = await ProjectHistoryClient.initializeProject(historyId) MockWeb() .get(`/project/${projectId}/details`) .reply(200, { diff --git a/services/project-history/test/acceptance/js/HealthCheckTests.js b/services/project-history/test/acceptance/js/HealthCheckTests.js index c70b893de5..b99819f06c 100644 --- a/services/project-history/test/acceptance/js/HealthCheckTests.js +++ b/services/project-history/test/acceptance/js/HealthCheckTests.js @@ -16,7 +16,7 @@ describe('Health Check', function () { const historyId = new ObjectId().toString() settings.history.healthCheck = { project_id: projectId } - await ProjectHistoryApp.promises.ensureRunning() + await ProjectHistoryApp.ensureRunning() MockHistoryStore().post('/api/projects').reply(200, { projectId: historyId, @@ -43,7 +43,7 @@ describe('Health Check', function () { }, }) - await ProjectHistoryClient.promises.initializeProject(historyId) + await ProjectHistoryClient.initializeProject(historyId) }) it('should respond to the health check', async function () { diff --git a/services/project-history/test/acceptance/js/LabelsTests.js b/services/project-history/test/acceptance/js/LabelsTests.js index b44a74fdb7..7e65826fc7 100644 --- a/services/project-history/test/acceptance/js/LabelsTests.js +++ b/services/project-history/test/acceptance/js/LabelsTests.js @@ -12,14 +12,14 @@ const fixture = path => new URL(`../fixtures/${path}`, import.meta.url) describe('Labels', function () { beforeEach(async function () { - await ProjectHistoryApp.promises.ensureRunning() + await ProjectHistoryApp.ensureRunning() this.historyId = new ObjectId().toString() MockHistoryStore().post('/api/projects').reply(200, { projectId: this.historyId, }) - const olProject = await ProjectHistoryClient.promises.initializeProject( + const olProject = await ProjectHistoryClient.initializeProject( this.historyId ) this.project_id = new ObjectId().toString() diff --git a/services/project-history/test/acceptance/js/LatestSnapshotTests.js b/services/project-history/test/acceptance/js/LatestSnapshotTests.js index 727f3bf296..5b3d34af12 100644 --- a/services/project-history/test/acceptance/js/LatestSnapshotTests.js +++ b/services/project-history/test/acceptance/js/LatestSnapshotTests.js @@ -13,14 +13,14 @@ const fixture = path => new URL(`../fixtures/${path}`, import.meta.url) describe('LatestSnapshot', function () { beforeEach(async function () { - await ProjectHistoryApp.promises.ensureRunning() + await ProjectHistoryApp.ensureRunning() this.historyId = new ObjectId().toString() MockHistoryStore().post('/api/projects').reply(200, { projectId: this.historyId, }) - const v1Project = await ProjectHistoryClient.promises.initializeProject( + const v1Project = await ProjectHistoryClient.initializeProject( this.historyId ) this.projectId = new ObjectId().toString() diff --git a/services/project-history/test/acceptance/js/ReadingASnapshotTests.js b/services/project-history/test/acceptance/js/ReadingASnapshotTests.js index 33992471a8..f166b8112d 100644 --- a/services/project-history/test/acceptance/js/ReadingASnapshotTests.js +++ b/services/project-history/test/acceptance/js/ReadingASnapshotTests.js @@ -12,14 +12,14 @@ const fixture = path => new URL(`../fixtures/${path}`, import.meta.url) describe('ReadSnapshot', function () { beforeEach(async function () { - await ProjectHistoryApp.promises.ensureRunning() + await ProjectHistoryApp.ensureRunning() this.historyId = new ObjectId().toString() MockHistoryStore().post('/api/projects').reply(200, { projectId: this.historyId, }) - const v1Project = await ProjectHistoryClient.promises.initializeProject( + const v1Project = await ProjectHistoryClient.initializeProject( this.historyId ) this.projectId = new ObjectId().toString() diff --git a/services/project-history/test/acceptance/js/RetryTests.js b/services/project-history/test/acceptance/js/RetryTests.js index 62efbbaa21..5d788a1f19 100644 --- a/services/project-history/test/acceptance/js/RetryTests.js +++ b/services/project-history/test/acceptance/js/RetryTests.js @@ -18,7 +18,7 @@ describe('Retrying failed projects', function () { beforeEach(async function () { this.timestamp = new Date() - await ProjectHistoryApp.promises.ensureRunning() + await ProjectHistoryApp.ensureRunning() this.project_id = new ObjectId().toString() this.doc_id = new ObjectId().toString() @@ -47,7 +47,7 @@ describe('Retrying failed projects', function () { }, }, }) - await ProjectHistoryClient.promises.initializeProject(historyId) + await ProjectHistoryClient.initializeProject(historyId) }) afterEach(function () { @@ -71,10 +71,7 @@ describe('Retrying failed projects', function () { meta: { user_id: this.user_id, ts: new Date() }, } - await ProjectHistoryClient.promises.pushRawUpdate( - this.project_id, - update - ) + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) await ProjectHistoryClient.setFailure({ project_id: this.project_id, attempts: 1, diff --git a/services/project-history/test/acceptance/js/SendingUpdatesTests.js b/services/project-history/test/acceptance/js/SendingUpdatesTests.js index dce5474cf0..fb88d779f5 100644 --- a/services/project-history/test/acceptance/js/SendingUpdatesTests.js +++ b/services/project-history/test/acceptance/js/SendingUpdatesTests.js @@ -1,7 +1,6 @@ import { expect } from 'chai' import Settings from '@overleaf/settings' import assert from 'node:assert' -import async from 'async' import crypto from 'node:crypto' import mongodb from 'mongodb-legacy' import nock from 'nock' @@ -202,38 +201,35 @@ function olAddFileUpdate(file, userId, ts, fileHash) { describe('Sending Updates', function () { const historyId = new ObjectId().toString() - beforeEach(function (done) { + beforeEach(async function () { this.timestamp = new Date() - ProjectHistoryApp.ensureRunning(error => { - if (error) { - return done(error) - } - this.userId = new ObjectId().toString() - this.projectId = new ObjectId().toString() - this.docId = new ObjectId().toString() + await ProjectHistoryApp.ensureRunning() - this.doc = { - id: this.docId, - pathname: '/main.tex', - length: 5, - } + this.userId = new ObjectId().toString() + this.projectId = new ObjectId().toString() + this.docId = new ObjectId().toString() - MockHistoryStore().post('/api/projects').reply(200, { - projectId: historyId, - }) - MockWeb() - .get(`/project/${this.projectId}/details`) - .reply(200, { - name: 'Test Project', - overleaf: { - history: { - id: historyId, - }, - }, - }) - ProjectHistoryClient.initializeProject(historyId, done) + this.doc = { + id: this.docId, + pathname: '/main.tex', + length: 5, + } + + MockHistoryStore().post('/api/projects').reply(200, { + projectId: historyId, }) + MockWeb() + .get(`/project/${this.projectId}/details`) + .reply(200, { + name: 'Test Project', + overleaf: { + history: { + id: historyId, + }, + }, + }) + await ProjectHistoryClient.initializeProject(historyId) }) afterEach(function () { @@ -255,7 +251,7 @@ describe('Sending Updates', function () { }) }) - it('should send add doc updates to the history store', function (done) { + it('should send add doc updates to the history store', async function () { const fileHash = '0a207c060e61f3b88eaee0a8cd0696f46fb155eb' const createBlob = MockHistoryStore() @@ -272,43 +268,24 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddDocUpdate( - historyId, - this.doc, - this.userId, - this.timestamp, - 'a\nb' - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createBlob.isDone(), - '/api/projects/:historyId/blobs/:hash should have been called' - ) - assert( - addFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddDocUpdate(historyId, this.doc, this.userId, this.timestamp, 'a\nb') + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createBlob.isDone(), + '/api/projects/:historyId/blobs/:hash should have been called' + ) + assert( + addFile.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should send ranges to the history store', function (done) { + it('should send ranges to the history store', async function () { const fileHash = '49e886093b3eacbc12b99a1eb5aeaa44a6b9d90e' const rangesHash = 'fa9a429ff518bc9e5b2507a96ff0646b566eca65' @@ -356,68 +333,55 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddDocUpdate( - historyId, - this.doc, - this.userId, - this.timestamp, - 'foo barbaz', - { - changes: [ - { - op: { p: 4, d: 'bar' }, - metadata: { - ts: 1704067200000, - user_id: 'user-id-1', - }, - }, - ], - comments: [ - { - op: { - p: 0, - c: 'foo', - t: 'comment-id-1', - }, - metadata: { resolved: false }, - }, - ], - } - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddDocUpdate( + historyId, + this.doc, + this.userId, + this.timestamp, + 'foo barbaz', + { + changes: [ + { + op: { p: 4, d: 'bar' }, + metadata: { + ts: 1704067200000, + user_id: 'user-id-1', + }, + }, + ], + comments: [ + { + op: { + p: 0, + c: 'foo', + t: 'comment-id-1', + }, + metadata: { resolved: false }, + }, + ], } - assert( - createBlob.isDone(), - '/api/projects/:historyId/blobs/:hash should have been called to create content blob' - ) - assert( - createRangesBlob.isDone(), - '/api/projects/:historyId/blobs/:hash should have been called to create ranges blob' - ) - assert( - addFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + ) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createBlob.isDone(), + '/api/projects/:historyId/blobs/:hash should have been called to create content blob' + ) + assert( + createRangesBlob.isDone(), + '/api/projects/:historyId/blobs/:hash should have been called to create ranges blob' + ) + assert( + addFile.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should strip non-BMP characters in add doc updates before sending to the history store', function (done) { + it('should strip non-BMP characters in add doc updates before sending to the history store', async function () { const fileHash = '11509fe05a41f9cdc51ea081342b5a4fc7c8d0fc' const createBlob = MockHistoryStore() @@ -437,43 +401,30 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddDocUpdate( - historyId, - this.doc, - this.userId, - this.timestamp, - 'a\nb\uD800\uDC00c' - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createBlob.isDone(), - '/api/projects/:historyId/blobs/:hash should have been called' - ) - assert( - addFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddDocUpdate( + historyId, + this.doc, + this.userId, + this.timestamp, + 'a\nb\uD800\uDC00c' + ) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createBlob.isDone(), + '/api/projects/:historyId/blobs/:hash should have been called' + ) + assert( + addFile.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should send text updates to the history store', function (done) { + it('should send text updates to the history store', async function () { const createChange = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -484,40 +435,22 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 1, - this.timestamp, - [{ p: 3, i: '\nc' }] - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 1, this.timestamp, [ + { p: 3, i: '\nc' }, + ]) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should send renames to the history store', function (done) { + it('should send renames to the history store', async function () { const createChange = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -534,40 +467,27 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slRenameUpdate( - historyId, - this.doc, - this.userId, - this.timestamp, - '/main.tex', - '/main2.tex' - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slRenameUpdate( + historyId, + this.doc, + this.userId, + this.timestamp, + '/main.tex', + '/main2.tex' + ) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should not get file from filestore if no url provided', function (done) { + it('should not get file from filestore if no url provided', async function () { const file = { id: new ObjectId().toString(), pathname: '/test.png', @@ -593,48 +513,35 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - createdBlobFileUpdate( - historyId, - file, - this.userId, - this.timestamp, - this.projectId - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - !fileStoreRequest.isDone(), - 'filestore should not have been called' - ) + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + createdBlobFileUpdate( + historyId, + file, + this.userId, + this.timestamp, + this.projectId + ) + ) - assert( - checkBlob.isDone(), - `HEAD /api/projects/${historyId}/blobs/${file.hash} should have been called` - ) - assert( - addFile.isDone(), - `/api/projects/${historyId}/latest/files should have been called` - ) - done() - } + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + !fileStoreRequest.isDone(), + 'filestore should not have been called' + ) + + assert( + checkBlob.isDone(), + `HEAD /api/projects/${historyId}/blobs/${file.hash} should have been called` + ) + assert( + addFile.isDone(), + `/api/projects/${historyId}/latest/files should have been called` ) }) - it('should send add file updates to the history store', function (done) { + it('should send add file updates to the history store', async function () { const file = { id: new ObjectId().toString(), pathname: '/test.png', @@ -660,47 +567,34 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddFileUpdate( - historyId, - file, - this.userId, - this.timestamp, - this.projectId - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - fileStoreRequest.isDone(), - `/project/${this.projectId}/file/${file.id} should have been called` - ) - assert( - createBlob.isDone(), - `/api/projects/${historyId}/latest/files should have been called` - ) - assert( - addFile.isDone(), - `/api/projects/${historyId}/latest/files should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddFileUpdate( + historyId, + file, + this.userId, + this.timestamp, + this.projectId + ) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + fileStoreRequest.isDone(), + `/project/${this.projectId}/file/${file.id} should have been called` + ) + assert( + createBlob.isDone(), + `/api/projects/${historyId}/latest/files should have been called` + ) + assert( + addFile.isDone(), + `/api/projects/${historyId}/latest/files should have been called` ) }) - it('should send a stub to the history store when the file is large', function (done) { + it('should send a stub to the history store when the file is large', async function () { const fileContents = Buffer.alloc(Settings.maxFileSizeInBytes + 1, 'X') const fileSize = Buffer.byteLength(fileContents) @@ -757,47 +651,34 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddFileUpdate( - historyId, - file, - this.userId, - this.timestamp, - this.projectId - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - addFile.isDone(), - `/api/projects/${historyId}/latest/files should have been called` - ) - assert( - createBlob.isDone(), - `/api/projects/${historyId}/latest/files should have been called` - ) - assert( - fileStoreRequest.isDone(), - `/project/${this.projectId}/file/${file.id} should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddFileUpdate( + historyId, + file, + this.userId, + this.timestamp, + this.projectId + ) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + addFile.isDone(), + `/api/projects/${historyId}/latest/files should have been called` + ) + assert( + createBlob.isDone(), + `/api/projects/${historyId}/latest/files should have been called` + ) + assert( + fileStoreRequest.isDone(), + `/project/${this.projectId}/file/${file.id} should have been called` ) }) - it('should handle comment ops', function (done) { + it('should handle comment ops', async function () { const createChange = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -818,57 +699,30 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 1, - this.timestamp, - [ - { p: 3, i: '\nc' }, - { p: 3, c: '\nc', t: 'comment-id-1' }, - ] - ), - cb - ) - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 2, - this.timestamp, - [{ p: 2, c: 'b', t: 'comment-id-2' }] - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 1, this.timestamp, [ + { p: 3, i: '\nc' }, + { p: 3, c: '\nc', t: 'comment-id-1' }, + ]) + ) + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 2, this.timestamp, [ + { p: 2, c: 'b', t: 'comment-id-2' }, + ]) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should be able to process lots of updates in batches', function (done) { + it('should be able to process lots of updates in batches', async function () { const BATCH_SIZE = 500 const createFirstChangeBatch = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { @@ -923,40 +777,26 @@ describe('Sending Updates', function () { }, }) - const pushChange = (n, cb) => { + // Push updates in a loop instead of using async.times + for (let n = 0; n < BATCH_SIZE + 50; n++) { this.doc.length += 1 - ProjectHistoryClient.pushRawUpdate( + await ProjectHistoryClient.pushRawUpdate( this.projectId, slTextUpdate(historyId, this.doc, this.userId, n, this.timestamp, [ { p: 0, i: 'a' }, - ]), - cb + ]) ) } - async.series( - [ - cb => { - async.times(BATCH_SIZE + 50, pushChange, cb) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createFirstChangeBatch.isDone(), - `/api/projects/${historyId}/changes should have been called for the first batch` - ) - assert( - createSecondChangeBatch.isDone(), - `/api/projects/${historyId}/changes should have been called for the second batch` - ) - done() - } + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createFirstChangeBatch.isDone(), + `/api/projects/${historyId}/changes should have been called for the first batch` + ) + assert( + createSecondChangeBatch.isDone(), + `/api/projects/${historyId}/changes should have been called for the second batch` ) }) }) @@ -976,7 +816,7 @@ describe('Sending Updates', function () { }) }) - it('should concat adjacent text updates', function (done) { + it('should concat adjacent text updates', async function () { const createChange = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -993,58 +833,31 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 1, - this.timestamp, - [ - { p: 3, i: 'foobar' }, - { p: 6, d: 'bar' }, - ] - ), - cb - ) - this.doc.length += 3 - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 2, - this.timestamp, - [{ p: 6, i: 'baz' }] - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 1, this.timestamp, [ + { p: 3, i: 'foobar' }, + { p: 6, d: 'bar' }, + ]) + ) + this.doc.length += 3 + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 2, this.timestamp, [ + { p: 6, i: 'baz' }, + ]) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should take the timestamp of the first update', function (done) { + it('should take the timestamp of the first update', async function () { const timestamp1 = new Date(this.timestamp) const timestamp2 = new Date(this.timestamp.getTime() + 10000) const createChange = MockHistoryStore() @@ -1063,45 +876,30 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate(historyId, this.doc, this.userId, 1, timestamp1, [ - { p: 3, i: 'foo' }, - ]), - cb - ) - this.doc.length += 3 - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate(historyId, this.doc, this.userId, 2, timestamp2, [ - { p: 6, i: 'baz' }, - ]), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 1, timestamp1, [ + { p: 3, i: 'foo' }, + ]) + ) + this.doc.length += 3 + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 2, timestamp2, [ + { p: 6, i: 'baz' }, + ]) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should not concat updates more than 60 seconds apart', function (done) { + it('should not concat updates more than 60 seconds apart', async function () { const timestamp1 = new Date(this.timestamp) const timestamp2 = new Date(this.timestamp.getTime() + 120000) const createChange = MockHistoryStore() @@ -1115,45 +913,30 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate(historyId, this.doc, this.userId, 1, timestamp1, [ - { p: 3, i: 'foo' }, - ]), - cb - ) - this.doc.length += 3 - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate(historyId, this.doc, this.userId, 2, timestamp2, [ - { p: 6, i: 'baz' }, - ]), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 1, timestamp1, [ + { p: 3, i: 'foo' }, + ]) + ) + this.doc.length += 3 + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 2, timestamp2, [ + { p: 6, i: 'baz' }, + ]) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should not concat updates with different user_ids', function (done) { + it('should not concat updates with different user_ids', async function () { const userId1 = new ObjectId().toString() const userId2 = new ObjectId().toString() @@ -1168,45 +951,30 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate(historyId, this.doc, userId1, 1, this.timestamp, [ - { p: 3, i: 'foo' }, - ]), - cb - ) - this.doc.length += 3 - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate(historyId, this.doc, userId2, 2, this.timestamp, [ - { p: 6, i: 'baz' }, - ]), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, userId1, 1, this.timestamp, [ + { p: 3, i: 'foo' }, + ]) + ) + this.doc.length += 3 + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, userId2, 2, this.timestamp, [ + { p: 6, i: 'baz' }, + ]) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should not concat updates with different docs', function (done) { + it('should not concat updates with different docs', async function () { const doc1 = { id: new ObjectId().toString(), pathname: '/doc1.tex', @@ -1229,44 +997,29 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate(historyId, doc1, this.userId, 1, this.timestamp, [ - { p: 3, i: 'foo' }, - ]), - cb - ) - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate(historyId, doc2, this.userId, 2, this.timestamp, [ - { p: 6, i: 'baz' }, - ]), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, doc1, this.userId, 1, this.timestamp, [ + { p: 3, i: 'foo' }, + ]) + ) + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, doc2, this.userId, 2, this.timestamp, [ + { p: 6, i: 'baz' }, + ]) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should not send updates without any ops', function (done) { + it('should not send updates without any ops', async function () { const createChange = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([]) @@ -1275,41 +1028,21 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - // These blank ops can get sent by doc-updater on setDocs from Dropbox that don't change anything - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 1, - this.timestamp, - [] - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - !createChange.isDone(), - `/api/projects/${historyId}/changes should not have been called` - ) - done() - } + // These blank ops can get sent by doc-updater on setDocs from Dropbox that don't change anything + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 1, this.timestamp, []) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + !createChange.isDone(), + `/api/projects/${historyId}/changes should not have been called` ) }) - it('should not send ops that compress to nothing', function (done) { + it('should not send ops that compress to nothing', async function () { const createChange = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([]) @@ -1318,55 +1051,30 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 1, - this.timestamp, - [{ i: 'foo', p: 3 }] - ), - cb - ) - this.doc.length += 3 - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 2, - this.timestamp, - [{ d: 'foo', p: 3 }] - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - !createChange.isDone(), - `/api/projects/${historyId}/changes should not have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 1, this.timestamp, [ + { i: 'foo', p: 3 }, + ]) + ) + this.doc.length += 3 + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 2, this.timestamp, [ + { d: 'foo', p: 3 }, + ]) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + !createChange.isDone(), + `/api/projects/${historyId}/changes should not have been called` ) }) - it('should not send ops from a diff that are blank', function (done) { + it('should not send ops from a diff that are blank', async function () { this.doc.length = 300 // Test case taken from a real life document where it was generating blank insert and // delete ops from a diff, and the blank delete was erroring on the OL history from @@ -1394,49 +1102,29 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 1, - this.timestamp, - [ - { - p: 73, - d: '\\begin{table}[h]\n\\centering\n\\caption{My caption}\n\\label{my-label}\n\\begin{tabular}{lll}\n & A & B \\\\\nLiter t up & 2 & 1 \\\\\nLiter Whiskey & 1 & 2 \\\\\nPris pr. liter & 200 & 250\n\\end{tabular}\n\\end{table}', - }, - { - p: 73, - i: '\\begin{table}[]\n\\centering\n\\caption{My caption}\n\\label{my-label}\n\\begin{tabular}{|l|ll|}\n\\hline\n & A & B \\\\ \\hline\nLiter t up & 2 & 1 \\\\\nLiter Whiskey & 1 & 2 \\\\\nPris pr. liter & 200 & 250 \\\\ \\hline\n\\end{tabular}\n\\end{table}', - }, - ] - ), - cb - ) + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 1, this.timestamp, [ + { + p: 73, + d: '\\begin{table}[h]\n\\centering\n\\caption{My caption}\n\\label{my-label}\n\\begin{tabular}{lll}\n & A & B \\\\\nLiter t up & 2 & 1 \\\\\nLiter Whiskey & 1 & 2 \\\\\nPris pr. liter & 200 & 250\n\\end{tabular}\n\\end{table}', }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) + { + p: 73, + i: '\\begin{table}[]\n\\centering\n\\caption{My caption}\n\\label{my-label}\n\\begin{tabular}{|l|ll|}\n\\hline\n & A & B \\\\ \\hline\nLiter t up & 2 & 1 \\\\\nLiter Whiskey & 1 & 2 \\\\\nPris pr. liter & 200 & 250 \\\\ \\hline\n\\end{tabular}\n\\end{table}', }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + ]) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should not concat text updates across project structure ops', function (done) { + it('should not concat text updates across project structure ops', async function () { const newDoc = { id: new ObjectId().toString(), pathname: '/main.tex', @@ -1472,71 +1160,42 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 1, - this.timestamp, - [ - { p: 3, i: 'foobar' }, - { p: 6, d: 'bar' }, - ] - ), - cb - ) - this.doc.length += 3 - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddDocUpdate( - historyId, - newDoc, - this.userId, - this.timestamp, - newDoc.docLines - ), - cb - ) - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 2, - this.timestamp, - [{ p: 6, i: 'baz' }] - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 1, this.timestamp, [ + { p: 3, i: 'foobar' }, + { p: 6, d: 'bar' }, + ]) + ) + this.doc.length += 3 + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddDocUpdate( + historyId, + newDoc, + this.userId, + this.timestamp, + newDoc.docLines + ) + ) + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 2, this.timestamp, [ + { p: 6, i: 'baz' }, + ]) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should track the doc length when splitting ops', function (done) { + it('should track the doc length when splitting ops', async function () { this.doc.length = 10 const createChange = MockHistoryStore() @@ -1556,53 +1215,26 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 1, - this.timestamp, - [ - { p: 3, d: 'foo' }, - { p: 3, i: 'bar' }, // Make sure the length of the op generated from this is 7, not 10 - ] - ), - cb - ) - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 2, - this.timestamp, - [{ p: 6, i: 'baz' }] - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 1, this.timestamp, [ + { p: 3, d: 'foo' }, + { p: 3, i: 'bar' }, // Make sure the length of the op generated from this is 7, not 10 + ]) + ) + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 2, this.timestamp, [ + { p: 6, i: 'baz' }, + ]) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) }) @@ -1622,7 +1254,7 @@ describe('Sending Updates', function () { }) }) - it('should replace \\ with _ and workaround * in pathnames', function (done) { + it('should replace \\\\ with _ and workaround * in pathnames', async function () { const doc = { id: this.doc.id, pathname: '\\main.tex', @@ -1671,78 +1303,59 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddDocUpdate( - historyId, - doc, - this.userId, - this.timestamp, - doc.docLines - ), - cb - ) - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slRenameUpdate( - historyId, - doc, - this.userId, - this.timestamp, - '/\\main.tex', - '/\\main2.tex' - ), - cb - ) - doc.pathname = '\\main2.tex' - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate(historyId, doc, this.userId, 2, this.timestamp, [ - { p: 3, i: 'foo' }, - ]), - cb - ) - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slRenameUpdate( - historyId, - doc, - this.userId, - this.timestamp, - '/\\main2.tex', - '/\\main*.tex' - ), - cb - ) - doc.pathname = '\\main*.tex' - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddDocUpdate( + historyId, + doc, + this.userId, + this.timestamp, + doc.docLines + ) + ) + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slRenameUpdate( + historyId, + doc, + this.userId, + this.timestamp, + '/\\main.tex', + '/\\main2.tex' + ) + ) + doc.pathname = '\\main2.tex' + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, doc, this.userId, 2, this.timestamp, [ + { p: 3, i: 'foo' }, + ]) + ) + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slRenameUpdate( + historyId, + doc, + this.userId, + this.timestamp, + '/\\main2.tex', + '/\\main*.tex' + ) + ) + doc.pathname = '\\main*.tex' + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should workaround pathnames beginning with spaces', function (done) { + it('should workaround pathnames beginning with spaces', async function () { const doc = { id: this.doc.id, pathname: 'main.tex', @@ -1777,50 +1390,35 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddDocUpdate( - historyId, - doc, - this.userId, - this.timestamp, - doc.docLines - ), - cb - ) - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slRenameUpdate( - historyId, - doc, - this.userId, - this.timestamp, - '/main.tex', - '/foo/ main.tex' - ), - cb - ) - doc.pathname = '/foo/ main.tex' - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddDocUpdate( + historyId, + doc, + this.userId, + this.timestamp, + doc.docLines + ) + ) + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slRenameUpdate( + historyId, + doc, + this.userId, + this.timestamp, + '/main.tex', + '/foo/ main.tex' + ) + ) + doc.pathname = '/foo/ main.tex' + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) }) @@ -1840,7 +1438,7 @@ describe('Sending Updates', function () { }) }) - it('should return a 500 if the filestore returns a 500', function (done) { + it('should return a 500 if the filestore returns a 500', async function () { const file = { id: new ObjectId().toString(), pathname: '/test.png', @@ -1866,57 +1464,37 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddFileUpdate( - historyId, - file, - this.userId, - this.timestamp, - this.projectId - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject( - this.projectId, - { allowErrors: true }, - (error, res) => { - if (error) { - return cb(error) - } - expect(res.statusCode).to.equal(500) - cb() - } - ) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - fileStoreRequest.isDone(), - `/project/${this.projectId}/file/${file.id} should have been called` - ) - assert( - !createBlob.isDone(), - `/api/projects/${historyId}/latest/files should not have been called` - ) - assert( - !addFile.isDone(), - `/api/projects/${historyId}/latest/files should not have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddFileUpdate( + historyId, + file, + this.userId, + this.timestamp, + this.projectId + ) + ) + + const res = await ProjectHistoryClient.flushProject(this.projectId, { + allowErrors: true, + }) + expect(res.statusCode).to.equal(500) + + assert( + fileStoreRequest.isDone(), + `/project/${this.projectId}/file/${file.id} should have been called` + ) + assert( + !createBlob.isDone(), + `/api/projects/${historyId}/latest/files should not have been called` + ) + assert( + !addFile.isDone(), + `/api/projects/${historyId}/latest/files should not have been called` ) }) - it('should return a 500 if the filestore request errors', function (done) { + it('should return a 500 if the filestore request errors', async function () { const file = { id: new ObjectId().toString(), pathname: '/test.png', @@ -1942,53 +1520,33 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddFileUpdate( - historyId, - file, - this.userId, - this.timestamp, - this.projectId - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject( - this.projectId, - { allowErrors: true }, - (error, res) => { - if (error) { - return cb(error) - } - expect(res.statusCode).to.equal(500) - cb() - } - ) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - fileStoreRequest.isDone(), - `/project/${this.projectId}/file/${file.id} should have been called` - ) - assert( - !createBlob.isDone(), - `/api/projects/${historyId}/latest/files should not have been called` - ) - assert( - !addFile.isDone(), - `/api/projects/${historyId}/latest/files should not have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddFileUpdate( + historyId, + file, + this.userId, + this.timestamp, + this.projectId + ) + ) + + const res = await ProjectHistoryClient.flushProject(this.projectId, { + allowErrors: true, + }) + expect(res.statusCode).to.equal(500) + + assert( + fileStoreRequest.isDone(), + `/project/${this.projectId}/file/${file.id} should have been called` + ) + assert( + !createBlob.isDone(), + `/api/projects/${historyId}/latest/files should not have been called` + ) + assert( + !addFile.isDone(), + `/api/projects/${historyId}/latest/files should not have been called` ) }) }) @@ -2008,7 +1566,7 @@ describe('Sending Updates', function () { }) }) - it('should discard project structure updates which have already been applied', function (done) { + it('should discard project structure updates which have already been applied', async function () { const newDoc = [] for (let i = 0; i <= 2; i++) { newDoc[i] = { @@ -2047,64 +1605,47 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddDocUpdateWithVersion( - historyId, - newDoc[0], - this.userId, - this.timestamp, - newDoc[0].docLines, - '100.0' - ), - cb - ) - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddDocUpdateWithVersion( - historyId, - newDoc[1], - this.userId, - this.timestamp, - newDoc[1].docLines, - '101.0' - ), - cb - ) - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slAddDocUpdateWithVersion( - historyId, - newDoc[2], - this.userId, - this.timestamp, - newDoc[2].docLines, - '102.0' - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddDocUpdateWithVersion( + historyId, + newDoc[0], + this.userId, + this.timestamp, + newDoc[0].docLines, + '100.0' + ) + ) + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddDocUpdateWithVersion( + historyId, + newDoc[1], + this.userId, + this.timestamp, + newDoc[1].docLines, + '101.0' + ) + ) + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slAddDocUpdateWithVersion( + historyId, + newDoc[2], + this.userId, + this.timestamp, + newDoc[2].docLines, + '102.0' + ) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) }) @@ -2124,7 +1665,7 @@ describe('Sending Updates', function () { }) }) - it('should discard doc updates which have already been applied', function (done) { + it('should discard doc updates which have already been applied', async function () { const createChange = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -2141,56 +1682,27 @@ describe('Sending Updates', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 100, - this.timestamp, - [ - { p: 3, i: 'foobar' }, // these ops should be skipped - { p: 6, d: 'bar' }, - ] - ), - cb - ) - this.doc.length += 3 - }, - cb => { - ProjectHistoryClient.pushRawUpdate( - this.projectId, - slTextUpdate( - historyId, - this.doc, - this.userId, - 101, - this.timestamp, - [ - { p: 6, i: 'baz' }, // this op should be applied - ] - ), - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject(this.projectId, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 100, this.timestamp, [ + { p: 3, i: 'foobar' }, // these ops should be skipped + { p: 6, d: 'bar' }, + ]) + ) + this.doc.length += 3 + + await ProjectHistoryClient.pushRawUpdate( + this.projectId, + slTextUpdate(historyId, this.doc, this.userId, 101, this.timestamp, [ + { p: 6, i: 'baz' }, // this op should be applied + ]) + ) + + await ProjectHistoryClient.flushProject(this.projectId) + + assert( + createChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) }) diff --git a/services/project-history/test/acceptance/js/SummarisedUpdatesTests.js b/services/project-history/test/acceptance/js/SummarisedUpdatesTests.js index e9e87948a7..ec931fbae1 100644 --- a/services/project-history/test/acceptance/js/SummarisedUpdatesTests.js +++ b/services/project-history/test/acceptance/js/SummarisedUpdatesTests.js @@ -15,13 +15,13 @@ describe('Summarized updates', function () { this.projectId = new ObjectId().toString() this.historyId = new ObjectId().toString() - await ProjectHistoryApp.promises.ensureRunning() + await ProjectHistoryApp.ensureRunning() MockHistoryStore().post('/api/projects').reply(200, { projectId: this.historyId, }) - const olProject = await ProjectHistoryClient.promises.initializeProject( + const olProject = await ProjectHistoryClient.initializeProject( this.historyId ) diff --git a/services/project-history/test/acceptance/js/SyncTests.js b/services/project-history/test/acceptance/js/SyncTests.js index f7420e6cdb..563ac6e7c4 100644 --- a/services/project-history/test/acceptance/js/SyncTests.js +++ b/services/project-history/test/acceptance/js/SyncTests.js @@ -1,7 +1,5 @@ -import async from 'async' import nock from 'nock' import { expect } from 'chai' -import request from 'request' import assert from 'node:assert' import mongodb from 'mongodb-legacy' import logger from '@overleaf/logger' @@ -10,6 +8,7 @@ import * as ProjectHistoryClient from './helpers/ProjectHistoryClient.js' import * as ProjectHistoryApp from './helpers/ProjectHistoryApp.js' import sinon from 'sinon' import { getFailure } from './helpers/ProjectHistoryClient.js' +import { fetchNothing, RequestFailedError } from '@overleaf/fetch-utils' const { ObjectId } = mongodb const EMPTY_FILE_HASH = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391' @@ -30,32 +29,28 @@ describe('Syncing with web and doc-updater', function () { loggerError.restore() }) - beforeEach(function (done) { + beforeEach(async function () { this.timestamp = new Date() - ProjectHistoryApp.ensureRunning(error => { - if (error) { - throw error - } - this.project_id = new ObjectId().toString() - this.doc_id = new ObjectId().toString() - this.file_id = new ObjectId().toString() + await ProjectHistoryApp.ensureRunning() + this.project_id = new ObjectId().toString() + this.doc_id = new ObjectId().toString() + this.file_id = new ObjectId().toString() - MockHistoryStore().post('/api/projects').reply(200, { - projectId: historyId, - }) - MockWeb() - .get(`/project/${this.project_id}/details`) - .reply(200, { - name: 'Test Project', - overleaf: { - history: { - id: historyId, - }, - }, - }) - ProjectHistoryClient.initializeProject(historyId, done) + MockHistoryStore().post('/api/projects').reply(200, { + projectId: historyId, }) + MockWeb() + .get(`/project/${this.project_id}/details`) + .reply(200, { + name: 'Test Project', + overleaf: { + history: { + id: historyId, + }, + }, + }) + await ProjectHistoryClient.initializeProject(historyId) }) afterEach(function () { @@ -68,19 +63,21 @@ describe('Syncing with web and doc-updater', function () { MockWeb().post(`/project/${this.project_id}/history/resync`).reply(404) }) - it('404s if project-history is not enabled', function (done) { - request.post( - { - url: `http://127.0.0.1:3054/project/${this.project_id}/resync`, - }, - (error, res, body) => { - if (error) { - return done(error) + it('404s if project-history is not enabled', async function () { + try { + await fetchNothing( + `http://127.0.0.1:3054/project/${this.project_id}/resync`, + { + method: 'POST', } - expect(res.statusCode).to.equal(404) - done() + ) + } catch (error) { + if (error instanceof RequestFailedError) { + expect(error.response.status).to.equal(404) + } else { + throw error } - ) + } }) }) @@ -90,7 +87,7 @@ describe('Syncing with web and doc-updater', function () { }) describe('when a doc is missing', function () { - it('should send add doc updates to the history store', function (done) { + it('should send add doc updates to the history store', async function () { MockHistoryStore() .get(`/api/projects/${historyId}/latest/history`) .reply(200, { @@ -138,51 +135,38 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [ - { path: '/main.tex', doc: this.doc_id }, - { path: '/persistedDoc', doc: 'other-doc-id' }, - ], - files: [], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - createBlob.isDone(), - '/api/projects/:historyId/blobs/:hash should have been called' - ) - assert( - addFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [ + { path: '/main.tex', doc: this.doc_id }, + { path: '/persistedDoc', doc: 'other-doc-id' }, + ], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + createBlob.isDone(), + '/api/projects/:historyId/blobs/:hash should have been called' + ) + assert( + addFile.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) }) describe('when a file is missing', function () { - it('should send add file updates to the history store', function (done) { + it('should send add file updates to the history store', async function () { MockHistoryStore() .get(`/api/projects/${historyId}/latest/history`) .reply(200, { @@ -235,58 +219,45 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [], - files: [ - { - file: this.file_id, - path: '/test.png', - _hash: fileHash, - url: `http://127.0.0.1:3009/project/${this.project_id}/file/${this.file_id}`, - }, - { path: '/persistedFile' }, - ], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - throw error - } - assert(!loggerWarn.called, 'no warning logged on 404') - assert( - headBlob.isDone(), - 'HEAD /api/projects/:historyId/blobs/:hash should have been called' - ) - assert( - createBlob.isDone(), - '/api/projects/:historyId/blobs/:hash should have been called' - ) - assert( - addFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [], + files: [ + { + file: this.file_id, + path: '/test.png', + _hash: fileHash, + url: `http://127.0.0.1:3009/project/${this.project_id}/file/${this.file_id}`, + }, + { path: '/persistedFile' }, + ], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert(!loggerWarn.called, 'no warning logged on 404') + assert( + headBlob.isDone(), + 'HEAD /api/projects/:historyId/blobs/:hash should have been called' + ) + assert( + createBlob.isDone(), + '/api/projects/:historyId/blobs/:hash should have been called' + ) + assert( + addFile.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should skip HEAD on blob without hash', function (done) { + it('should skip HEAD on blob without hash', async function () { MockHistoryStore() .get(`/api/projects/${historyId}/latest/history`) .reply(200, { @@ -339,57 +310,44 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [], - files: [ - { - file: this.file_id, - path: '/test.png', - url: `http://127.0.0.1:3009/project/${this.project_id}/file/${this.file_id}`, - }, - { path: '/persistedFile' }, - ], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - throw error - } - assert(!loggerWarn.called, 'no warning logged on 404') - assert( - !headBlob.isDone(), - 'HEAD /api/projects/:historyId/blobs/:hash should have been skipped' - ) - assert( - createBlob.isDone(), - '/api/projects/:historyId/blobs/:hash should have been called' - ) - assert( - addFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [], + files: [ + { + file: this.file_id, + path: '/test.png', + url: `http://127.0.0.1:3009/project/${this.project_id}/file/${this.file_id}`, + }, + { path: '/persistedFile' }, + ], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert(!loggerWarn.called, 'no warning logged on 404') + assert( + !headBlob.isDone(), + 'HEAD /api/projects/:historyId/blobs/:hash should have been skipped' + ) + assert( + createBlob.isDone(), + '/api/projects/:historyId/blobs/:hash should have been called' + ) + assert( + addFile.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should record error when checking blob fails with 500', function (done) { + it('should record error when checking blob fails with 500', async function () { MockHistoryStore() .get(`/api/projects/${historyId}/latest/history`) .reply(200, { @@ -442,74 +400,54 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [], - files: [ - { - file: this.file_id, - path: '/test.png', - _hash: fileHash, - url: `http://127.0.0.1:3009/project/${this.project_id}/file/${this.file_id}`, - }, - { path: '/persistedFile' }, - ], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject( - this.project_id, - { - allowErrors: true, - }, - (err, res) => { - if (err) return cb(err) - assert(res.statusCode === 500, 'resync should have failed') - cb() - } - ) - }, - ], - error => { - if (error) { - throw error - } - assert( - loggerError.calledWithMatch( - sinon.match.any, - 'error checking whether blob exists' - ), - 'error logged on 500' - ) - assert( - headBlob.isDone(), - 'HEAD /api/projects/:historyId/blobs/:hash should have been called' - ) - assert( - !createBlob.isDone(), - '/api/projects/:historyId/blobs/:hash should have been skipped' - ) - assert( - !addFile.isDone(), - `/api/projects/${historyId}/changes should have been skipped` - ) - done() - } + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [], + files: [ + { + file: this.file_id, + path: '/test.png', + _hash: fileHash, + url: `http://127.0.0.1:3009/project/${this.project_id}/file/${this.file_id}`, + }, + { path: '/persistedFile' }, + ], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) + + const res = await ProjectHistoryClient.flushProject(this.project_id, { + allowErrors: true, + }) + assert(res.statusCode === 500, 'resync should have failed') + + assert( + loggerError.calledWithMatch( + sinon.match.any, + 'error checking whether blob exists' + ), + 'error logged on 500' + ) + assert( + headBlob.isDone(), + 'HEAD /api/projects/:historyId/blobs/:hash should have been called' + ) + assert( + !createBlob.isDone(), + '/api/projects/:historyId/blobs/:hash should have been skipped' + ) + assert( + !addFile.isDone(), + `/api/projects/${historyId}/changes should have been skipped` ) }) - it('should skip blob write when blob exists', function (done) { + it('should skip blob write when blob exists', async function () { MockHistoryStore() .get(`/api/projects/${historyId}/latest/history`) .reply(200, { @@ -562,58 +500,45 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [], - files: [ - { - file: this.file_id, - path: '/test.png', - _hash: fileHash, - url: `http://127.0.0.1:3009/project/${this.project_id}/file/${this.file_id}`, - }, - { path: '/persistedFile' }, - ], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - throw error - } - assert(!loggerWarn.called, 'no warning logged on 404') - assert( - headBlob.isDone(), - 'HEAD /api/projects/:historyId/blobs/:hash should have been called' - ) - assert( - !createBlob.isDone(), - '/api/projects/:historyId/blobs/:hash should have been skipped' - ) - assert( - addFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [], + files: [ + { + file: this.file_id, + path: '/test.png', + _hash: fileHash, + url: `http://127.0.0.1:3009/project/${this.project_id}/file/${this.file_id}`, + }, + { path: '/persistedFile' }, + ], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert(!loggerWarn.called, 'no warning logged on 404') + assert( + headBlob.isDone(), + 'HEAD /api/projects/:historyId/blobs/:hash should have been called' + ) + assert( + !createBlob.isDone(), + '/api/projects/:historyId/blobs/:hash should have been skipped' + ) + assert( + addFile.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should add file w/o url', function (done) { + it('should add file w/o url', async function () { MockHistoryStore() .get(`/api/projects/${historyId}/latest/history`) .reply(200, { @@ -666,55 +591,42 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [], - files: [ - { - file: this.file_id, - path: '/test.png', - _hash: fileHash, - createdBlob: true, - }, - { path: '/persistedFile' }, - ], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - throw error - } - assert(!loggerWarn.called, 'no warning logged on 404') - assert( - headBlob.isDone(), - 'HEAD /api/projects/:historyId/blobs/:hash should have been called' - ) - assert( - !createBlob.isDone(), - '/api/projects/:historyId/blobs/:hash should have been skipped' - ) - assert( - addFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [], + files: [ + { + file: this.file_id, + path: '/test.png', + _hash: fileHash, + createdBlob: true, + }, + { path: '/persistedFile' }, + ], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert(!loggerWarn.called, 'no warning logged on 404') + assert( + headBlob.isDone(), + 'HEAD /api/projects/:historyId/blobs/:hash should have been called' + ) + assert( + !createBlob.isDone(), + '/api/projects/:historyId/blobs/:hash should have been skipped' + ) + assert( + addFile.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) describe('with filestore disabled', function () { @@ -724,7 +636,7 @@ describe('Syncing with web and doc-updater', function () { after(function () { Settings.apis.filestore.enabled = true }) - it('should record error when blob is missing', function (done) { + it('should record error when blob is missing', async function () { MockHistoryStore() .get(`/api/projects/${historyId}/latest/history`) .reply(200, { @@ -778,85 +690,61 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [], - files: [ - { - file: this.file_id, - path: '/test.png', - _hash: fileHash, - url: `http://127.0.0.1:3009/project/${this.project_id}/file/${this.file_id}`, - }, - { path: '/persistedFile' }, - ], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate( - this.project_id, - update, - cb - ) - }, - cb => { - ProjectHistoryClient.flushProject( - this.project_id, - { - allowErrors: true, - }, - (err, res) => { - if (err) return cb(err) - assert( - res.statusCode === 500, - 'resync should have failed' - ) - cb() - } - ) - }, - ], - error => { - if (error) { - throw error - } - assert( - loggerError.calledWithMatch( - sinon.match.any, - 'blocking filestore read' - ), - 'error logged on 500' - ) - assert( - headBlob.isDone(), - 'HEAD /api/projects/:historyId/blobs/:hash should have been called' - ) - assert( - !createBlob.isDone(), - '/api/projects/:historyId/blobs/:hash should have been skipped' - ) - assert( - !addFile.isDone(), - `/api/projects/${historyId}/changes should have been skipped` - ) - done() + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [], + files: [ + { + file: this.file_id, + path: '/test.png', + _hash: fileHash, + url: `http://127.0.0.1:3009/project/${this.project_id}/file/${this.file_id}`, + }, + { path: '/persistedFile' }, + ], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) + + const res = await ProjectHistoryClient.flushProject( + this.project_id, + { + allowErrors: true, } ) + assert(res.statusCode === 500, 'resync should have failed') + + assert( + loggerError.calledWithMatch( + sinon.match.any, + 'blocking filestore read' + ), + 'error logged on 500' + ) + assert( + headBlob.isDone(), + 'HEAD /api/projects/:historyId/blobs/:hash should have been called' + ) + assert( + !createBlob.isDone(), + '/api/projects/:historyId/blobs/:hash should have been skipped' + ) + assert( + !addFile.isDone(), + `/api/projects/${historyId}/changes should have been skipped` + ) }) }) }) describe('when a file hash mismatches', function () { - it('should remove and re-add file w/o url', function (done) { + it('should remove and re-add file w/o url', async function () { MockHistoryStore() .get(`/api/projects/${historyId}/latest/history`) .reply(200, { @@ -921,60 +809,47 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [], - files: [ - { - file: this.file_id, - path: '/test.png', - _hash: fileHash, - createdBlob: true, - }, - ], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - throw error - } - assert(!loggerWarn.called, 'no warning logged on 404') - assert( - headBlob.isDone(), - 'HEAD /api/projects/:historyId/blobs/:hash should have been called' - ) - assert( - !createBlob.isDone(), - '/api/projects/:historyId/blobs/:hash should have been skipped' - ) - assert( - addFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [], + files: [ + { + file: this.file_id, + path: '/test.png', + _hash: fileHash, + createdBlob: true, + }, + ], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert(!loggerWarn.called, 'no warning logged on 404') + assert( + headBlob.isDone(), + 'HEAD /api/projects/:historyId/blobs/:hash should have been called' + ) + assert( + !createBlob.isDone(), + '/api/projects/:historyId/blobs/:hash should have been skipped' + ) + assert( + addFile.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) }) describe("when a file exists which shouldn't", function () { - it('should send remove file updates to the history store', function (done) { + it('should send remove file updates to the history store', async function () { MockHistoryStore() .get(`/api/projects/${historyId}/latest/history`) .reply(200, { @@ -1019,38 +894,25 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: 'docToKeep' }], - files: [], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - throw error - } - assert( - deleteFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: 'docToKeep' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + deleteFile.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) }) @@ -1083,7 +945,7 @@ describe('Syncing with web and doc-updater', function () { .reply(200, 'a\nb') }) - it('should send test updates to the history store', function (done) { + it('should send test updates to the history store', async function () { const addFile = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -1105,56 +967,42 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb\nc', - }, - doc: this.doc_id, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - throw error - } - assert( - addFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb\nc', + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + addFile.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should strip non-BMP characters in updates before sending to the history store', function (done) { + it('should strip non-BMP characters in updates before sending to the history store', async function () { const addFile = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -1176,56 +1024,42 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb\n\uD800\uDC00c', - }, - doc: this.doc_id, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - throw error - } - assert( - addFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb\n\uD800\uDC00c', + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + addFile.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should add comments in the history store', function (done) { + it('should add comments in the history store', async function () { const commentId = 'comment-id' const addComment = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { @@ -1249,74 +1083,60 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb', - ranges: { - comments: [ - { - id: commentId, - op: { - c: 'a', - p: 0, - hpos: 1, - hlen: 10, - t: commentId, - }, - meta: { - user_id: 'user-id', - ts: this.timestamp, - }, - }, - ], + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb', + ranges: { + comments: [ + { + id: commentId, + op: { + c: 'a', + p: 0, + hpos: 1, + hlen: 10, + t: commentId, + }, + meta: { + user_id: 'user-id', + ts: this.timestamp, }, }, - doc: this.doc_id, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) + ], }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - addComment.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + addComment.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should add comments in the history store (history-ot)', function (done) { + it('should add comments in the history store (history-ot)', async function () { const commentId = 'comment-id' const addComment = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { @@ -1340,69 +1160,55 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb', + historyOTRanges: { + comments: [ + { + id: commentId, + ranges: [ + { + pos: 1, + length: 10, + }, + ], }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) + ], }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb', - historyOTRanges: { - comments: [ - { - id: commentId, - ranges: [ - { - pos: 1, - length: 10, - }, - ], - }, - ], - }, - }, - doc: this.doc_id, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - addComment.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + addComment.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should add tracked changes in the history store', function (done) { + it('should add tracked changes in the history store', async function () { const fixTrackedChange = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -1442,83 +1248,69 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb', - ranges: { - changes: [ - { - id: 'id1', - op: { - d: 'a', - p: 0, - }, - metadata: { - user_id: 'user-id', - ts: this.timestamp, - }, - }, - { - id: 'id2', - op: { - i: '\n', - p: 0, - hpos: 1, - }, - metadata: { - user_id: 'user-id', - ts: this.timestamp, - }, - }, - ], + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb', + ranges: { + changes: [ + { + id: 'id1', + op: { + d: 'a', + p: 0, + }, + metadata: { + user_id: 'user-id', + ts: this.timestamp, }, }, - doc: this.doc_id, - meta: { - ts: this.timestamp, + { + id: 'id2', + op: { + i: '\n', + p: 0, + hpos: 1, + }, + metadata: { + user_id: 'user-id', + ts: this.timestamp, + }, }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) + ], }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - fixTrackedChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + fixTrackedChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should add tracked changes in the history store (history-ot)', function (done) { + it('should add tracked changes in the history store (history-ot)', async function () { const fixTrackedChange = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -1558,72 +1350,58 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb', - historyOTRanges: { - trackedChanges: [ - { - range: { pos: 0, length: 1 }, - tracking: { - ts: this.timestamp.toJSON(), - type: 'delete', - userId: 'user-id', - }, - }, - { - range: { pos: 1, length: 1 }, - tracking: { - ts: this.timestamp.toJSON(), - type: 'insert', - userId: 'user-id', - }, - }, - ], + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb', + historyOTRanges: { + trackedChanges: [ + { + range: { pos: 0, length: 1 }, + tracking: { + ts: this.timestamp.toJSON(), + type: 'delete', + userId: 'user-id', }, }, - doc: this.doc_id, - meta: { - ts: this.timestamp, + { + range: { pos: 1, length: 1 }, + tracking: { + ts: this.timestamp.toJSON(), + type: 'insert', + userId: 'user-id', + }, }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) + ], }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - fixTrackedChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + fixTrackedChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) }) @@ -1687,7 +1465,7 @@ describe('Syncing with web and doc-updater', function () { ) }) - it('should fix comments in the history store', function (done) { + it('should fix comments in the history store', async function () { const addComment = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -1710,99 +1488,85 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb', - ranges: { - comments: [ - { - id: commentId, - op: { - c: 'a', - p: 0, - hpos: 1, - hlen: 2, - t: commentId, - }, - meta: { - user_id: 'user-id', - ts: this.timestamp, - }, - }, - ], - changes: [ - { - id: 'id1', - op: { - d: 'a', - p: 0, - }, - metadata: { - user_id: 'user-id', - ts: this.timestamp, - }, - }, - { - id: 'id2', - op: { - i: '\n', - p: 1, - hpos: 2, - }, - metadata: { - user_id: 'user-id', - ts: this.timestamp, - }, - }, - ], + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb', + ranges: { + comments: [ + { + id: commentId, + op: { + c: 'a', + p: 0, + hpos: 1, + hlen: 2, + t: commentId, + }, + meta: { + user_id: 'user-id', + ts: this.timestamp, }, }, - doc: this.doc_id, - meta: { - ts: this.timestamp, + ], + changes: [ + { + id: 'id1', + op: { + d: 'a', + p: 0, + }, + metadata: { + user_id: 'user-id', + ts: this.timestamp, + }, }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) + { + id: 'id2', + op: { + i: '\n', + p: 1, + hpos: 2, + }, + metadata: { + user_id: 'user-id', + ts: this.timestamp, + }, + }, + ], }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - addComment.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + addComment.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should fix resolved state for comments in the history store', function (done) { + it('should fix resolved state for comments in the history store', async function () { const addComment = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -1825,100 +1589,86 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb', - resolvedCommentIds: [commentId], - ranges: { - comments: [ - { - id: commentId, - op: { - c: 'a', - p: 0, - hpos: 0, - hlen: 3, - t: commentId, - }, - meta: { - user_id: 'user-id', - ts: this.timestamp, - }, - }, - ], - changes: [ - { - id: 'id1', - op: { - d: 'a', - p: 0, - }, - metadata: { - user_id: 'user-id', - ts: this.timestamp, - }, - }, - { - id: 'id2', - op: { - i: '\n', - p: 1, - hpos: 2, - }, - metadata: { - user_id: 'user-id', - ts: this.timestamp, - }, - }, - ], + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb', + resolvedCommentIds: [commentId], + ranges: { + comments: [ + { + id: commentId, + op: { + c: 'a', + p: 0, + hpos: 0, + hlen: 3, + t: commentId, + }, + meta: { + user_id: 'user-id', + ts: this.timestamp, }, }, - doc: this.doc_id, - meta: { - ts: this.timestamp, + ], + changes: [ + { + id: 'id1', + op: { + d: 'a', + p: 0, + }, + metadata: { + user_id: 'user-id', + ts: this.timestamp, + }, }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) + { + id: 'id2', + op: { + i: '\n', + p: 1, + hpos: 2, + }, + metadata: { + user_id: 'user-id', + ts: this.timestamp, + }, + }, + ], }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - addComment.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + addComment.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should fix comments in the history store (history-ot)', function (done) { + it('should fix comments in the history store (history-ot)', async function () { const addComment = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -1941,87 +1691,73 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb', + historyOTRanges: { + comments: [ + { + id: commentId, + ranges: [ + { + pos: 1, + length: 2, + }, + ], }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb', - historyOTRanges: { - comments: [ - { - id: commentId, - ranges: [ - { - pos: 1, - length: 2, - }, - ], - }, - ], - trackedChanges: [ - { - range: { pos: 0, length: 1 }, - tracking: { - ts: this.timestamp.toJSON(), - type: 'delete', - userId: 'user-id', - }, - }, - { - range: { pos: 2, length: 1 }, - tracking: { - ts: this.timestamp.toJSON(), - type: 'insert', - userId: 'user-id', - }, - }, - ], + ], + trackedChanges: [ + { + range: { pos: 0, length: 1 }, + tracking: { + ts: this.timestamp.toJSON(), + type: 'delete', + userId: 'user-id', }, }, - doc: this.doc_id, - meta: { - ts: this.timestamp, + { + range: { pos: 2, length: 1 }, + tracking: { + ts: this.timestamp.toJSON(), + type: 'insert', + userId: 'user-id', + }, }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) + ], }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - addComment.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + addComment.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should fix resolved state for comments in the history store (history-ot)', function (done) { + it('should fix resolved state for comments in the history store (history-ot)', async function () { const addComment = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -2044,88 +1780,74 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb', + historyOTRanges: { + comments: [ + { + id: commentId, + ranges: [ + { + pos: 0, + length: 3, + }, + ], + resolved: true, }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb', - historyOTRanges: { - comments: [ - { - id: commentId, - ranges: [ - { - pos: 0, - length: 3, - }, - ], - resolved: true, - }, - ], - trackedChanges: [ - { - range: { pos: 0, length: 1 }, - tracking: { - ts: this.timestamp.toJSON(), - type: 'delete', - userId: 'user-id', - }, - }, - { - range: { pos: 2, length: 1 }, - tracking: { - ts: this.timestamp.toJSON(), - type: 'insert', - userId: 'user-id', - }, - }, - ], + ], + trackedChanges: [ + { + range: { pos: 0, length: 1 }, + tracking: { + ts: this.timestamp.toJSON(), + type: 'delete', + userId: 'user-id', }, }, - doc: this.doc_id, - meta: { - ts: this.timestamp, + { + range: { pos: 2, length: 1 }, + tracking: { + ts: this.timestamp.toJSON(), + type: 'insert', + userId: 'user-id', + }, }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) + ], }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - addComment.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + addComment.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should fix tracked changes in the history store', function (done) { + it('should fix tracked changes in the history store', async function () { const fixTrackedChange = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -2163,99 +1885,85 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb', - ranges: { - comments: [ - { - id: commentId, - op: { - c: 'a', - p: 0, - hpos: 0, - hlen: 3, - t: commentId, - }, - meta: { - user_id: 'user-id', - ts: this.timestamp, - }, - }, - ], - changes: [ - { - id: 'id1', - op: { - d: 'a', - p: 0, - }, - metadata: { - user_id: 'user-id', - ts: this.timestamp, - }, - }, - { - id: 'id2', - op: { - i: '\n', - p: 0, - hpos: 1, - }, - metadata: { - user_id: 'user-id', - ts: this.timestamp, - }, - }, - ], + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb', + ranges: { + comments: [ + { + id: commentId, + op: { + c: 'a', + p: 0, + hpos: 0, + hlen: 3, + t: commentId, + }, + meta: { + user_id: 'user-id', + ts: this.timestamp, }, }, - doc: this.doc_id, - meta: { - ts: this.timestamp, + ], + changes: [ + { + id: 'id1', + op: { + d: 'a', + p: 0, + }, + metadata: { + user_id: 'user-id', + ts: this.timestamp, + }, }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) + { + id: 'id2', + op: { + i: '\n', + p: 0, + hpos: 1, + }, + metadata: { + user_id: 'user-id', + ts: this.timestamp, + }, + }, + ], }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - fixTrackedChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + fixTrackedChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should fix tracked changes in the history store (history-ot)', function (done) { + it('should fix tracked changes in the history store (history-ot)', async function () { const fixTrackedChange = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -2293,87 +2001,73 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb', + historyOTRanges: { + comments: [ + { + id: commentId, + ranges: [ + { + pos: 0, + length: 3, + }, + ], }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb', - historyOTRanges: { - comments: [ - { - id: commentId, - ranges: [ - { - pos: 0, - length: 3, - }, - ], - }, - ], - trackedChanges: [ - { - range: { pos: 0, length: 1 }, - tracking: { - ts: this.timestamp.toJSON(), - type: 'delete', - userId: 'user-id', - }, - }, - { - range: { pos: 1, length: 1 }, - tracking: { - ts: this.timestamp.toJSON(), - type: 'insert', - userId: 'user-id', - }, - }, - ], + ], + trackedChanges: [ + { + range: { pos: 0, length: 1 }, + tracking: { + ts: this.timestamp.toJSON(), + type: 'delete', + userId: 'user-id', }, }, - doc: this.doc_id, - meta: { - ts: this.timestamp, + { + range: { pos: 1, length: 1 }, + tracking: { + ts: this.timestamp.toJSON(), + type: 'insert', + userId: 'user-id', + }, }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) + ], }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - fixTrackedChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + fixTrackedChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) - it('should fix both comments and tracked changes in the history store (history-ot)', function (done) { + it('should fix both comments and tracked changes in the history store (history-ot)', async function () { const fixTrackedChange = MockHistoryStore() .post(`/api/projects/${historyId}/legacy_changes`, body => { expect(body).to.deep.equal([ @@ -2425,89 +2119,75 @@ describe('Syncing with web and doc-updater', function () { .query({ end_version: 0 }) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [], + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update1 = { + projectHistoryId: historyId, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update1) + + const update2 = { + path: '/main.tex', + projectHistoryId: historyId, + resyncDocContent: { + content: 'a\nb', + historyOTRanges: { + comments: [ + { + id: commentId, + ranges: [ + { + pos: 1, + length: 2, + }, + ], }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - const update = { - path: '/main.tex', - projectHistoryId: historyId, - resyncDocContent: { - content: 'a\nb', - historyOTRanges: { - comments: [ - { - id: commentId, - ranges: [ - { - pos: 1, - length: 2, - }, - ], - }, - ], - trackedChanges: [ - { - range: { pos: 0, length: 1 }, - tracking: { - ts: this.timestamp.toJSON(), - type: 'delete', - userId: 'user-id', - }, - }, - { - range: { pos: 1, length: 1 }, - tracking: { - ts: this.timestamp.toJSON(), - type: 'insert', - userId: 'user-id', - }, - }, - ], + ], + trackedChanges: [ + { + range: { pos: 0, length: 1 }, + tracking: { + ts: this.timestamp.toJSON(), + type: 'delete', + userId: 'user-id', }, }, - doc: this.doc_id, - meta: { - ts: this.timestamp, + { + range: { pos: 1, length: 1 }, + tracking: { + ts: this.timestamp.toJSON(), + type: 'insert', + userId: 'user-id', + }, }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) + ], }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - ], - error => { - if (error) { - return done(error) - } - assert( - fixTrackedChange.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - done() - } + }, + doc: this.doc_id, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update2) + + await ProjectHistoryClient.flushProject(this.project_id) + + assert( + fixTrackedChange.isDone(), + `/api/projects/${historyId}/changes should have been called` ) }) }) describe('resyncProjectStructureOnly', function () { - it('should handle structure only updates', function (done) { + it('should handle structure only updates', async function () { const fileHash = 'aed2973e4b8a7ff1b30ff5c4751e5a2b38989e74' MockHistoryStore() @@ -2565,57 +2245,43 @@ describe('Syncing with web and doc-updater', function () { .post(`/project/${this.project_id}/history/resync`) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructureOnly: true, - resyncProjectStructure: { - docs: [{ path: '/main.tex' }], - files: [ - { - file: this.file_id, - path: '/test.png', - _hash: fileHash, - createdBlob: true, - }, - ], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject(this.project_id, cb) - }, - cb => { - // fails when previous resync did not finish - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - ], - error => { - if (error) { - throw error - } - assert( - addFile.isDone(), - `/api/projects/${historyId}/changes should have been called` - ) - assert( - !docContentRequest.isDone(), - 'should not have requested doc content' - ) - done() - } + await ProjectHistoryClient.resyncHistory(this.project_id) + + const update = { + projectHistoryId: historyId, + resyncProjectStructureOnly: true, + resyncProjectStructure: { + docs: [{ path: '/main.tex' }], + files: [ + { + file: this.file_id, + path: '/test.png', + _hash: fileHash, + createdBlob: true, + }, + ], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) + + await ProjectHistoryClient.flushProject(this.project_id) + + // fails when previous resync did not finish + await ProjectHistoryClient.resyncHistory(this.project_id) + + assert( + addFile.isDone(), + `/api/projects/${historyId}/changes should have been called` + ) + assert( + !docContentRequest.isDone(), + 'should not have requested doc content' ) }) - it('should reject partial resync on docs', function (done) { + it('should reject partial resync on docs', async function () { const fileHash = 'aed2973e4b8a7ff1b30ff5c4751e5a2b38989e74' MockHistoryStore() @@ -2655,75 +2321,59 @@ describe('Syncing with web and doc-updater', function () { .post(`/project/${this.project_id}/history/resync`) .reply(204) - async.series( - [ - cb => { - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - cb => { - const update = { - projectHistoryId: historyId, - resyncProjectStructureOnly: true, - resyncProjectStructure: { - docs: [{ path: '/main-renamed.tex' }], - files: [ - { - file: this.file_id, - path: '/test.png', - _hash: fileHash, - createdBlob: true, - }, - ], - }, - meta: { - ts: this.timestamp, - }, - } - ProjectHistoryClient.pushRawUpdate(this.project_id, update, cb) - }, - cb => { - ProjectHistoryClient.flushProject( - this.project_id, - { allowErrors: true }, - (err, res) => { - if (err) return cb(err) - expect(res.statusCode).to.equal(500) - expect(loggerError).to.have.been.calledWith( - sinon.match({ - err: { - name: 'NeedFullProjectStructureResyncError', - message: 'aborting partial resync: touched doc', - }, - }) - ) + await ProjectHistoryClient.resyncHistory(this.project_id) - getFailure(this.project_id, (err, failure) => { - if (err) return cb(err) - expect(failure).to.include({ - error: - 'NeedFullProjectStructureResyncError: aborting partial resync: touched doc', - }) - cb() - }) - } - ) + const update = { + projectHistoryId: historyId, + resyncProjectStructureOnly: true, + resyncProjectStructure: { + docs: [{ path: '/main-renamed.tex' }], + files: [ + { + file: this.file_id, + path: '/test.png', + _hash: fileHash, + createdBlob: true, + }, + ], + }, + meta: { + ts: this.timestamp, + }, + } + await ProjectHistoryClient.pushRawUpdate(this.project_id, update) + + const res = await ProjectHistoryClient.flushProject(this.project_id, { + allowErrors: true, + }) + expect(res.statusCode).to.equal(500) + expect(loggerError).to.have.been.calledWith( + sinon.match({ + err: { + name: 'NeedFullProjectStructureResyncError', + message: 'aborting partial resync: touched doc', }, - cb => { - // fails when previous resync did not finish - ProjectHistoryClient.resyncHistory(this.project_id, cb) - }, - ], - error => { - if (error) { - throw error - } - assert(!addFile.isDone(), 'should not have persisted changes') - assert( - !docContentRequest.isDone(), - 'should not have requested doc content' - ) - done() - } + }) + ) + + const failure = await new Promise((resolve, reject) => { + getFailure(this.project_id, (err, failure) => { + if (err) return reject(err) + resolve(failure) + }) + }) + expect(failure).to.include({ + error: + 'NeedFullProjectStructureResyncError: aborting partial resync: touched doc', + }) + + // fails when previous resync did not finish + await ProjectHistoryClient.resyncHistory(this.project_id) + + assert(!addFile.isDone(), 'should not have persisted changes') + assert( + !docContentRequest.isDone(), + 'should not have requested doc content' ) }) }) diff --git a/services/project-history/test/acceptance/js/helpers/ProjectHistoryApp.js b/services/project-history/test/acceptance/js/helpers/ProjectHistoryApp.js index 59a9d34d88..df8ced4da7 100644 --- a/services/project-history/test/acceptance/js/helpers/ProjectHistoryApp.js +++ b/services/project-history/test/acceptance/js/helpers/ProjectHistoryApp.js @@ -1,50 +1,37 @@ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS205: Consider reworking code to avoid use of IIFEs - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ import { app } from '../../../../app/js/server.js' import { mongoClient } from '../../../../app/js/mongodb.js' -import { promisify } from '@overleaf/promise-utils' let running = false -let initing = false -const callbacks = [] +let initPromise = null -export function ensureRunning(callback) { - if (callback == null) { - callback = function () {} - } - if (running) { - return callback() - } else if (initing) { - return callbacks.push(callback) - } - initing = true - callbacks.push(callback) - app.listen(3054, '127.0.0.1', error => { - if (error != null) { - throw error - } +async function initialize() { + try { + await new Promise((resolve, reject) => { + app.listen(3054, '127.0.0.1', error => { + if (error) return reject(error) + resolve() + }) + }) // Wait for mongo - mongoClient.connect(error => { - if (error != null) { - throw error - } - running = true - for (callback of Array.from(callbacks)) { - callback() - } - }) - }) + await mongoClient.connect() + + running = true + } catch (error) { + initPromise = null + throw error + } } -export const promises = { - ensureRunning: promisify(ensureRunning), +export async function ensureRunning() { + if (running) { + return + } + + if (initPromise) { + return await initPromise + } + + initPromise = initialize() + return await initPromise } diff --git a/services/project-history/test/acceptance/js/helpers/ProjectHistoryClient.js b/services/project-history/test/acceptance/js/helpers/ProjectHistoryClient.js index d56b6add1c..9ca5af4f91 100644 --- a/services/project-history/test/acceptance/js/helpers/ProjectHistoryClient.js +++ b/services/project-history/test/acceptance/js/helpers/ProjectHistoryClient.js @@ -3,7 +3,6 @@ import request from 'request' import Settings from '@overleaf/settings' import RedisWrapper from '@overleaf/redis-wrapper' import { db } from '../../../../app/js/mongodb.js' -import { promisify } from '@overleaf/promise-utils' import { fetchJson, fetchJsonWithResponse, @@ -19,44 +18,34 @@ export function resetDatabase(callback) { rclient.flushdb(callback) } -export function initializeProject(historyId, callback) { - request.post( +export async function initializeProject(historyId) { + const response = await fetchJsonWithResponse( + 'http://127.0.0.1:3054/project', { - url: 'http://127.0.0.1:3054/project', + method: 'POST', json: { historyId }, - }, - (error, res, body) => { - if (error) { - return callback(error) - } - expect(res.statusCode).to.equal(200) - callback(null, body.project) } ) + expect(response.response.status).to.equal(200) + return response.json.project } -export function flushProject(projectId, options, callback) { - if (typeof options === 'function') { - callback = options - options = null - } - if (!options) { - options = { allowErrors: false } - } - request.post( - { - url: `http://127.0.0.1:3054/project/${projectId}/flush`, - }, - (error, res, body) => { - if (error) { - return callback(error) - } - if (!options.allowErrors) { - expect(res.statusCode).to.equal(204) - } - callback(error, res) +export async function flushProject(projectId, options = {}) { + try { + const response = await fetchNothing( + `http://127.0.0.1:3054/project/${projectId}/flush`, + { method: 'POST' } + ) + if (!options.allowErrors) { + expect(response.status).to.equal(204) } - ) + return { statusCode: response.status } + } catch (error) { + if (options.allowErrors && error instanceof RequestFailedError) { + return { statusCode: error.response.status } + } + throw error + } } export async function getSummarizedUpdates(projectId, query) { @@ -135,33 +124,29 @@ export async function getSnapshot(projectId, pathname, version, options = {}) { } } -export function pushRawUpdate(projectId, update, callback) { - rclient.rpush( +export async function pushRawUpdate(projectId, update) { + await rclient.rpush( Keys.projectHistoryOps({ project_id: projectId }), - JSON.stringify(update), - callback + JSON.stringify(update) ) } -export function setFirstOpTimestamp(projectId, timestamp, callback) { - rclient.set( +export async function setFirstOpTimestamp(projectId, timestamp) { + await rclient.set( Keys.projectHistoryFirstOpTimestamp({ project_id: projectId }), - timestamp, - callback + timestamp ) } -export function getFirstOpTimestamp(projectId, callback) { - rclient.get( - Keys.projectHistoryFirstOpTimestamp({ project_id: projectId }), - callback +export async function getFirstOpTimestamp(projectId) { + return await rclient.get( + Keys.projectHistoryFirstOpTimestamp({ project_id: projectId }) ) } -export function clearFirstOpTimestamp(projectId, callback) { - rclient.del( - Keys.projectHistoryFirstOpTimestamp({ project_id: projectId }), - callback +export async function clearFirstOpTimestamp(projectId) { + await rclient.del( + Keys.projectHistoryFirstOpTimestamp({ project_id: projectId }) ) } @@ -179,21 +164,15 @@ export function getQueueCounts(callback) { ) } -export function resyncHistory(projectId, callback) { - request.post( +export async function resyncHistory(projectId) { + const response = await fetchNothing( + `http://127.0.0.1:3054/project/${projectId}/resync`, { - url: `http://127.0.0.1:3054/project/${projectId}/resync`, - json: true, - body: { origin: { kind: 'test-origin' } }, - }, - (error, res, body) => { - if (error) { - return callback(error) - } - expect(res.statusCode).to.equal(204) - callback(error) + method: 'POST', + json: { origin: { kind: 'test-origin' } }, } ) + expect(response.status).to.equal(204) } export async function createLabel( @@ -257,11 +236,3 @@ export async function deleteProject(projectId) { ) expect(response.status).to.equal(204) } - -export const promises = { - initializeProject: promisify(initializeProject), - pushRawUpdate: promisify(pushRawUpdate), - setFirstOpTimestamp: promisify(setFirstOpTimestamp), - getFirstOpTimestamp: promisify(getFirstOpTimestamp), - flushProject: promisify(flushProject), -}