mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-01 18:01:32 -05:00
587 lines
16 KiB
JavaScript
587 lines
16 KiB
JavaScript
/* eslint-disable
|
|
handle-callback-err,
|
|
no-unused-vars,
|
|
*/
|
|
// TODO: This file was created by bulk-decaffeinate.
|
|
// Fix any style issues and re-enable lint.
|
|
/*
|
|
* decaffeinate suggestions:
|
|
* DS102: Remove unnecessary code created because of implicit returns
|
|
* DS207: Consider shorter variations of null checks
|
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
*/
|
|
const sinon = require('sinon')
|
|
const { expect } = require('chai')
|
|
const { ObjectId } = require('../../../app/js/mongodb')
|
|
const Settings = require('@overleaf/settings')
|
|
const request = require('request')
|
|
const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now
|
|
|
|
const TrackChangesApp = require('./helpers/TrackChangesApp')
|
|
const TrackChangesClient = require('./helpers/TrackChangesClient')
|
|
const MockWebApi = require('./helpers/MockWebApi')
|
|
|
|
describe('Appending doc ops to the history', function () {
|
|
before(function (done) {
|
|
return TrackChangesApp.ensureRunning(done)
|
|
})
|
|
|
|
describe('when the history does not exist yet', function () {
|
|
before(function (done) {
|
|
this.project_id = ObjectId().toString()
|
|
this.doc_id = ObjectId().toString()
|
|
this.user_id = ObjectId().toString()
|
|
MockWebApi.projects[this.project_id] = { features: { versioning: false } }
|
|
TrackChangesClient.pushRawUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
[
|
|
{
|
|
op: [{ i: 'f', p: 3 }],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 3,
|
|
},
|
|
{
|
|
op: [{ i: 'o', p: 4 }],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 4,
|
|
},
|
|
{
|
|
op: [{ i: 'o', p: 5 }],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 5,
|
|
},
|
|
],
|
|
error => {
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return TrackChangesClient.flushAndGetCompressedUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
(error, updates) => {
|
|
this.updates = updates
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return done()
|
|
}
|
|
)
|
|
}
|
|
)
|
|
return null
|
|
})
|
|
|
|
it('should insert the compressed op into mongo', function () {
|
|
return expect(this.updates[0].pack[0].op).to.deep.equal([
|
|
{
|
|
p: 3,
|
|
i: 'foo',
|
|
},
|
|
])
|
|
})
|
|
|
|
it('should insert the correct version number into mongo', function () {
|
|
return expect(this.updates[0].v).to.equal(5)
|
|
})
|
|
|
|
it('should store the doc id', function () {
|
|
return expect(this.updates[0].doc_id.toString()).to.equal(this.doc_id)
|
|
})
|
|
|
|
it('should store the project id', function () {
|
|
return expect(this.updates[0].project_id.toString()).to.equal(
|
|
this.project_id
|
|
)
|
|
})
|
|
|
|
return it('should clear the doc from the DocsWithHistoryOps set', function (done) {
|
|
rclient.sismember(
|
|
`DocsWithHistoryOps:${this.project_id}`,
|
|
this.doc_id,
|
|
(error, member) => {
|
|
member.should.equal(0)
|
|
return done()
|
|
}
|
|
)
|
|
return null
|
|
})
|
|
})
|
|
|
|
describe('when the history has already been started', function () {
|
|
beforeEach(function (done) {
|
|
this.project_id = ObjectId().toString()
|
|
this.doc_id = ObjectId().toString()
|
|
this.user_id = ObjectId().toString()
|
|
MockWebApi.projects[this.project_id] = { features: { versioning: false } }
|
|
TrackChangesClient.pushRawUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
[
|
|
{
|
|
op: [{ i: 'f', p: 3 }],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 3,
|
|
},
|
|
{
|
|
op: [{ i: 'o', p: 4 }],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 4,
|
|
},
|
|
{
|
|
op: [{ i: 'o', p: 5 }],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 5,
|
|
},
|
|
],
|
|
error => {
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return TrackChangesClient.flushAndGetCompressedUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
(error, updates) => {
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return done()
|
|
}
|
|
)
|
|
}
|
|
)
|
|
return null
|
|
})
|
|
|
|
describe('when the updates are recent and from the same user', function () {
|
|
beforeEach(function (done) {
|
|
TrackChangesClient.pushRawUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
[
|
|
{
|
|
op: [{ i: 'b', p: 6 }],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 6,
|
|
},
|
|
{
|
|
op: [{ i: 'a', p: 7 }],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 7,
|
|
},
|
|
{
|
|
op: [{ i: 'r', p: 8 }],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 8,
|
|
},
|
|
],
|
|
error => {
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return TrackChangesClient.flushAndGetCompressedUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
(error, updates) => {
|
|
this.updates = updates
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return done()
|
|
}
|
|
)
|
|
}
|
|
)
|
|
return null
|
|
})
|
|
|
|
it('should combine all the updates into one pack', function () {
|
|
return expect(this.updates[0].pack[1].op).to.deep.equal([
|
|
{
|
|
p: 6,
|
|
i: 'bar',
|
|
},
|
|
])
|
|
})
|
|
|
|
return it('should insert the correct version number into mongo', function () {
|
|
return expect(this.updates[0].v_end).to.equal(8)
|
|
})
|
|
})
|
|
|
|
return describe('when the updates are far apart', function () {
|
|
beforeEach(function (done) {
|
|
const oneDay = 24 * 60 * 60 * 1000
|
|
TrackChangesClient.pushRawUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
[
|
|
{
|
|
op: [{ i: 'b', p: 6 }],
|
|
meta: { ts: Date.now() + oneDay, user_id: this.user_id },
|
|
v: 6,
|
|
},
|
|
{
|
|
op: [{ i: 'a', p: 7 }],
|
|
meta: { ts: Date.now() + oneDay, user_id: this.user_id },
|
|
v: 7,
|
|
},
|
|
{
|
|
op: [{ i: 'r', p: 8 }],
|
|
meta: { ts: Date.now() + oneDay, user_id: this.user_id },
|
|
v: 8,
|
|
},
|
|
],
|
|
error => {
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return TrackChangesClient.flushAndGetCompressedUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
(error, updates) => {
|
|
this.updates = updates
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return done()
|
|
}
|
|
)
|
|
}
|
|
)
|
|
return null
|
|
})
|
|
|
|
return it('should combine the updates into one pack', function () {
|
|
expect(this.updates[0].pack[0].op).to.deep.equal([
|
|
{
|
|
p: 3,
|
|
i: 'foo',
|
|
},
|
|
])
|
|
return expect(this.updates[0].pack[1].op).to.deep.equal([
|
|
{
|
|
p: 6,
|
|
i: 'bar',
|
|
},
|
|
])
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('when the updates need processing in batches', function () {
|
|
before(function (done) {
|
|
this.project_id = ObjectId().toString()
|
|
this.doc_id = ObjectId().toString()
|
|
this.user_id = ObjectId().toString()
|
|
MockWebApi.projects[this.project_id] = { features: { versioning: false } }
|
|
const updates = []
|
|
this.expectedOp = [{ p: 0, i: '' }]
|
|
for (let i = 0; i <= 250; i++) {
|
|
updates.push({
|
|
op: [{ i: 'a', p: 0 }],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: i,
|
|
})
|
|
this.expectedOp[0].i = `a${this.expectedOp[0].i}`
|
|
}
|
|
|
|
TrackChangesClient.pushRawUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
updates,
|
|
error => {
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return TrackChangesClient.flushAndGetCompressedUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
(error, updates1) => {
|
|
this.updates = updates1
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return done()
|
|
}
|
|
)
|
|
}
|
|
)
|
|
return null
|
|
})
|
|
|
|
it('should concat the compressed op into mongo', function () {
|
|
return expect(this.updates[0].pack.length).to.deep.equal(3)
|
|
}) // batch size is 100
|
|
|
|
return it('should insert the correct version number into mongo', function () {
|
|
return expect(this.updates[0].v_end).to.equal(250)
|
|
})
|
|
})
|
|
|
|
describe('when there are multiple ops in each update', function () {
|
|
before(function (done) {
|
|
this.project_id = ObjectId().toString()
|
|
this.doc_id = ObjectId().toString()
|
|
this.user_id = ObjectId().toString()
|
|
MockWebApi.projects[this.project_id] = { features: { versioning: false } }
|
|
const oneDay = 24 * 60 * 60 * 1000
|
|
TrackChangesClient.pushRawUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
[
|
|
{
|
|
op: [
|
|
{ i: 'f', p: 3 },
|
|
{ i: 'o', p: 4 },
|
|
{ i: 'o', p: 5 },
|
|
],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 3,
|
|
},
|
|
{
|
|
op: [
|
|
{ i: 'b', p: 6 },
|
|
{ i: 'a', p: 7 },
|
|
{ i: 'r', p: 8 },
|
|
],
|
|
meta: { ts: Date.now() + oneDay, user_id: this.user_id },
|
|
v: 4,
|
|
},
|
|
],
|
|
error => {
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return TrackChangesClient.flushAndGetCompressedUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
(error, updates) => {
|
|
this.updates = updates
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return done()
|
|
}
|
|
)
|
|
}
|
|
)
|
|
return null
|
|
})
|
|
|
|
it('should insert the compressed ops into mongo', function () {
|
|
expect(this.updates[0].pack[0].op).to.deep.equal([
|
|
{
|
|
p: 3,
|
|
i: 'foo',
|
|
},
|
|
])
|
|
return expect(this.updates[0].pack[1].op).to.deep.equal([
|
|
{
|
|
p: 6,
|
|
i: 'bar',
|
|
},
|
|
])
|
|
})
|
|
|
|
return it('should insert the correct version numbers into mongo', function () {
|
|
expect(this.updates[0].pack[0].v).to.equal(3)
|
|
return expect(this.updates[0].pack[1].v).to.equal(4)
|
|
})
|
|
})
|
|
|
|
describe('when there is a no-op update', function () {
|
|
before(function (done) {
|
|
this.project_id = ObjectId().toString()
|
|
this.doc_id = ObjectId().toString()
|
|
this.user_id = ObjectId().toString()
|
|
MockWebApi.projects[this.project_id] = { features: { versioning: false } }
|
|
const oneDay = 24 * 60 * 60 * 1000
|
|
TrackChangesClient.pushRawUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
[
|
|
{
|
|
op: [],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 3,
|
|
},
|
|
{
|
|
op: [{ i: 'foo', p: 3 }],
|
|
meta: { ts: Date.now() + oneDay, user_id: this.user_id },
|
|
v: 4,
|
|
},
|
|
],
|
|
error => {
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return TrackChangesClient.flushAndGetCompressedUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
(error, updates) => {
|
|
this.updates = updates
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return done()
|
|
}
|
|
)
|
|
}
|
|
)
|
|
return null
|
|
})
|
|
|
|
it('should insert the compressed no-op into mongo', function () {
|
|
return expect(this.updates[0].pack[0].op).to.deep.equal([])
|
|
})
|
|
|
|
it('should insert the compressed next update into mongo', function () {
|
|
return expect(this.updates[0].pack[1].op).to.deep.equal([
|
|
{
|
|
p: 3,
|
|
i: 'foo',
|
|
},
|
|
])
|
|
})
|
|
|
|
return it('should insert the correct version numbers into mongo', function () {
|
|
expect(this.updates[0].pack[0].v).to.equal(3)
|
|
return expect(this.updates[0].pack[1].v).to.equal(4)
|
|
})
|
|
})
|
|
|
|
describe('when there is a comment update', function () {
|
|
before(function (done) {
|
|
this.project_id = ObjectId().toString()
|
|
this.doc_id = ObjectId().toString()
|
|
this.user_id = ObjectId().toString()
|
|
MockWebApi.projects[this.project_id] = { features: { versioning: false } }
|
|
TrackChangesClient.pushRawUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
[
|
|
{
|
|
op: [
|
|
{ c: 'foo', p: 3 },
|
|
{ d: 'bar', p: 6 },
|
|
],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 3,
|
|
},
|
|
],
|
|
error => {
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return TrackChangesClient.flushAndGetCompressedUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
(error, updates) => {
|
|
this.updates = updates
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return done()
|
|
}
|
|
)
|
|
}
|
|
)
|
|
return null
|
|
})
|
|
|
|
it('should ignore the comment op', function () {
|
|
return expect(this.updates[0].pack[0].op).to.deep.equal([
|
|
{ d: 'bar', p: 6 },
|
|
])
|
|
})
|
|
|
|
return it('should insert the correct version numbers into mongo', function () {
|
|
return expect(this.updates[0].pack[0].v).to.equal(3)
|
|
})
|
|
})
|
|
|
|
describe('when the project has versioning enabled', function () {
|
|
before(function (done) {
|
|
this.project_id = ObjectId().toString()
|
|
this.doc_id = ObjectId().toString()
|
|
this.user_id = ObjectId().toString()
|
|
MockWebApi.projects[this.project_id] = { features: { versioning: true } }
|
|
|
|
TrackChangesClient.pushRawUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
[
|
|
{
|
|
op: [{ i: 'f', p: 3 }],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 3,
|
|
},
|
|
],
|
|
error => {
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return TrackChangesClient.flushAndGetCompressedUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
(error, updates) => {
|
|
this.updates = updates
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return done()
|
|
}
|
|
)
|
|
}
|
|
)
|
|
return null
|
|
})
|
|
|
|
return it('should not add a expiresAt entry in the update in mongo', function () {
|
|
return expect(this.updates[0].expiresAt).to.be.undefined
|
|
})
|
|
})
|
|
|
|
return describe('when the project does not have versioning enabled', function () {
|
|
before(function (done) {
|
|
this.project_id = ObjectId().toString()
|
|
this.doc_id = ObjectId().toString()
|
|
this.user_id = ObjectId().toString()
|
|
MockWebApi.projects[this.project_id] = { features: { versioning: false } }
|
|
|
|
TrackChangesClient.pushRawUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
[
|
|
{
|
|
op: [{ i: 'f', p: 3 }],
|
|
meta: { ts: Date.now(), user_id: this.user_id },
|
|
v: 3,
|
|
},
|
|
],
|
|
error => {
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return TrackChangesClient.flushAndGetCompressedUpdates(
|
|
this.project_id,
|
|
this.doc_id,
|
|
(error, updates) => {
|
|
this.updates = updates
|
|
if (error != null) {
|
|
throw error
|
|
}
|
|
return done()
|
|
}
|
|
)
|
|
}
|
|
)
|
|
return null
|
|
})
|
|
|
|
return it('should add a expiresAt entry in the update in mongo', function () {
|
|
return expect(this.updates[0].expiresAt).to.exist
|
|
})
|
|
})
|
|
})
|