overleaf/services/document-updater/test/unit/js/ShareJsUpdateManager/ShareJsUpdateManagerTests.js
Eric Mc Sween 4d70bd664f Reintroduce Node 12 and metrics upgrades
These changes were previously merged, not deployed, and reverted. This
reverts the revert.

This reverts commit a6b8c6c658b33b6eee78b8b99e43308f32211ae2, reversing
changes made to 93c98921372eed4244d22fce800716cb27eca299.
2021-04-01 15:51:00 -04:00

237 lines
7.3 KiB
JavaScript

/* eslint-disable
no-return-assign,
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
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const sinon = require('sinon')
const modulePath = '../../../../app/js/ShareJsUpdateManager.js'
const SandboxedModule = require('sandboxed-module')
const crypto = require('crypto')
describe('ShareJsUpdateManager', function () {
beforeEach(function () {
let Model
this.project_id = 'project-id-123'
this.doc_id = 'document-id-123'
this.callback = sinon.stub()
return (this.ShareJsUpdateManager = SandboxedModule.require(modulePath, {
requires: {
'./sharejs/server/model': (Model = class Model {
constructor(db) {
this.db = db
}
}),
'./ShareJsDB': (this.ShareJsDB = { mockDB: true }),
'@overleaf/redis-wrapper': {
createClient: () => {
return (this.rclient = { auth() {} })
}
},
'./RealTimeRedisManager': (this.RealTimeRedisManager = {}),
'./Metrics': (this.metrics = { inc: sinon.stub() })
},
globals: {
clearTimeout: (this.clearTimeout = sinon.stub())
}
}))
})
describe('applyUpdate', function () {
beforeEach(function () {
this.lines = ['one', 'two']
this.version = 34
this.updatedDocLines = ['onefoo', 'two']
const content = this.updatedDocLines.join('\n')
this.hash = crypto
.createHash('sha1')
.update('blob ' + content.length + '\x00')
.update(content, 'utf8')
.digest('hex')
this.update = { p: 4, t: 'foo', v: this.version, hash: this.hash }
this.model = {
applyOp: sinon.stub().callsArg(2),
getSnapshot: sinon.stub(),
db: {
appliedOps: {}
}
}
this.ShareJsUpdateManager.getNewShareJsModel = sinon
.stub()
.returns(this.model)
this.ShareJsUpdateManager._listenForOps = sinon.stub()
return (this.ShareJsUpdateManager.removeDocFromCache = sinon
.stub()
.callsArg(1))
})
describe('successfully', function () {
beforeEach(function (done) {
this.model.getSnapshot.callsArgWith(1, null, {
snapshot: this.updatedDocLines.join('\n'),
v: this.version
})
this.model.db.appliedOps[
`${this.project_id}:${this.doc_id}`
] = this.appliedOps = ['mock-ops']
return this.ShareJsUpdateManager.applyUpdate(
this.project_id,
this.doc_id,
this.update,
this.lines,
this.version,
(err, docLines, version, appliedOps) => {
this.callback(err, docLines, version, appliedOps)
return done()
}
)
})
it('should create a new ShareJs model', function () {
return this.ShareJsUpdateManager.getNewShareJsModel
.calledWith(this.project_id, this.doc_id, this.lines, this.version)
.should.equal(true)
})
it('should listen for ops on the model', function () {
return this.ShareJsUpdateManager._listenForOps
.calledWith(this.model)
.should.equal(true)
})
it('should send the update to ShareJs', function () {
return this.model.applyOp
.calledWith(`${this.project_id}:${this.doc_id}`, this.update)
.should.equal(true)
})
it('should get the updated doc lines', function () {
return this.model.getSnapshot
.calledWith(`${this.project_id}:${this.doc_id}`)
.should.equal(true)
})
return it('should return the updated doc lines, version and ops', function () {
return this.callback
.calledWith(null, this.updatedDocLines, this.version, this.appliedOps)
.should.equal(true)
})
})
describe('when applyOp fails', function () {
beforeEach(function (done) {
this.error = new Error('Something went wrong')
this.model.applyOp = sinon.stub().callsArgWith(2, this.error)
return this.ShareJsUpdateManager.applyUpdate(
this.project_id,
this.doc_id,
this.update,
this.lines,
this.version,
(err, docLines, version) => {
this.callback(err, docLines, version)
return done()
}
)
})
return it('should call the callback with the error', function () {
return this.callback.calledWith(this.error).should.equal(true)
})
})
describe('when getSnapshot fails', function () {
beforeEach(function (done) {
this.error = new Error('Something went wrong')
this.model.getSnapshot.callsArgWith(1, this.error)
return this.ShareJsUpdateManager.applyUpdate(
this.project_id,
this.doc_id,
this.update,
this.lines,
this.version,
(err, docLines, version) => {
this.callback(err, docLines, version)
return done()
}
)
})
return it('should call the callback with the error', function () {
return this.callback.calledWith(this.error).should.equal(true)
})
})
return describe('with an invalid hash', function () {
beforeEach(function (done) {
this.error = new Error('invalid hash')
this.model.getSnapshot.callsArgWith(1, null, {
snapshot: 'unexpected content',
v: this.version
})
this.model.db.appliedOps[
`${this.project_id}:${this.doc_id}`
] = this.appliedOps = ['mock-ops']
return this.ShareJsUpdateManager.applyUpdate(
this.project_id,
this.doc_id,
this.update,
this.lines,
this.version,
(err, docLines, version, appliedOps) => {
this.callback(err, docLines, version, appliedOps)
return done()
}
)
})
return it('should call the callback with the error', function () {
return this.callback
.calledWith(sinon.match.instanceOf(Error))
.should.equal(true)
})
})
})
return describe('_listenForOps', function () {
beforeEach(function () {
this.model = {
on: (event, callback) => {
return (this.callback = callback)
}
}
sinon.spy(this.model, 'on')
return this.ShareJsUpdateManager._listenForOps(this.model)
})
it('should listen to the model for updates', function () {
return this.model.on.calledWith('applyOp').should.equal(true)
})
return describe('the callback', function () {
beforeEach(function () {
this.opData = {
op: { t: 'foo', p: 1 },
meta: { source: 'bar' }
}
this.RealTimeRedisManager.sendData = sinon.stub()
return this.callback(`${this.project_id}:${this.doc_id}`, this.opData)
})
return it('should publish the op to redis', function () {
return this.RealTimeRedisManager.sendData
.calledWith({
project_id: this.project_id,
doc_id: this.doc_id,
op: this.opData
})
.should.equal(true)
})
})
})
})