overleaf/services/clsi/test/unit/js/LockManagerTests.js
Antoine Clausse 7f48c67512 Add prefer-node-protocol ESLint rule (#21532)
* Add `unicorn/prefer-node-protocol`

* Fix `unicorn/prefer-node-protocol` ESLint errors

* Run `npm run format:fix`

* Add sandboxed-module sourceTransformers in mocha setups

Fix `no such file or directory, open 'node:fs'` in `sandboxed-module`

* Remove `node:` in the SandboxedModule requires

* Fix new linting errors with `node:`

GitOrigin-RevId: 68f6e31e2191fcff4cb8058dd0a6914c14f59926
2024-11-11 09:04:51 +00:00

115 lines
3.2 KiB
JavaScript

const { expect } = require('chai')
const sinon = require('sinon')
const SandboxedModule = require('sandboxed-module')
const modulePath = require('node:path').join(
__dirname,
'../../../app/js/LockManager'
)
const Errors = require('../../../app/js/Errors')
describe('LockManager', function () {
beforeEach(function () {
this.key = '/local/compile/directory'
this.clock = sinon.useFakeTimers()
this.LockManager = SandboxedModule.require(modulePath, {
requires: {
'@overleaf/metrics': (this.Metrics = {
inc: sinon.stub(),
gauge: sinon.stub(),
}),
'@overleaf/settings': (this.Settings = {
compileConcurrencyLimit: 5,
}),
'./Errors': (this.Erros = Errors),
},
})
})
afterEach(function () {
this.clock.restore()
})
describe('when the lock is available', function () {
it('the lock can be acquired', function () {
const lock = this.LockManager.acquire(this.key)
expect(lock).to.exist
lock.release()
})
})
describe('after the lock is acquired', function () {
beforeEach(function () {
this.lock = this.LockManager.acquire(this.key)
})
afterEach(function () {
if (this.lock != null) {
this.lock.release()
}
})
it("the lock can't be acquired again", function () {
expect(() => this.LockManager.acquire(this.key)).to.throw(
Errors.AlreadyCompilingError
)
})
it('another lock can be acquired', function () {
const lock = this.LockManager.acquire('another key')
expect(lock).to.exist
lock.release()
})
it('the lock can be acquired again after an expiry period', function () {
// The expiry time is a little bit over 10 minutes. Let's wait 15 minutes.
this.clock.tick(15 * 60 * 1000)
this.lock = this.LockManager.acquire(this.key)
expect(this.lock).to.exist
})
it('the lock can be acquired again after it was released', function () {
this.lock.release()
this.lock = this.LockManager.acquire(this.key)
expect(this.lock).to.exist
})
})
describe('concurrency limit', function () {
it('exceeding the limit', function () {
for (let i = 0; i <= this.Settings.compileConcurrencyLimit; i++) {
this.LockManager.acquire('test_key' + i)
}
this.Metrics.inc
.calledWith('exceeded-compilier-concurrency-limit')
.should.equal(false)
expect(() =>
this.LockManager.acquire(
'test_key_' + (this.Settings.compileConcurrencyLimit + 1),
false
)
).to.throw(Errors.TooManyCompileRequestsError)
this.Metrics.inc
.calledWith('exceeded-compilier-concurrency-limit')
.should.equal(true)
})
it('within the limit', function () {
for (let i = 0; i <= this.Settings.compileConcurrencyLimit - 1; i++) {
this.LockManager.acquire('test_key' + i)
}
this.Metrics.inc
.calledWith('exceeded-compilier-concurrency-limit')
.should.equal(false)
const lock = this.LockManager.acquire(
'test_key_' + this.Settings.compileConcurrencyLimit,
false
)
expect(lock.key).to.equal(
'test_key_' + this.Settings.compileConcurrencyLimit
)
})
})
})