mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-11 17:06:43 -05:00
de4f1a5abd
Ensure project's root doc is valid before exporting GitOrigin-RevId: 6f15955015097df9464267f458b8dbb126c22173
438 lines
14 KiB
CoffeeScript
438 lines
14 KiB
CoffeeScript
sinon = require('sinon')
|
|
chai = require('chai')
|
|
should = chai.should()
|
|
expect = chai.expect
|
|
modulePath = '../../../../app/js/Features/Exports/ExportsHandler.js'
|
|
SandboxedModule = require('sandboxed-module')
|
|
|
|
describe 'ExportsHandler', ->
|
|
|
|
beforeEach ->
|
|
@stubRequest = {}
|
|
@request = defaults: => return @stubRequest
|
|
@ExportsHandler = SandboxedModule.require modulePath, requires:
|
|
'logger-sharelatex':
|
|
log: ->
|
|
err: ->
|
|
'../Project/ProjectGetter': @ProjectGetter = {}
|
|
'../Project/ProjectHistoryHandler': @ProjectHistoryHandler = {}
|
|
'../Project/ProjectLocator': @ProjectLocator = {}
|
|
'../Project/ProjectRootDocManager': @ProjectRootDocManager = {}
|
|
'../User/UserGetter': @UserGetter = {}
|
|
'settings-sharelatex': @settings = {}
|
|
'request': @request
|
|
@project_id = "project-id-123"
|
|
@project_history_id = 987
|
|
@user_id = "user-id-456"
|
|
@brand_variation_id = 789
|
|
@title = "title"
|
|
@description = "description"
|
|
@author = "author"
|
|
@license = "other"
|
|
@show_source = true
|
|
@export_params = {
|
|
project_id: @project_id,
|
|
brand_variation_id: @brand_variation_id,
|
|
user_id: @user_id
|
|
title: @title
|
|
description: @description
|
|
author: @author
|
|
license: @license
|
|
show_source: @show_source
|
|
}
|
|
@callback = sinon.stub()
|
|
|
|
describe 'exportProject', ->
|
|
beforeEach ->
|
|
@export_data = {iAmAnExport: true}
|
|
@response_body = {iAmAResponseBody: true}
|
|
@ExportsHandler._buildExport = sinon.stub().yields(null, @export_data)
|
|
@ExportsHandler._requestExport = sinon.stub().yields(null, @response_body)
|
|
|
|
describe "when all goes well", ->
|
|
beforeEach (done) ->
|
|
@ExportsHandler.exportProject @export_params, (error, export_data) =>
|
|
@callback(error, export_data)
|
|
done()
|
|
|
|
it "should build the export", ->
|
|
@ExportsHandler._buildExport
|
|
.calledWith(@export_params)
|
|
.should.equal true
|
|
|
|
it "should request the export", ->
|
|
@ExportsHandler._requestExport
|
|
.calledWith(@export_data)
|
|
.should.equal true
|
|
|
|
it "should return the export", ->
|
|
@callback
|
|
.calledWith(null, @export_data)
|
|
.should.equal true
|
|
|
|
describe "when request can't be built", ->
|
|
beforeEach (done) ->
|
|
@ExportsHandler._buildExport = sinon.stub().yields(new Error("cannot export project without root doc"))
|
|
@ExportsHandler.exportProject @export_params, (error, export_data) =>
|
|
@callback(error, export_data)
|
|
done()
|
|
|
|
it "should return an error", ->
|
|
(@callback.args[0][0] instanceof Error)
|
|
.should.equal true
|
|
|
|
describe '_buildExport', ->
|
|
beforeEach (done) ->
|
|
@project =
|
|
id: @project_id
|
|
rootDoc_id: 'doc1_id'
|
|
compiler: 'pdflatex'
|
|
imageName: 'mock-image-name'
|
|
overleaf:
|
|
id: @project_history_id # for projects imported from v1
|
|
history:
|
|
id: @project_history_id
|
|
@user =
|
|
id: @user_id
|
|
first_name: 'Arthur'
|
|
last_name: 'Author'
|
|
email: 'arthur.author@arthurauthoring.org'
|
|
overleaf:
|
|
id: 876
|
|
@rootDocPath = 'main.tex'
|
|
@historyVersion = 777
|
|
@ProjectGetter.getProject = sinon.stub().yields(null, @project)
|
|
@ProjectHistoryHandler.ensureHistoryExistsForProject = sinon.stub().yields(null)
|
|
@ProjectLocator.findRootDoc = sinon.stub().yields(null, [null, {fileSystem: 'main.tex'}])
|
|
@ProjectRootDocManager.ensureRootDocumentIsValid = sinon.stub().callsArgWith(1, null)
|
|
@UserGetter.getUser = sinon.stub().yields(null, @user)
|
|
@ExportsHandler._requestVersion = sinon.stub().yields(null, @historyVersion)
|
|
done()
|
|
|
|
describe "when all goes well", ->
|
|
beforeEach (done) ->
|
|
@ExportsHandler._buildExport @export_params, (error, export_data) =>
|
|
@callback(error, export_data)
|
|
done()
|
|
|
|
it "should ensure the project has history", ->
|
|
@ProjectHistoryHandler.ensureHistoryExistsForProject.called
|
|
.should.equal true
|
|
|
|
it "should request the project history version", ->
|
|
@ExportsHandler._requestVersion.called
|
|
.should.equal true
|
|
|
|
it "should return export data", ->
|
|
expected_export_data =
|
|
project:
|
|
id: @project_id
|
|
rootDocPath: @rootDocPath
|
|
historyId: @project_history_id
|
|
historyVersion: @historyVersion
|
|
v1ProjectId: @project_history_id
|
|
metadata:
|
|
compiler: 'pdflatex'
|
|
imageName: 'mock-image-name'
|
|
title: @title
|
|
description: @description
|
|
author: @author
|
|
license: @license
|
|
showSource: @show_source
|
|
user:
|
|
id: @user_id
|
|
firstName: @user.first_name
|
|
lastName: @user.last_name
|
|
email: @user.email
|
|
orcidId: null
|
|
v1UserId: 876
|
|
destination:
|
|
brandVariationId: @brand_variation_id
|
|
options:
|
|
callbackUrl: null
|
|
@callback.calledWith(null, expected_export_data)
|
|
.should.equal true
|
|
|
|
describe "when we send replacement user first and last name", ->
|
|
beforeEach (done) ->
|
|
@custom_first_name = "FIRST"
|
|
@custom_last_name = "LAST"
|
|
@export_params.first_name = @custom_first_name
|
|
@export_params.last_name = @custom_last_name
|
|
@ExportsHandler._buildExport @export_params, (error, export_data) =>
|
|
@callback(error, export_data)
|
|
done()
|
|
|
|
it "should send the data from the user input", ->
|
|
expected_export_data =
|
|
project:
|
|
id: @project_id
|
|
rootDocPath: @rootDocPath
|
|
historyId: @project_history_id
|
|
historyVersion: @historyVersion
|
|
v1ProjectId: @project_history_id
|
|
metadata:
|
|
compiler: 'pdflatex'
|
|
imageName: 'mock-image-name'
|
|
title: @title
|
|
description: @description
|
|
author: @author
|
|
license: @license
|
|
showSource: @show_source
|
|
user:
|
|
id: @user_id
|
|
firstName: @custom_first_name
|
|
lastName: @custom_last_name
|
|
email: @user.email
|
|
orcidId: null
|
|
v1UserId: 876
|
|
destination:
|
|
brandVariationId: @brand_variation_id
|
|
options:
|
|
callbackUrl: null
|
|
@callback.calledWith(null, expected_export_data)
|
|
.should.equal true
|
|
|
|
describe "when project is not found", ->
|
|
beforeEach (done) ->
|
|
@ProjectGetter.getProject = sinon.stub().yields(new Error("project not found"))
|
|
@ExportsHandler._buildExport @export_params, (error, export_data) =>
|
|
@callback(error, export_data)
|
|
done()
|
|
|
|
it "should return an error", ->
|
|
(@callback.args[0][0] instanceof Error)
|
|
.should.equal true
|
|
|
|
describe "when project has no root doc", ->
|
|
describe "when a root doc can be set automatically", ->
|
|
beforeEach (done) ->
|
|
@project.rootDoc_id = null
|
|
@ProjectLocator.findRootDoc = sinon.stub().yields(null, [null, {fileSystem: 'other.tex'}])
|
|
@ExportsHandler._buildExport @export_params, (error, export_data) =>
|
|
@callback(error, export_data)
|
|
done()
|
|
|
|
it "should set a root doc", ->
|
|
@ProjectRootDocManager.ensureRootDocumentIsValid.called
|
|
.should.equal true
|
|
|
|
it "should return export data", ->
|
|
expected_export_data =
|
|
project:
|
|
id: @project_id
|
|
rootDocPath: 'other.tex'
|
|
historyId: @project_history_id
|
|
historyVersion: @historyVersion
|
|
v1ProjectId: @project_history_id
|
|
metadata:
|
|
compiler: 'pdflatex'
|
|
imageName: 'mock-image-name'
|
|
title: @title
|
|
description: @description
|
|
author: @author
|
|
license: @license
|
|
showSource: @show_source
|
|
user:
|
|
id: @user_id
|
|
firstName: @user.first_name
|
|
lastName: @user.last_name
|
|
email: @user.email
|
|
orcidId: null
|
|
v1UserId: 876
|
|
destination:
|
|
brandVariationId: @brand_variation_id
|
|
options:
|
|
callbackUrl: null
|
|
@callback.calledWith(null, expected_export_data)
|
|
.should.equal true
|
|
|
|
describe "when project has an invalid root doc", ->
|
|
describe "when a new root doc can be set automatically", ->
|
|
beforeEach (done) ->
|
|
@fakeDoc_id = '1a2b3c4d5e6f'
|
|
@project.rootDoc_id = @fakeDoc_id
|
|
@ProjectLocator.findRootDoc = sinon.stub().yields(null, [null, {fileSystem: 'other.tex'}])
|
|
@ExportsHandler._buildExport @export_params, (error, export_data) =>
|
|
@callback(error, export_data)
|
|
done()
|
|
|
|
it "should set a valid root doc", ->
|
|
@ProjectRootDocManager.ensureRootDocumentIsValid.called
|
|
.should.equal true
|
|
|
|
it "should return export data", ->
|
|
expected_export_data =
|
|
project:
|
|
id: @project_id
|
|
rootDocPath: 'other.tex'
|
|
historyId: @project_history_id
|
|
historyVersion: @historyVersion
|
|
v1ProjectId: @project_history_id
|
|
metadata:
|
|
compiler: 'pdflatex'
|
|
imageName: 'mock-image-name'
|
|
title: @title
|
|
description: @description
|
|
author: @author
|
|
license: @license
|
|
showSource: @show_source
|
|
user:
|
|
id: @user_id
|
|
firstName: @user.first_name
|
|
lastName: @user.last_name
|
|
email: @user.email
|
|
orcidId: null
|
|
v1UserId: 876
|
|
destination:
|
|
brandVariationId: @brand_variation_id
|
|
options:
|
|
callbackUrl: null
|
|
@callback.calledWith(null, expected_export_data)
|
|
.should.equal true
|
|
|
|
describe "when no root doc can be identified", ->
|
|
beforeEach (done) ->
|
|
@ProjectLocator.findRootDoc = sinon.stub().yields(null, [null, null])
|
|
@ExportsHandler._buildExport @export_params, (error, export_data) =>
|
|
@callback(error, export_data)
|
|
done()
|
|
|
|
it "should return an error", ->
|
|
(@callback.args[0][0] instanceof Error)
|
|
.should.equal true
|
|
|
|
describe "when user is not found", ->
|
|
beforeEach (done) ->
|
|
@UserGetter.getUser = sinon.stub().yields(new Error("user not found"))
|
|
@ExportsHandler._buildExport @export_params, (error, export_data) =>
|
|
@callback(error, export_data)
|
|
done()
|
|
|
|
it "should return an error", ->
|
|
(@callback.args[0][0] instanceof Error)
|
|
.should.equal true
|
|
|
|
describe "when project history request fails", ->
|
|
beforeEach (done) ->
|
|
@ExportsHandler._requestVersion = sinon.stub().yields(new Error("project history call failed"))
|
|
@ExportsHandler._buildExport @export_params, (error, export_data) =>
|
|
@callback(error, export_data)
|
|
done()
|
|
|
|
it "should return an error", ->
|
|
(@callback.args[0][0] instanceof Error)
|
|
.should.equal true
|
|
|
|
describe '_requestExport', ->
|
|
beforeEach (done) ->
|
|
@settings.apis =
|
|
v1:
|
|
url: 'http://localhost:5000'
|
|
user: 'overleaf'
|
|
pass: 'pass'
|
|
@export_data = {iAmAnExport: true}
|
|
@export_id = 4096
|
|
@stubPost = sinon.stub().yields(null, {statusCode: 200}, { exportId: @export_id })
|
|
done()
|
|
|
|
describe "when all goes well", ->
|
|
beforeEach (done) ->
|
|
@stubRequest.post = @stubPost
|
|
@ExportsHandler._requestExport @export_data, (error, export_v1_id) =>
|
|
@callback(error, export_v1_id)
|
|
done()
|
|
|
|
it 'should issue the request', ->
|
|
expect(@stubPost.getCall(0).args[0]).to.deep.equal
|
|
url: @settings.apis.v1.url + '/api/v1/sharelatex/exports'
|
|
auth:
|
|
user: @settings.apis.v1.user
|
|
pass: @settings.apis.v1.pass
|
|
json: @export_data
|
|
|
|
it 'should return the v1 export id', ->
|
|
@callback.calledWith(null, @export_id)
|
|
.should.equal true
|
|
|
|
describe "when the request fails", ->
|
|
beforeEach (done) ->
|
|
@stubRequest.post = sinon.stub().yields(new Error("export request failed"))
|
|
@ExportsHandler._requestExport @export_data, (error, export_v1_id) =>
|
|
@callback(error, export_v1_id)
|
|
done()
|
|
|
|
it "should return an error", ->
|
|
(@callback.args[0][0] instanceof Error)
|
|
.should.equal true
|
|
|
|
describe "when the request returns an error code", ->
|
|
beforeEach (done) ->
|
|
@stubRequest.post = sinon.stub().yields(null, {statusCode: 401}, { })
|
|
@ExportsHandler._requestExport @export_data, (error, export_v1_id) =>
|
|
@callback(error, export_v1_id)
|
|
done()
|
|
|
|
it "should return the error", ->
|
|
(@callback.args[0][0] instanceof Error)
|
|
.should.equal true
|
|
|
|
describe 'fetchExport', ->
|
|
beforeEach (done) ->
|
|
@settings.apis =
|
|
v1:
|
|
url: 'http://localhost:5000'
|
|
user: 'overleaf'
|
|
pass: 'pass'
|
|
@export_id = 897
|
|
@body = "{\"id\":897, \"status_summary\":\"completed\"}"
|
|
@stubGet = sinon.stub().yields(null, {statusCode: 200}, { body: @body })
|
|
done()
|
|
|
|
describe "when all goes well", ->
|
|
beforeEach (done) ->
|
|
@stubRequest.get = @stubGet
|
|
@ExportsHandler.fetchExport @export_id, (error, body) =>
|
|
@callback(error, body)
|
|
done()
|
|
|
|
it 'should issue the request', ->
|
|
expect(@stubGet.getCall(0).args[0]).to.deep.equal
|
|
url: @settings.apis.v1.url + '/api/v1/sharelatex/exports/' + @export_id
|
|
auth:
|
|
user: @settings.apis.v1.user
|
|
pass: @settings.apis.v1.pass
|
|
|
|
it 'should return the v1 export id', ->
|
|
@callback.calledWith(null, { body: @body })
|
|
.should.equal true
|
|
|
|
describe 'fetchDownload', ->
|
|
beforeEach (done) ->
|
|
@settings.apis =
|
|
v1:
|
|
url: 'http://localhost:5000'
|
|
user: 'overleaf'
|
|
pass: 'pass'
|
|
@export_id = 897
|
|
@body = "https://writelatex-conversions-dev.s3.amazonaws.com/exports/ieee_latexqc/tnb/2912/xggmprcrpfwbsnqzqqmvktddnrbqkqkr.zip?X-Amz-Expires=14400&X-Amz-Date=20180730T181003Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJDGDIJFGLNVGZH6A/20180730/us-east-1/s3/aws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=dec990336913cef9933f0e269afe99722d7ab2830ebf2c618a75673ee7159fee"
|
|
@stubGet = sinon.stub().yields(null, {statusCode: 200}, { body: @body })
|
|
done()
|
|
|
|
describe "when all goes well", ->
|
|
beforeEach (done) ->
|
|
@stubRequest.get = @stubGet
|
|
@ExportsHandler.fetchDownload @export_id, 'zip', (error, body) =>
|
|
@callback(error, body)
|
|
done()
|
|
|
|
it 'should issue the request', ->
|
|
expect(@stubGet.getCall(0).args[0]).to.deep.equal
|
|
url: @settings.apis.v1.url + '/api/v1/sharelatex/exports/' + @export_id + '/zip_url'
|
|
auth:
|
|
user: @settings.apis.v1.user
|
|
pass: @settings.apis.v1.pass
|
|
|
|
it 'should return the v1 export id', ->
|
|
@callback.calledWith(null, { body: @body })
|
|
.should.equal true
|