overleaf/services/web/test/unit/coffee/Exports/ExportsHandlerTests.coffee
Ersun Warncke de4f1a5abd Merge pull request #1126 from sharelatex/mm-ensure-valid-rootdoc
Ensure project's root doc is valid before exporting

GitOrigin-RevId: 6f15955015097df9464267f458b8dbb126c22173
2018-11-09 08:26:24 +00:00

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