| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- 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
- )
- })
- })
- })
|