overleaf/services/web/test/unit/src/Documents/DocumentControllerTests.js
Eric Mc Sween 9f4541c266 Merge pull request #4761 from overleaf/em-peek-doc
Do not unarchive docs when resyncing project history

GitOrigin-RevId: c7df75789c01e6c85b464a9b94b14654d8568407
2021-08-20 08:03:50 +00:00

268 lines
8.9 KiB
JavaScript

const sinon = require('sinon')
const modulePath =
'../../../../app/src/Features/Documents/DocumentController.js'
const SandboxedModule = require('sandboxed-module')
const MockRequest = require('../helpers/MockRequest')
const MockResponse = require('../helpers/MockResponse')
const Errors = require('../../../../app/src/Features/Errors/Errors')
describe('DocumentController', function () {
beforeEach(function () {
this.DocumentController = SandboxedModule.require(modulePath, {
requires: {
'../Project/ProjectGetter': (this.ProjectGetter = {}),
'../Project/ProjectLocator': (this.ProjectLocator = {}),
'../Project/ProjectEntityHandler': (this.ProjectEntityHandler = {}),
'../Project/ProjectEntityUpdateHandler': (this.ProjectEntityUpdateHandler = {}),
},
})
this.res = new MockResponse()
this.req = new MockRequest()
this.next = sinon.stub()
this.project_id = 'project-id-123'
this.doc_id = 'doc-id-123'
this.doc_lines = ['one', 'two', 'three']
this.version = 42
this.ranges = { mock: 'ranges' }
this.pathname = '/a/b/c/file.tex'
this.lastUpdatedAt = new Date().getTime()
this.lastUpdatedBy = 'fake-last-updater-id'
this.rev = 5
})
describe('getDocument', function () {
beforeEach(function () {
this.req.params = {
Project_id: this.project_id,
doc_id: this.doc_id,
}
})
describe('when the project exists without project history enabled', function () {
beforeEach(function () {
this.project = { _id: this.project_id }
this.ProjectGetter.getProject = sinon
.stub()
.callsArgWith(2, null, this.project)
})
describe('when the document exists', function () {
beforeEach(function () {
this.doc = { _id: this.doc_id }
this.ProjectLocator.findElement = sinon
.stub()
.callsArgWith(1, null, this.doc, { fileSystem: this.pathname })
this.ProjectEntityHandler.getDoc = sinon
.stub()
.yields(null, this.doc_lines, this.rev, this.version, this.ranges)
this.DocumentController.getDocument(this.req, this.res, this.next)
})
it('should get the project', function () {
this.ProjectGetter.getProject
.calledWith(this.project_id, { rootFolder: true, overleaf: true })
.should.equal(true)
})
it('should get the pathname of the document', function () {
this.ProjectLocator.findElement
.calledWith({
project: this.project,
element_id: this.doc_id,
type: 'doc',
})
.should.equal(true)
})
it('should get the document content', function () {
this.ProjectEntityHandler.getDoc
.calledWith(this.project_id, this.doc_id)
.should.equal(true)
})
it('should return the document data to the client as JSON', function () {
this.res.type.should.equal('application/json')
this.res.body.should.equal(
JSON.stringify({
lines: this.doc_lines,
version: this.version,
ranges: this.ranges,
pathname: this.pathname,
})
)
})
})
describe("when the document doesn't exist", function () {
beforeEach(function () {
this.ProjectLocator.findElement = sinon
.stub()
.callsArgWith(1, new Errors.NotFoundError('not found'))
this.DocumentController.getDocument(this.req, this.res, this.next)
})
it('should call next with the NotFoundError', function () {
this.next
.calledWith(sinon.match.instanceOf(Errors.NotFoundError))
.should.equal(true)
})
})
})
describe('when project exists with project history enabled', function () {
beforeEach(function () {
this.doc = { _id: this.doc_id }
this.projectHistoryId = 1234
this.projectHistoryDisplay = true
this.projectHistoryType = 'project-history'
this.project = {
_id: this.project_id,
overleaf: {
history: {
id: this.projectHistoryId,
display: this.projectHistoryDisplay,
},
},
}
this.ProjectGetter.getProject = sinon
.stub()
.callsArgWith(2, null, this.project)
this.ProjectLocator.findElement = sinon
.stub()
.callsArgWith(1, null, this.doc, { fileSystem: this.pathname })
this.ProjectEntityHandler.getDoc = sinon
.stub()
.yields(null, this.doc_lines, this.rev, this.version, this.ranges)
this.DocumentController.getDocument(this.req, this.res, this.next)
})
it('should return the history id and display setting to the client as JSON', function () {
this.res.type.should.equal('application/json')
this.res.body.should.equal(
JSON.stringify({
lines: this.doc_lines,
version: this.version,
ranges: this.ranges,
pathname: this.pathname,
projectHistoryId: this.projectHistoryId,
projectHistoryType: this.projectHistoryType,
})
)
})
})
describe('when project exists that was migrated with downgrades allowed', function () {
beforeEach(function () {
this.doc = { _id: this.doc_id }
this.projectHistoryId = 1234
this.projectHistoryDisplay = true
this.projectHistoryType = undefined
this.project = {
_id: this.project_id,
overleaf: {
history: {
id: this.projectHistoryId,
display: this.projectHistoryDisplay,
allowDowngrade: true,
},
},
}
this.ProjectGetter.getProject = sinon
.stub()
.callsArgWith(2, null, this.project)
this.ProjectLocator.findElement = sinon
.stub()
.callsArgWith(1, null, this.doc, { fileSystem: this.pathname })
this.ProjectEntityHandler.getDoc = sinon
.stub()
.yields(null, this.doc_lines, this.rev, this.version, this.ranges)
return this.DocumentController.getDocument(
this.req,
this.res,
this.next
)
})
it('should return the history id in the JSON but not history type, sending history to both services', function () {
this.res.type.should.equal('application/json')
return this.res.body.should.equal(
JSON.stringify({
lines: this.doc_lines,
version: this.version,
ranges: this.ranges,
pathname: this.pathname,
projectHistoryId: this.projectHistoryId,
projectHistoryType: this.projectHistoryType,
})
)
})
})
describe('when the project does not exist', function () {
beforeEach(function () {
this.ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, null)
this.DocumentController.getDocument(this.req, this.res, this.next)
})
it('returns a 404', function () {
this.res.statusCode.should.equal(404)
})
})
})
describe('setDocument', function () {
beforeEach(function () {
this.req.params = {
Project_id: this.project_id,
doc_id: this.doc_id,
}
})
describe('when the document exists', function () {
beforeEach(function () {
this.ProjectEntityUpdateHandler.updateDocLines = sinon.stub().yields()
this.req.body = {
lines: this.doc_lines,
version: this.version,
ranges: this.ranges,
lastUpdatedAt: this.lastUpdatedAt,
lastUpdatedBy: this.lastUpdatedBy,
}
this.DocumentController.setDocument(this.req, this.res, this.next)
})
it('should update the document in Mongo', function () {
sinon.assert.calledWith(
this.ProjectEntityUpdateHandler.updateDocLines,
this.project_id,
this.doc_id,
this.doc_lines,
this.version,
this.ranges,
this.lastUpdatedAt,
this.lastUpdatedBy
)
})
it('should return a successful response', function () {
this.res.success.should.equal(true)
})
})
describe("when the document doesn't exist", function () {
beforeEach(function () {
this.ProjectEntityUpdateHandler.updateDocLines = sinon
.stub()
.yields(new Errors.NotFoundError('document does not exist'))
this.req.body = { lines: this.doc_lines }
this.DocumentController.setDocument(this.req, this.res, this.next)
})
it('should call next with the NotFoundError', function () {
this.next
.calledWith(sinon.match.instanceOf(Errors.NotFoundError))
.should.equal(true)
})
})
})
})