mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 17:19:37 +02:00
* [clsi] initial implementation of compile from history * [clsi] copy changes * [saas-e2e] extend test case with nested folder * [saas-e2e] add test case for tracked changes * [web] fix accumulating changes from multiple chunks * [web] optimize size check for compile request payload * [clsi] deduplicate globalBlobs * [clsi] add validation for request body details * [clsi] add metrics for compile from history * [clsi] download binary files concurrently * [clsi] skip download of empty file blob * [clsi] break down e2e compile time metric by compileFromHistory GitOrigin-RevId: 0dadef93e89d8a172c35cb130a1042d9d1bec42a
198 lines
5.6 KiB
JavaScript
198 lines
5.6 KiB
JavaScript
import { vi, describe, beforeEach, it } from 'vitest'
|
|
import sinon from 'sinon'
|
|
import path from 'node:path'
|
|
|
|
const modulePath = path.join(
|
|
import.meta.dirname,
|
|
'../../../app/js/ProjectPersistenceManager'
|
|
)
|
|
|
|
describe('ProjectPersistenceManager', () => {
|
|
beforeEach(async ctx => {
|
|
ctx.fsPromises = {
|
|
statfs: sinon.stub(),
|
|
}
|
|
|
|
vi.doMock('@overleaf/metrics', () => ({
|
|
default: (ctx.Metrics = { gauge: sinon.stub() }),
|
|
}))
|
|
|
|
vi.doMock('../../../app/js/UrlCache', () => ({
|
|
default: (ctx.UrlCache = {}),
|
|
}))
|
|
|
|
vi.doMock(
|
|
'../../../app/js/HistoryResourceWriter',
|
|
() =>
|
|
(ctx.HistoryResourceWriter = {
|
|
clearCacheCb: sinon.stub().yields(null),
|
|
})
|
|
)
|
|
|
|
vi.doMock('../../../app/js/CompileManager', () => ({
|
|
default: (ctx.CompileManager = {}),
|
|
}))
|
|
|
|
vi.doMock('fs', () => ({
|
|
default: { promises: ctx.fsPromises },
|
|
}))
|
|
|
|
vi.doMock('@overleaf/settings', () => ({
|
|
default: (ctx.settings = {
|
|
project_cache_length_ms: 1000,
|
|
path: {
|
|
compilesDir: '/compiles',
|
|
outputDir: '/output',
|
|
clsiCacheDir: '/cache',
|
|
},
|
|
}),
|
|
}))
|
|
|
|
ctx.ProjectPersistenceManager = (await import(modulePath)).default
|
|
ctx.callback = sinon.stub()
|
|
ctx.project_id = 'project-id-123'
|
|
return (ctx.user_id = '1234')
|
|
})
|
|
|
|
describe('refreshExpiryTimeout', () => {
|
|
it('should leave expiry alone if plenty of disk', async ctx => {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.fsPromises.statfs.resolves({
|
|
blocks: 100,
|
|
bsize: 1,
|
|
bavail: 40,
|
|
})
|
|
|
|
ctx.ProjectPersistenceManager.refreshExpiryTimeout(() => {
|
|
ctx.Metrics.gauge.should.have.been.calledWith(
|
|
'disk_available_percent',
|
|
40
|
|
)
|
|
ctx.ProjectPersistenceManager.EXPIRY_TIMEOUT.should.equal(
|
|
ctx.settings.project_cache_length_ms
|
|
)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should drop EXPIRY_TIMEOUT 10% if low disk usage', async ctx => {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.fsPromises.statfs.resolves({
|
|
blocks: 100,
|
|
bsize: 1,
|
|
bavail: 5,
|
|
})
|
|
|
|
ctx.ProjectPersistenceManager.refreshExpiryTimeout(() => {
|
|
ctx.Metrics.gauge.should.have.been.calledWith(
|
|
'disk_available_percent',
|
|
5
|
|
)
|
|
ctx.ProjectPersistenceManager.EXPIRY_TIMEOUT.should.equal(900)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should not drop EXPIRY_TIMEOUT to below 50% of project_cache_length_ms', async ctx => {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.fsPromises.statfs.resolves({
|
|
blocks: 100,
|
|
bsize: 1,
|
|
bavail: 5,
|
|
})
|
|
ctx.ProjectPersistenceManager.EXPIRY_TIMEOUT = 500
|
|
ctx.ProjectPersistenceManager.refreshExpiryTimeout(() => {
|
|
ctx.Metrics.gauge.should.have.been.calledWith(
|
|
'disk_available_percent',
|
|
5
|
|
)
|
|
ctx.ProjectPersistenceManager.EXPIRY_TIMEOUT.should.equal(500)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should not modify EXPIRY_TIMEOUT if there is an error getting disk values', async ctx => {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.fsPromises.statfs.rejects(new Error())
|
|
ctx.ProjectPersistenceManager.refreshExpiryTimeout(() => {
|
|
ctx.ProjectPersistenceManager.EXPIRY_TIMEOUT.should.equal(1000)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('clearExpiredProjects', () => {
|
|
beforeEach(ctx => {
|
|
ctx.project_ids = ['project-id-1', 'project-id-2']
|
|
ctx.ProjectPersistenceManager._findExpiredProjectIds = sinon
|
|
.stub()
|
|
.callsArgWith(0, null, ctx.project_ids)
|
|
ctx.ProjectPersistenceManager.clearProjectFromCache = sinon
|
|
.stub()
|
|
.callsArg(2)
|
|
ctx.CompileManager.clearExpiredProjects = sinon.stub().callsArg(1)
|
|
return ctx.ProjectPersistenceManager.clearExpiredProjects(ctx.callback)
|
|
})
|
|
|
|
it('should clear each expired project', ctx => {
|
|
return Array.from(ctx.project_ids).map(projectId =>
|
|
ctx.ProjectPersistenceManager.clearProjectFromCache
|
|
.calledWith(projectId)
|
|
.should.equal(true)
|
|
)
|
|
})
|
|
|
|
return it('should call the callback', ctx => {
|
|
return ctx.callback.called.should.equal(true)
|
|
})
|
|
})
|
|
|
|
return describe('clearProject', () => {
|
|
beforeEach(ctx => {
|
|
ctx.ProjectPersistenceManager._clearProjectFromDatabase = sinon
|
|
.stub()
|
|
.callsArg(1)
|
|
ctx.UrlCache.clearProject = sinon.stub().callsArg(2)
|
|
ctx.CompileManager.clearProject = sinon.stub().callsArg(2)
|
|
return ctx.ProjectPersistenceManager.clearProject(
|
|
ctx.project_id,
|
|
ctx.user_id,
|
|
ctx.callback
|
|
)
|
|
})
|
|
|
|
it('should clear the project from the database', ctx => {
|
|
return ctx.ProjectPersistenceManager._clearProjectFromDatabase
|
|
.calledWith(ctx.project_id)
|
|
.should.equal(true)
|
|
})
|
|
|
|
it('should clear the history cache', ctx => {
|
|
ctx.HistoryResourceWriter.clearCacheCb.should.have.been.calledWith(
|
|
ctx.project_id,
|
|
ctx.user_id
|
|
)
|
|
})
|
|
|
|
it('should clear all the cached Urls for the project', ctx => {
|
|
return ctx.UrlCache.clearProject
|
|
.calledWith(ctx.project_id)
|
|
.should.equal(true)
|
|
})
|
|
|
|
it('should clear the project compile folder', ctx => {
|
|
return ctx.CompileManager.clearProject
|
|
.calledWith(ctx.project_id, ctx.user_id)
|
|
.should.equal(true)
|
|
})
|
|
|
|
return it('should call the callback', ctx => {
|
|
return ctx.callback.called.should.equal(true)
|
|
})
|
|
})
|
|
})
|