Files
overleaf-cep/services/clsi/test/unit/js/LockManager.test.js
Andrew Rumble cd7da983d1 Merge pull request #30232 from overleaf/ar/convert-clsi-to-es-modules
[clsi] convert to ES modules

GitOrigin-RevId: fb7fa52cc8f678ee31be352e62a5dff95e88008b
2026-01-22 09:06:23 +00:00

125 lines
3.3 KiB
JavaScript

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