overleaf/services/document-updater/test/unit/js/DispatchManager/DispatchManagerTests.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

199 lines
6.2 KiB
JavaScript

/* eslint-disable
handle-callback-err,
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
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const sinon = require('sinon')
const modulePath = '../../../../app/js/DispatchManager.js'
const SandboxedModule = require('sandboxed-module')
const Errors = require('../../../../app/js/Errors.js')
describe('DispatchManager', function () {
beforeEach(function () {
let Timer
this.timeout(3000)
this.DispatchManager = SandboxedModule.require(modulePath, {
requires: {
'./UpdateManager': (this.UpdateManager = {}),
'settings-sharelatex': (this.settings = {
redis: {
documentupdater: {}
}
}),
'@overleaf/redis-wrapper': (this.redis = {}),
'./RateLimitManager': {},
'./Errors': Errors,
'./Metrics': (this.Metrics = {
Timer: (Timer = (function () {
Timer = class Timer {
static initClass() {
this.prototype.done = sinon.stub()
}
}
Timer.initClass()
return Timer
})())
})
}
})
this.callback = sinon.stub()
return (this.RateLimiter = {
run(task, cb) {
return task(cb)
}
})
}) // run task without rate limit
return describe('each worker', function () {
beforeEach(function () {
this.client = { auth: sinon.stub() }
this.redis.createClient = sinon.stub().returns(this.client)
return (this.worker = this.DispatchManager.createDispatcher(
this.RateLimiter,
0
))
})
it('should create a new redis client', function () {
return this.redis.createClient.called.should.equal(true)
})
describe('_waitForUpdateThenDispatchWorker', function () {
beforeEach(function () {
this.project_id = 'project-id-123'
this.doc_id = 'doc-id-123'
this.doc_key = `${this.project_id}:${this.doc_id}`
return (this.client.blpop = sinon
.stub()
.callsArgWith(2, null, ['pending-updates-list', this.doc_key]))
})
describe('in the normal case', function () {
beforeEach(function () {
this.UpdateManager.processOutstandingUpdatesWithLock = sinon
.stub()
.callsArg(2)
return this.worker._waitForUpdateThenDispatchWorker(this.callback)
})
it('should call redis with BLPOP', function () {
return this.client.blpop
.calledWith('pending-updates-list', 0)
.should.equal(true)
})
it('should call processOutstandingUpdatesWithLock', function () {
return this.UpdateManager.processOutstandingUpdatesWithLock
.calledWith(this.project_id, this.doc_id)
.should.equal(true)
})
it('should not log any errors', function () {
this.logger.error.called.should.equal(false)
return this.logger.warn.called.should.equal(false)
})
return it('should call the callback', function () {
return this.callback.called.should.equal(true)
})
})
describe('with an error', function () {
beforeEach(function () {
this.UpdateManager.processOutstandingUpdatesWithLock = sinon
.stub()
.callsArgWith(2, new Error('a generic error'))
return this.worker._waitForUpdateThenDispatchWorker(this.callback)
})
it('should log an error', function () {
return this.logger.error.called.should.equal(true)
})
return it('should call the callback', function () {
return this.callback.called.should.equal(true)
})
})
describe("with a 'Delete component' error", function () {
beforeEach(function () {
this.UpdateManager.processOutstandingUpdatesWithLock = sinon
.stub()
.callsArgWith(2, new Errors.DeleteMismatchError())
return this.worker._waitForUpdateThenDispatchWorker(this.callback)
})
it('should log a warning', function () {
return this.logger.warn.called.should.equal(true)
})
return it('should call the callback', function () {
return this.callback.called.should.equal(true)
})
})
describe('pending updates list with shard key', function () {
beforeEach(function (done) {
this.client = {
auth: sinon.stub(),
blpop: sinon.stub().callsArgWith(2)
}
this.redis.createClient = sinon.stub().returns(this.client)
this.queueShardNumber = 7
this.worker = this.DispatchManager.createDispatcher(
this.RateLimiter,
this.queueShardNumber
)
this.worker._waitForUpdateThenDispatchWorker(done)
})
it('should call redis with BLPOP with the correct key', function () {
this.client.blpop
.calledWith(`pending-updates-list-${this.queueShardNumber}`, 0)
.should.equal(true)
})
})
})
return describe('run', function () {
return it('should call _waitForUpdateThenDispatchWorker until shutting down', function (done) {
let callCount = 0
this.worker._waitForUpdateThenDispatchWorker = (callback) => {
if (callback == null) {
callback = function (error) {}
}
callCount++
if (callCount === 3) {
this.settings.shuttingDown = true
}
return setTimeout(() => callback(), 10)
}
sinon.spy(this.worker, '_waitForUpdateThenDispatchWorker')
this.worker.run()
var checkStatus = () => {
if (!this.settings.shuttingDown) {
// retry until shutdown
setTimeout(checkStatus, 100)
} else {
this.worker._waitForUpdateThenDispatchWorker.callCount.should.equal(
3
)
return done()
}
}
return checkStatus()
})
})
})
})