mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-11 23:20:47 +02:00
7c70b749d4
* [monorepo] remove PII and variables from error messages Exclusions: - scripts - tests - fuzzing - SplitTestManager (messages are sent to admin frontend) - Group setup (we may want an error per unique tuple) - sharejs (unused types; text type errors are shadowed already) - history-v1 error messages that are used by the ErrorRecorder - errors that flag issues with configuration/call signatures I've used these search terms for finding unwanted error messages: - new Error(` - new Error\(\n\s+` (regex search) - new OError(` - new OError\(\n\s+` (regex search) * [web] throw NotFoundError from ProjectLocator * [github-sync] fix OError.tag call in script Co-authored-by: Jessica Lawshe <jessica.lawshe@overleaf.com> * [templates] revert changes to test client --------- Co-authored-by: Jessica Lawshe <jessica.lawshe@overleaf.com> GitOrigin-RevId: 736857a4fc5d9bfb0f8cb03e0f004eda87e5a220
626 lines
17 KiB
JavaScript
626 lines
17 KiB
JavaScript
import { assert, beforeAll, beforeEach, describe, it, vi, expect } from 'vitest'
|
|
import Errors from '../../../../app/src/Features/Errors/Errors.js'
|
|
import tk from 'timekeeper'
|
|
import { RequestFailedError } from '@overleaf/fetch-utils'
|
|
|
|
const modulePath = '../../../../app/src/Features/Docstore/DocstoreManager'
|
|
|
|
vi.mock('../../../../app/src/Features/Errors/Errors.js', () =>
|
|
vi.importActual('../../../../app/src/Features/Errors/Errors.js')
|
|
)
|
|
|
|
describe('DocstoreManager', function () {
|
|
let DocstoreManager, FetchUtils, projectId, docId, settings
|
|
|
|
beforeEach(async function () {
|
|
settings = {
|
|
apis: {
|
|
docstore: {
|
|
url: 'http://docstore.overleaf.com',
|
|
},
|
|
},
|
|
}
|
|
|
|
vi.doMock('@overleaf/settings', () => ({
|
|
default: settings,
|
|
}))
|
|
|
|
FetchUtils = {
|
|
fetchNothing: vi.fn().mockResolvedValue(),
|
|
fetchJson: vi.fn().mockResolvedValue({}),
|
|
RequestFailedError,
|
|
}
|
|
|
|
vi.doMock('@overleaf/fetch-utils', () => FetchUtils)
|
|
|
|
DocstoreManager = (await import(modulePath)).default
|
|
|
|
projectId = 'project-id-123'
|
|
docId = 'doc-id-123'
|
|
})
|
|
|
|
describe('deleteDoc', function () {
|
|
describe('with a successful response code', function () {
|
|
// for assertions on the deletedAt timestamp, we need to freeze the clock.
|
|
beforeAll(function () {
|
|
tk.freeze(Date.now())
|
|
})
|
|
afterAll(function () {
|
|
tk.reset()
|
|
})
|
|
|
|
beforeEach(async function () {
|
|
await DocstoreManager.promises.deleteDoc(
|
|
projectId,
|
|
docId,
|
|
'wombat.tex',
|
|
new Date()
|
|
)
|
|
})
|
|
|
|
it('should delete the doc in the docstore api', function () {
|
|
const url = new URL(settings.apis.docstore.url)
|
|
url.pathname = `/project/${projectId}/doc/${docId}`
|
|
expect(FetchUtils.fetchNothing).toHaveBeenCalledWith(url, {
|
|
json: { deleted: true, deletedAt: new Date(), name: 'wombat.tex' },
|
|
signal: expect.anything(),
|
|
method: 'PATCH',
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with a failed response code', function () {
|
|
beforeEach(function () {
|
|
FetchUtils.fetchNothing.mockImplementation((url, opts) => {
|
|
throw new RequestFailedError(url, opts, { status: 500 })
|
|
})
|
|
})
|
|
|
|
it('should reject with an error', async function () {
|
|
let error
|
|
|
|
try {
|
|
await DocstoreManager.promises.deleteDoc(
|
|
projectId,
|
|
docId,
|
|
'main.tex',
|
|
new Date()
|
|
)
|
|
} catch (err) {
|
|
error = err
|
|
}
|
|
|
|
expect(error).to.be.instanceOf(Error)
|
|
expect(error).to.have.property(
|
|
'message',
|
|
'docstore api responded with non-success code'
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('with a missing (404) response code', function () {
|
|
beforeEach(function () {
|
|
FetchUtils.fetchNothing.mockImplementation((url, opts) => {
|
|
throw new RequestFailedError(url, opts, { status: 404 })
|
|
})
|
|
})
|
|
|
|
it('should reject with an error', async function () {
|
|
let error
|
|
try {
|
|
await DocstoreManager.promises.deleteDoc(
|
|
projectId,
|
|
docId,
|
|
'main.tex',
|
|
new Date()
|
|
)
|
|
} catch (err) {
|
|
error = err
|
|
}
|
|
|
|
expect(error).to.be.instanceOf(Error)
|
|
expect(error).to.have.property(
|
|
'message',
|
|
'tried to delete doc not in docstore'
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('updateDoc', function () {
|
|
let lines, modified, ranges, rev, updateDocResponse, version
|
|
beforeEach(function () {
|
|
lines = ['mock', 'doc', 'lines']
|
|
rev = 5
|
|
version = 42
|
|
ranges = { mock: 'ranges' }
|
|
modified = true
|
|
})
|
|
|
|
describe('with a successful response code', async function () {
|
|
beforeEach(async function () {
|
|
FetchUtils.fetchJson.mockResolvedValue({
|
|
modified,
|
|
rev,
|
|
})
|
|
updateDocResponse = await DocstoreManager.promises.updateDoc(
|
|
projectId,
|
|
docId,
|
|
lines,
|
|
version,
|
|
ranges
|
|
)
|
|
})
|
|
|
|
it('should update the doc in the docstore api', function () {
|
|
expect(FetchUtils.fetchJson).toHaveBeenCalledWith(
|
|
new URL(
|
|
`${settings.apis.docstore.url}/project/${projectId}/doc/${docId}`
|
|
),
|
|
{
|
|
signal: expect.anything(),
|
|
method: 'POST',
|
|
json: {
|
|
lines,
|
|
version,
|
|
ranges,
|
|
},
|
|
}
|
|
)
|
|
})
|
|
|
|
it('should return the modified status and revision', function () {
|
|
expect(updateDocResponse).to.haveOwnProperty('modified', modified)
|
|
expect(updateDocResponse).to.haveOwnProperty('rev', rev)
|
|
})
|
|
})
|
|
|
|
describe('with a failed response code', function () {
|
|
beforeEach(function () {
|
|
FetchUtils.fetchJson.mockImplementation((url, opts) => {
|
|
throw new RequestFailedError(url, opts, { status: 500 })
|
|
})
|
|
})
|
|
|
|
it('should reject with an error', async function () {
|
|
let error
|
|
|
|
try {
|
|
await DocstoreManager.promises.updateDoc(
|
|
projectId,
|
|
docId,
|
|
lines,
|
|
version,
|
|
ranges
|
|
)
|
|
assert.fail('updateDoc should have thrown an error')
|
|
} catch (err) {
|
|
error = err
|
|
}
|
|
|
|
expect(error).to.be.instanceOf(Error)
|
|
expect(error).to.have.property(
|
|
'message',
|
|
'docstore api responded with non-success code'
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('getDoc', function () {
|
|
let doc, getDocResponse, lines, ranges, rev, version
|
|
beforeEach(function () {
|
|
lines = ['mock', 'doc', 'lines']
|
|
rev = 5
|
|
version = 42
|
|
ranges = { mock: 'ranges' }
|
|
doc = {
|
|
lines,
|
|
rev,
|
|
version,
|
|
ranges,
|
|
}
|
|
})
|
|
|
|
describe('with a successful response code', function () {
|
|
beforeEach(async function () {
|
|
FetchUtils.fetchJson.mockResolvedValue(doc)
|
|
getDocResponse = await DocstoreManager.promises.getDoc(projectId, docId)
|
|
})
|
|
|
|
it('should get the doc from the docstore api', function () {
|
|
expect(FetchUtils.fetchJson).toHaveBeenCalledWith(
|
|
new URL(
|
|
`${settings.apis.docstore.url}/project/${projectId}/doc/${docId}`
|
|
),
|
|
{
|
|
signal: expect.anything(),
|
|
}
|
|
)
|
|
})
|
|
|
|
it('should resolve with the lines, version and rev', function () {
|
|
expect(getDocResponse).to.eql({
|
|
lines,
|
|
rev,
|
|
version,
|
|
ranges,
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with a failed response code', function () {
|
|
beforeEach(function () {
|
|
FetchUtils.fetchJson.mockImplementation((url, opts) => {
|
|
throw new RequestFailedError(url, opts, { status: 500 })
|
|
})
|
|
})
|
|
|
|
it('should reject with an error', async function () {
|
|
let error
|
|
|
|
try {
|
|
await DocstoreManager.promises.getDoc(projectId, docId)
|
|
assert.fail('getDoc should have thrown an error')
|
|
} catch (err) {
|
|
error = err
|
|
}
|
|
|
|
expect(error).to.be.instanceOf(Error)
|
|
expect(error).to.have.property(
|
|
'message',
|
|
'docstore api responded with non-success code'
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('with include_deleted=true', function () {
|
|
beforeEach(async function () {
|
|
FetchUtils.fetchJson.mockResolvedValue(doc)
|
|
getDocResponse = await DocstoreManager.promises.getDoc(
|
|
projectId,
|
|
docId,
|
|
{ include_deleted: true }
|
|
)
|
|
})
|
|
|
|
it('should get the doc from the docstore api (including deleted)', function () {
|
|
expect(FetchUtils.fetchJson).toHaveBeenCalledWith(
|
|
new URL(
|
|
`${settings.apis.docstore.url}/project/${projectId}/doc/${docId}?include_deleted=true`
|
|
),
|
|
{
|
|
signal: expect.anything(),
|
|
}
|
|
)
|
|
})
|
|
|
|
it('should resolve with the lines, version and rev', function () {
|
|
expect(getDocResponse).to.eql({
|
|
lines,
|
|
rev,
|
|
version,
|
|
ranges,
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with peek=true', function () {
|
|
beforeEach(async function () {
|
|
await DocstoreManager.promises.getDoc(projectId, docId, {
|
|
peek: true,
|
|
})
|
|
})
|
|
|
|
it('should call the docstore peek url', function () {
|
|
expect(FetchUtils.fetchJson).toHaveBeenCalledWith(
|
|
new URL(
|
|
`${settings.apis.docstore.url}/project/${projectId}/doc/${docId}/peek`
|
|
),
|
|
{
|
|
signal: expect.anything(),
|
|
}
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('with a missing (404) response code', function () {
|
|
beforeEach(function () {
|
|
FetchUtils.fetchJson.mockImplementation((url, opts) => {
|
|
throw new RequestFailedError(url, opts, { status: 404 })
|
|
})
|
|
})
|
|
|
|
it('should reject with an error', async function () {
|
|
let error
|
|
|
|
try {
|
|
await DocstoreManager.promises.getDoc(projectId, docId)
|
|
assert.fail('getDoc should have thrown an error')
|
|
} catch (err) {
|
|
error = err
|
|
}
|
|
|
|
expect(error).to.be.instanceOf(Errors.NotFoundError)
|
|
expect(error).to.have.property('message', 'doc not found in docstore')
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('getAllDocs', function () {
|
|
describe('with a successful response code', function () {
|
|
let getAllDocsResult
|
|
let docs
|
|
beforeEach(async function () {
|
|
docs = [{ _id: 'mock-doc-id' }]
|
|
FetchUtils.fetchJson.mockResolvedValue(docs)
|
|
getAllDocsResult = await DocstoreManager.promises.getAllDocs(projectId)
|
|
})
|
|
|
|
it('should get all the project docs in the docstore api', function () {
|
|
expect(FetchUtils.fetchJson).toBeCalledWith(
|
|
new URL(`${settings.apis.docstore.url}/project/${projectId}/doc`),
|
|
{
|
|
signal: expect.anything(),
|
|
}
|
|
)
|
|
})
|
|
|
|
it('should return the docs', function () {
|
|
expect(getAllDocsResult).to.eql(docs)
|
|
})
|
|
})
|
|
|
|
describe('with a failed response code', function () {
|
|
beforeEach(function () {
|
|
FetchUtils.fetchJson.mockImplementation((url, opts) => {
|
|
throw new RequestFailedError(url, opts, { status: 500 })
|
|
})
|
|
})
|
|
|
|
it('should reject with an error', async function () {
|
|
let error
|
|
|
|
try {
|
|
await DocstoreManager.promises.getAllDocs(projectId)
|
|
assert.fail('getAllDocs should have thrown an error')
|
|
} catch (err) {
|
|
error = err
|
|
}
|
|
|
|
expect(error).to.be.instanceOf(Error)
|
|
expect(error).to.have.property(
|
|
'message',
|
|
'docstore api responded with non-success code'
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('getAllDeletedDocs', function () {
|
|
describe('with a successful response code', function () {
|
|
let getAllDeletedDocsResponse
|
|
let docs
|
|
beforeEach(async function () {
|
|
docs = [{ _id: 'mock-doc-id', name: 'foo.tex' }]
|
|
FetchUtils.fetchJson.mockResolvedValue(docs)
|
|
getAllDeletedDocsResponse =
|
|
await DocstoreManager.promises.getAllDeletedDocs(projectId)
|
|
})
|
|
|
|
it('should get all the project docs in the docstore api', function () {
|
|
expect(FetchUtils.fetchJson).toHaveBeenCalledWith(
|
|
new URL(
|
|
`${settings.apis.docstore.url}/project/${projectId}/doc-deleted`
|
|
),
|
|
{
|
|
signal: expect.anything(),
|
|
}
|
|
)
|
|
})
|
|
|
|
it('should resolve with the docs', function () {
|
|
expect(getAllDeletedDocsResponse).to.eql(docs)
|
|
})
|
|
})
|
|
|
|
describe('with an error', function () {
|
|
beforeEach(async function () {
|
|
FetchUtils.fetchJson.mockRejectedValue(new Error('connect failed'))
|
|
})
|
|
|
|
it('should reject with an error', async function () {
|
|
let error
|
|
|
|
try {
|
|
await DocstoreManager.promises.getAllDeletedDocs(projectId)
|
|
assert.fail('getAllDeletedDocs should have thrown an error')
|
|
} catch (err) {
|
|
error = err
|
|
}
|
|
|
|
expect(error).to.be.instanceOf(Error)
|
|
expect(error).to.have.property('message', 'connect failed')
|
|
})
|
|
})
|
|
|
|
describe('with a failed response code', function () {
|
|
beforeEach(function () {
|
|
FetchUtils.fetchJson.mockImplementation((url, opts) => {
|
|
throw new RequestFailedError(url, opts, { status: 500 })
|
|
})
|
|
})
|
|
|
|
it('should reject with an error', async function () {
|
|
let error
|
|
|
|
try {
|
|
await DocstoreManager.promises.getAllDeletedDocs(projectId)
|
|
assert.fail('getAllDeletedDocs should have thrown an error')
|
|
} catch (err) {
|
|
error = err
|
|
}
|
|
|
|
expect(error).to.be.instanceOf(Error)
|
|
expect(error).to.have.property(
|
|
'message',
|
|
'docstore api responded with non-success code'
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('getAllRanges', function () {
|
|
describe('with a successful response code', function () {
|
|
let getAllRangesResult
|
|
let docs
|
|
beforeEach(async function () {
|
|
docs = [{ _id: 'mock-doc-id', ranges: 'mock-ranges' }]
|
|
FetchUtils.fetchJson.mockResolvedValue(docs)
|
|
getAllRangesResult =
|
|
await DocstoreManager.promises.getAllRanges(projectId)
|
|
})
|
|
|
|
it('should get all the project doc ranges in the docstore api', function () {
|
|
expect(FetchUtils.fetchJson).toHaveBeenCalledWith(
|
|
new URL(`${settings.apis.docstore.url}/project/${projectId}/ranges`),
|
|
{
|
|
signal: expect.anything(),
|
|
}
|
|
)
|
|
})
|
|
|
|
it('should return the docs', async function () {
|
|
expect(getAllRangesResult).to.eql(docs)
|
|
})
|
|
})
|
|
|
|
describe('with a failed response code', function () {
|
|
beforeEach(function () {
|
|
FetchUtils.fetchJson.mockImplementation((url, opts) => {
|
|
throw new RequestFailedError(url, opts, { status: 500 })
|
|
})
|
|
})
|
|
|
|
it('should reject with an error', async function () {
|
|
let error
|
|
|
|
try {
|
|
await DocstoreManager.promises.getAllRanges(projectId)
|
|
assert.fail('getAllRanges should have thrown an error')
|
|
} catch (err) {
|
|
error = err
|
|
}
|
|
|
|
expect(error).to.be.instanceOf(Error)
|
|
expect(error).to.have.property(
|
|
'message',
|
|
'docstore api responded with non-success code'
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('archiveProject', function () {
|
|
describe('with a successful response code', function () {
|
|
it('should resolve', async function () {
|
|
await expect(DocstoreManager.promises.archiveProject(projectId)).to
|
|
.eventually.be.fulfilled
|
|
})
|
|
})
|
|
|
|
describe('with a failed response code', function () {
|
|
beforeEach(function () {
|
|
FetchUtils.fetchNothing.mockImplementation((url, opts) => {
|
|
throw new RequestFailedError(url, opts, { status: 500 })
|
|
})
|
|
})
|
|
|
|
it('should reject with an error', async function () {
|
|
let error
|
|
|
|
try {
|
|
await DocstoreManager.promises.archiveProject(projectId)
|
|
assert.fail('archiveProject should have thrown an error')
|
|
} catch (err) {
|
|
error = err
|
|
}
|
|
|
|
expect(error).to.be.instanceOf(Error)
|
|
expect(error).to.have.property(
|
|
'message',
|
|
'docstore api responded with non-success code'
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('unarchiveProject', function () {
|
|
describe('with a successful response code', function () {
|
|
it('should resolve', async function () {
|
|
await expect(DocstoreManager.promises.unarchiveProject(projectId)).to
|
|
.eventually.be.fulfilled
|
|
})
|
|
})
|
|
|
|
describe('with a failed response code', function () {
|
|
beforeEach(function () {
|
|
FetchUtils.fetchNothing.mockImplementation((url, opts) => {
|
|
throw new RequestFailedError(url, opts, { status: 500 })
|
|
})
|
|
})
|
|
|
|
it('should reject with an error', async function () {
|
|
let error
|
|
|
|
try {
|
|
await DocstoreManager.promises.unarchiveProject(projectId)
|
|
assert.fail('unarchiveProject should have thrown an error')
|
|
} catch (err) {
|
|
error = err
|
|
}
|
|
|
|
expect(error).to.be.instanceOf(Error)
|
|
expect(error).to.have.property(
|
|
'message',
|
|
'docstore api responded with non-success code'
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('destroyProject', function () {
|
|
describe('with a successful response code', function () {
|
|
it('should resolve', async function () {
|
|
await expect(DocstoreManager.promises.destroyProject(projectId)).to
|
|
.eventually.be.fulfilled
|
|
})
|
|
})
|
|
|
|
describe('with a failed response code', function () {
|
|
beforeEach(function () {
|
|
FetchUtils.fetchNothing.mockImplementation((url, opts) => {
|
|
throw new RequestFailedError(url, opts, { status: 500 })
|
|
})
|
|
})
|
|
|
|
it('should reject with an error', async function () {
|
|
let error
|
|
|
|
try {
|
|
await DocstoreManager.promises.destroyProject(projectId)
|
|
assert.fail('destroyProject should have thrown an error')
|
|
} catch (err) {
|
|
error = err
|
|
}
|
|
|
|
expect(error).to.be.instanceOf(Error)
|
|
expect(error).to.have.property(
|
|
'message',
|
|
'docstore api responded with non-success code'
|
|
)
|
|
})
|
|
})
|
|
})
|
|
})
|