mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 09:09:36 +02:00
230 lines
6.4 KiB
JavaScript
230 lines
6.4 KiB
JavaScript
import { vi, expect, describe, beforeEach, it } from 'vitest'
|
|
|
|
import sinon from 'sinon'
|
|
import Path from 'node:path'
|
|
import * as Errors from '../../../app/js/Errors.js'
|
|
|
|
const modulePath = Path.join(
|
|
import.meta.dirname,
|
|
'../../../app/js/ResourceStateManager'
|
|
)
|
|
|
|
describe('ResourceStateManager', () => {
|
|
beforeEach(async ctx => {
|
|
vi.doMock('fs', () => ({
|
|
default: (ctx.fs = {}),
|
|
}))
|
|
|
|
vi.doMock('../../../app/js/SafeReader', () => ({
|
|
default: (ctx.SafeReader = {}),
|
|
}))
|
|
|
|
ctx.ResourceStateManager = (await import(modulePath)).default
|
|
ctx.basePath = '/path/to/write/files/to'
|
|
ctx.resources = [
|
|
{ path: 'resource-1-mock' },
|
|
{ path: 'resource-2-mock' },
|
|
{ path: 'resource-3-mock' },
|
|
]
|
|
ctx.state = '1234567890'
|
|
ctx.resourceFileName = `${ctx.basePath}/.project-sync-state`
|
|
ctx.resourceFileContents = `${ctx.resources[0].path}\n${ctx.resources[1].path}\n${ctx.resources[2].path}\nstateHash:${ctx.state}`
|
|
ctx.callback = sinon.stub()
|
|
})
|
|
|
|
describe('saveProjectState', () => {
|
|
beforeEach(ctx => {
|
|
ctx.fs.writeFile = sinon.stub().callsArg(2)
|
|
})
|
|
|
|
describe('when the state is specified', () => {
|
|
beforeEach(ctx => {
|
|
ctx.ResourceStateManager.saveProjectState(
|
|
ctx.state,
|
|
ctx.resources,
|
|
ctx.basePath,
|
|
ctx.callback
|
|
)
|
|
})
|
|
|
|
it('should write the resource list to disk', ctx => {
|
|
ctx.fs.writeFile
|
|
.calledWith(ctx.resourceFileName, ctx.resourceFileContents)
|
|
.should.equal(true)
|
|
})
|
|
|
|
it('should call the callback', ctx => {
|
|
ctx.callback.called.should.equal(true)
|
|
})
|
|
})
|
|
|
|
describe('when the state is undefined', () => {
|
|
beforeEach(ctx => {
|
|
ctx.state = undefined
|
|
ctx.fs.unlink = sinon.stub().callsArg(1)
|
|
ctx.ResourceStateManager.saveProjectState(
|
|
ctx.state,
|
|
ctx.resources,
|
|
ctx.basePath,
|
|
ctx.callback
|
|
)
|
|
})
|
|
|
|
it('should unlink the resource file', ctx => {
|
|
ctx.fs.unlink.calledWith(ctx.resourceFileName).should.equal(true)
|
|
})
|
|
|
|
it('should not write the resource list to disk', ctx => {
|
|
ctx.fs.writeFile.called.should.equal(false)
|
|
})
|
|
|
|
it('should call the callback', ctx => {
|
|
ctx.callback.called.should.equal(true)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('checkProjectStateMatches', () => {
|
|
describe('when the state matches', () => {
|
|
beforeEach(ctx => {
|
|
ctx.SafeReader.readFile = sinon
|
|
.stub()
|
|
.callsArgWith(3, null, ctx.resourceFileContents)
|
|
ctx.ResourceStateManager.checkProjectStateMatches(
|
|
ctx.state,
|
|
ctx.basePath,
|
|
ctx.callback
|
|
)
|
|
})
|
|
|
|
it('should read the resource file', ctx => {
|
|
ctx.SafeReader.readFile
|
|
.calledWith(ctx.resourceFileName)
|
|
.should.equal(true)
|
|
})
|
|
|
|
it('should call the callback with the results', ctx => {
|
|
ctx.callback.calledWithMatch(null, ctx.resources).should.equal(true)
|
|
})
|
|
})
|
|
|
|
describe('when the state file is not present', () => {
|
|
beforeEach(ctx => {
|
|
ctx.SafeReader.readFile = sinon.stub().callsArg(3)
|
|
ctx.ResourceStateManager.checkProjectStateMatches(
|
|
ctx.state,
|
|
ctx.basePath,
|
|
ctx.callback
|
|
)
|
|
})
|
|
|
|
it('should read the resource file', ctx => {
|
|
ctx.SafeReader.readFile
|
|
.calledWith(ctx.resourceFileName)
|
|
.should.equal(true)
|
|
})
|
|
|
|
it('should call the callback with an error', ctx => {
|
|
ctx.callback
|
|
.calledWith(sinon.match(Errors.FilesOutOfSyncError))
|
|
.should.equal(true)
|
|
|
|
const message = ctx.callback.args[0][0].message
|
|
expect(message).to.include('invalid state for incremental update')
|
|
})
|
|
})
|
|
|
|
describe('when the state does not match', () => {
|
|
beforeEach(ctx => {
|
|
ctx.SafeReader.readFile = sinon
|
|
.stub()
|
|
.callsArgWith(3, null, ctx.resourceFileContents)
|
|
ctx.ResourceStateManager.checkProjectStateMatches(
|
|
'not-the-original-state',
|
|
ctx.basePath,
|
|
ctx.callback
|
|
)
|
|
})
|
|
|
|
it('should call the callback with an error', ctx => {
|
|
ctx.callback
|
|
.calledWith(sinon.match(Errors.FilesOutOfSyncError))
|
|
.should.equal(true)
|
|
|
|
const message = ctx.callback.args[0][0].message
|
|
expect(message).to.include('invalid state for incremental update')
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('checkResourceFiles', () => {
|
|
describe('when all the files are present', () => {
|
|
beforeEach(ctx => {
|
|
ctx.allFiles = [
|
|
ctx.resources[0].path,
|
|
ctx.resources[1].path,
|
|
ctx.resources[2].path,
|
|
]
|
|
ctx.ResourceStateManager.checkResourceFiles(
|
|
ctx.resources,
|
|
ctx.allFiles,
|
|
ctx.basePath,
|
|
ctx.callback
|
|
)
|
|
})
|
|
|
|
it('should call the callback', ctx => {
|
|
ctx.callback.calledWithExactly().should.equal(true)
|
|
})
|
|
})
|
|
|
|
describe('when there is a missing file', () => {
|
|
beforeEach(ctx => {
|
|
ctx.allFiles = [ctx.resources[0].path, ctx.resources[1].path]
|
|
ctx.fs.stat = sinon.stub().callsArgWith(1, new Error())
|
|
ctx.ResourceStateManager.checkResourceFiles(
|
|
ctx.resources,
|
|
ctx.allFiles,
|
|
ctx.basePath,
|
|
ctx.callback
|
|
)
|
|
})
|
|
|
|
it('should call the callback with an error', ctx => {
|
|
ctx.callback
|
|
.calledWith(sinon.match(Errors.FilesOutOfSyncError))
|
|
.should.equal(true)
|
|
|
|
const message = ctx.callback.args[0][0].message
|
|
expect(message).to.include(
|
|
'resource files missing in incremental update'
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('when a resource contains a relative path', () => {
|
|
beforeEach(ctx => {
|
|
ctx.resources[0].path = '../foo/bar.tex'
|
|
ctx.allFiles = [
|
|
ctx.resources[0].path,
|
|
ctx.resources[1].path,
|
|
ctx.resources[2].path,
|
|
]
|
|
ctx.ResourceStateManager.checkResourceFiles(
|
|
ctx.resources,
|
|
ctx.allFiles,
|
|
ctx.basePath,
|
|
ctx.callback
|
|
)
|
|
})
|
|
|
|
it('should call the callback with an error', ctx => {
|
|
ctx.callback.calledWith(sinon.match(Error)).should.equal(true)
|
|
|
|
const message = ctx.callback.args[0][0].message
|
|
expect(message).to.include('relative path in resource file list')
|
|
})
|
|
})
|
|
})
|
|
})
|