Merge branch 'master' into sk-unlisted-projects

This commit is contained in:
Shane Kilkelly
2017-11-02 10:10:09 +00:00
77 changed files with 6901 additions and 2072 deletions
@@ -431,3 +431,103 @@ describe "CollaboratorsHandler", ->
expect(err).to.not.exist
expect(isTokenMember).to.equal false
done()
describe 'transferProjects', ->
beforeEach ->
@from_user_id = "from-user-id"
@to_user_id = "to-user-id"
@projects = [{
_id: "project-id-1"
}, {
_id: "project-id-2"
}]
@Project.find = sinon.stub().yields(null, @projects)
@Project.update = sinon.stub().yields()
@ProjectEntityHandler.flushProjectToThirdPartyDataStore = sinon.stub().yields()
describe "successfully", ->
beforeEach ->
@CollaboratorHandler.transferProjects @from_user_id, @to_user_id, @callback
it "should look up the affected projects", ->
@Project.find
.calledWith({
$or : [
{ owner_ref: @from_user_id }
{ collaberator_refs: @from_user_id }
{ readOnly_refs: @from_user_id }
]
})
.should.equal true
it "should transfer owned projects", ->
@Project.update
.calledWith({
owner_ref: @from_user_id
}, {
$set: { owner_ref: @to_user_id }
}, {
multi: true
})
.should.equal true
it "should transfer collaborator projects", ->
@Project.update
.calledWith({
collaberator_refs: @from_user_id
}, {
$addToSet: { collaberator_refs: @to_user_id }
}, {
multi: true
})
.should.equal true
@Project.update
.calledWith({
collaberator_refs: @from_user_id
}, {
$pull: { collaberator_refs: @from_user_id }
}, {
multi: true
})
.should.equal true
it "should transfer read only collaborator projects", ->
@Project.update
.calledWith({
readOnly_refs: @from_user_id
}, {
$addToSet: { readOnly_refs: @to_user_id }
}, {
multi: true
})
.should.equal true
@Project.update
.calledWith({
readOnly_refs: @from_user_id
}, {
$pull: { readOnly_refs: @from_user_id }
}, {
multi: true
})
.should.equal true
it "should flush each project to the TPDS", ->
for project in @projects
@ProjectEntityHandler.flushProjectToThirdPartyDataStore
.calledWith(project._id)
.should.equal true
it "should call the callback", ->
@callback.called.should.equal true
describe "when flushing to TPDS fails", ->
beforeEach ->
@ProjectEntityHandler.flushProjectToThirdPartyDataStore = sinon.stub().yields(new Error('oops'))
@CollaboratorHandler.transferProjects @from_user_id, @to_user_id, @callback
it "should log an error", ->
@logger.err.called.should.equal true
it "should not return an error since it happens in the background", ->
@callback.called.should.equal true
@callback.calledWith(new Error('oops')).should.equal false
@@ -33,7 +33,7 @@ describe "ClsiManager", ->
getProjectDocsIfMatch: sinon.stub().callsArgWith(2,null,null)
"./ClsiCookieManager": @ClsiCookieManager
"./ClsiStateManager": @ClsiStateManager
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub(), warn: sinon.stub() }
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub(), err: sinon.stub(), warn: sinon.stub() }
"request": @request = sinon.stub()
"./ClsiFormatChecker": @ClsiFormatChecker
"metrics-sharelatex": @Metrics =
@@ -122,6 +122,21 @@ describe "ClsiManager", ->
it "should call the callback with a success status", ->
@callback.calledWith(null, @status, ).should.equal true
describe "when the resources fail the precompile check", ->
beforeEach ->
@ClsiFormatChecker.checkRecoursesForProblems = sinon.stub().callsArgWith(1, new Error("failed"))
@ClsiManager._postToClsi = sinon.stub().callsArgWith(4, null, {
compile:
status: @status = "failure"
})
@ClsiManager.sendRequest @project_id, @user_id, {}, @callback
it "should call the callback only once", ->
@callback.calledOnce.should.equal true
it "should call the callback with an error", ->
@callback.calledWithExactly(new Error("failed")).should.equal true
describe "deleteAuxFiles", ->
beforeEach ->
@ClsiManager._makeRequest = sinon.stub().callsArg(2)
@@ -247,12 +262,12 @@ describe "ClsiManager", ->
.calledWith(@project_id, {compiler:1, rootDoc_id: 1, imageName: 1, rootFolder: 1})
.should.equal true
it "should flush the project to the database", ->
it "should not explicitly flush the project to the database", ->
@DocumentUpdaterHandler.flushProjectToMongo
.calledWith(@project_id)
.should.equal true
.should.equal false
it "should get only the live docs from the docupdater", ->
it "should get only the live docs from the docupdater with a background flush in docupdater", ->
@DocumentUpdaterHandler.getProjectDocsIfMatch
.calledWith(@project_id)
.should.equal true
@@ -331,7 +346,49 @@ describe "ClsiManager", ->
it "should set to main.tex", ->
@request.compile.rootResourcePath.should.equal "main.tex"
describe "when there is no valid root document and no main.tex document", ->
beforeEach () ->
@project.rootDoc_id = "not-valid"
@docs = {
"/other.tex": @doc_1 = {
name: "other.tex"
_id: "mock-doc-id-1"
lines: ["Hello", "world"]
},
"/chapters/chapter1.tex": @doc_2 = {
name: "chapter1.tex"
_id: "mock-doc-id-2"
lines: [
"Chapter 1"
]
}
}
@ProjectEntityHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @docs)
@ClsiManager._buildRequest @project, null, @callback
it "should report an error", ->
@callback.calledWith(new Error("no main file specified")).should.equal true
describe "when there is no valid root document and a single document which is not main.tex", ->
beforeEach (done) ->
@project.rootDoc_id = "not-valid"
@docs = {
"/other.tex": @doc_1 = {
name: "other.tex"
_id: "mock-doc-id-1"
lines: ["Hello", "world"]
}
}
@ProjectEntityHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @docs)
@ClsiManager._buildRequest @project, null, (@error, @request) =>
done()
it "should set io to the only file", ->
@request.compile.rootResourcePath.should.equal "other.tex"
describe "with the draft option", ->
it "should add the draft option into the request", (done) ->
@ClsiManager._buildRequest @project_id, {timeout:100, draft: true}, (error, request) =>
@@ -44,7 +44,7 @@ describe "CompileManager", ->
describe "succesfully", ->
beforeEach ->
@CompileManager._checkIfAutoCompileLimitHasBeenHit = (_, cb)-> cb(null, true)
@CompileManager._checkIfAutoCompileLimitHasBeenHit = (isAutoCompile, compileGroup, cb)-> cb(null, true)
@CompileManager.compile @project_id, @user_id, {}, @callback
it "should check the project has not been recently compiled", ->
@@ -84,7 +84,7 @@ describe "CompileManager", ->
describe "when the project has been recently compiled", ->
it "should return", (done)->
@CompileManager._checkIfAutoCompileLimitHasBeenHit = (_, cb)-> cb(null, true)
@CompileManager._checkIfAutoCompileLimitHasBeenHit = (isAutoCompile, compileGroup, cb)-> cb(null, true)
@CompileManager._checkIfRecentlyCompiled = sinon.stub().callsArgWith(2, null, true)
@CompileManager.compile @project_id, @user_id, {}, (err, status)->
status.should.equal "too-recently-compiled"
@@ -92,7 +92,7 @@ describe "CompileManager", ->
describe "should check the rate limit", ->
it "should return", (done)->
@CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(1, null, false)
@CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(2, null, false)
@CompileManager.compile @project_id, @user_id, {}, (err, status)->
status.should.equal "autocompile-backoff"
done()
@@ -222,14 +222,14 @@ describe "CompileManager", ->
describe "_checkIfAutoCompileLimitHasBeenHit", ->
it "should be able to compile if it is not an autocompile", (done)->
@ratelimiter.addCount.callsArgWith(1, null, true)
@CompileManager._checkIfAutoCompileLimitHasBeenHit false, (err, canCompile)=>
@ratelimiter.addCount.callsArgWith(2, null, true)
@CompileManager._checkIfAutoCompileLimitHasBeenHit false, "everyone", (err, canCompile)=>
canCompile.should.equal true
done()
it "should be able to compile if rate limit has remianing", (done)->
@ratelimiter.addCount.callsArgWith(1, null, true)
@CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=>
@CompileManager._checkIfAutoCompileLimitHasBeenHit true, "everyone", (err, canCompile)=>
args = @ratelimiter.addCount.args[0][0]
args.throttle.should.equal 25
args.subjectName.should.equal "everyone"
@@ -240,13 +240,13 @@ describe "CompileManager", ->
it "should be not able to compile if rate limit has no remianing", (done)->
@ratelimiter.addCount.callsArgWith(1, null, false)
@CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=>
@CompileManager._checkIfAutoCompileLimitHasBeenHit true, "everyone", (err, canCompile)=>
canCompile.should.equal false
done()
it "should return false if there is an error in the rate limit", (done)->
@ratelimiter.addCount.callsArgWith(1, "error")
@CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=>
@CompileManager._checkIfAutoCompileLimitHasBeenHit true, "everyone", (err, canCompile)=>
canCompile.should.equal false
done()
@@ -265,19 +265,19 @@ describe 'DocumentUpdaterHandler', ->
v: @version
@docs = [ @doc0, @doc0, @doc0 ]
@body = JSON.stringify @docs
@request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @body)
@request.post = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @body)
@handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback
it 'should get the documenst from the document updater', ->
url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}/doc?state=#{@project_state_hash}"
@request.get.calledWith(url).should.equal true
url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}/get_and_flush_if_old?state=#{@project_state_hash}"
@request.post.calledWith(url).should.equal true
it "should call the callback with the documents", ->
@callback.calledWithExactly(null, @docs).should.equal true
describe "when the document updater API returns an error", ->
beforeEach ->
@request.get = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null)
@request.post = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null)
@handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback
it "should return an error to the callback", ->
@@ -285,7 +285,7 @@ describe 'DocumentUpdaterHandler', ->
describe "when the document updater returns a conflict error code", ->
beforeEach ->
@request.get = sinon.stub().callsArgWith(1, null, { statusCode: 409 }, "Conflict")
@request.post = sinon.stub().callsArgWith(1, null, { statusCode: 409 }, "Conflict")
@handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback
it "should return the callback with no documents", ->
@@ -312,7 +312,7 @@ describe 'DocumentUpdaterHandler', ->
describe "when the document updater API returns an error", ->
beforeEach ->
@request.get = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null)
@request.post = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null)
@handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback
it "should return an error to the callback", ->
@@ -320,7 +320,7 @@ describe 'DocumentUpdaterHandler', ->
describe "when the document updater returns a conflict error code", ->
beforeEach ->
@request.get = sinon.stub().callsArgWith(1, null, { statusCode: 409 }, "Conflict")
@request.post = sinon.stub().callsArgWith(1, null, { statusCode: 409 }, "Conflict")
@handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback
it "should return the callback with no documents", ->
@@ -24,6 +24,7 @@ describe "DocumentController", ->
@doc_lines = ["one", "two", "three"]
@version = 42
@ranges = {"mock": "ranges"}
@pathname = '/a/b/c/file.tex'
@rev = 5
describe "getDocument", ->
@@ -34,12 +35,12 @@ describe "DocumentController", ->
describe "when the document exists", ->
beforeEach ->
@ProjectEntityHandler.getDoc = sinon.stub().callsArgWith(2, null, @doc_lines, @rev, @version, @ranges)
@ProjectEntityHandler.getDoc = sinon.stub().callsArgWith(3, null, @doc_lines, @rev, @version, @ranges, @pathname)
@DocumentController.getDocument(@req, @res, @next)
it "should get the document from Mongo", ->
@ProjectEntityHandler.getDoc
.calledWith(@project_id, @doc_id)
.calledWith(@project_id, @doc_id, pathname: true)
.should.equal true
it "should return the document data to the client as JSON", ->
@@ -48,10 +49,11 @@ describe "DocumentController", ->
lines: @doc_lines
version: @version
ranges: @ranges
pathname: @pathname
describe "when the document doesn't exist", ->
beforeEach ->
@ProjectEntityHandler.getDoc = sinon.stub().callsArgWith(2, new Errors.NotFoundError("not found"), null)
@ProjectEntityHandler.getDoc = sinon.stub().callsArgWith(3, new Errors.NotFoundError("not found"), null)
@DocumentController.getDocument(@req, @res, @next)
it "should call next with the NotFoundError", ->
@@ -6,6 +6,7 @@ SandboxedModule = require('sandboxed-module')
describe "HistoryController", ->
beforeEach ->
@callback = sinon.stub()
@user_id = "user-id-123"
@AuthenticationController =
getLoggedInUserId: sinon.stub().returns(@user_id)
@@ -14,46 +15,134 @@ describe "HistoryController", ->
"settings-sharelatex": @settings = {}
"logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub()}
"../Authentication/AuthenticationController": @AuthenticationController
@settings.apis =
trackchanges:
enabled: false
url: "http://trackchanges.example.com"
project_history:
url: "http://project_history.example.com"
describe "proxyToHistoryApi", ->
beforeEach ->
@req = { url: "/mock/url", method: "POST" }
@res = "mock-res"
@next = sinon.stub()
@settings.apis =
trackchanges:
url: "http://trackchanges.example.com"
@proxy =
events: {}
pipe: sinon.stub()
on: (event, handler) -> @events[event] = handler
@request.returns @proxy
@HistoryController.proxyToHistoryApi @req, @res, @next
describe "successfully", ->
it "should get the user id", ->
@AuthenticationController.getLoggedInUserId
.calledWith(@req)
.should.equal true
describe "with project history enabled", ->
beforeEach ->
@settings.apis.project_history.enabled = true
@HistoryController.proxyToHistoryApi @req, @res, @next
it "should call the track changes api", ->
@request
.calledWith({
url: "#{@settings.apis.trackchanges.url}#{@req.url}"
method: @req.method
headers:
"X-User-Id": @user_id
})
.should.equal true
it "should get the user id", ->
@AuthenticationController.getLoggedInUserId
.calledWith(@req)
.should.equal true
it "should pipe the response to the client", ->
@proxy.pipe
.calledWith(@res)
.should.equal true
it "should call the project history api", ->
@request
.calledWith({
url: "#{@settings.apis.project_history.url}#{@req.url}"
method: @req.method
headers:
"X-User-Id": @user_id
})
.should.equal true
it "should pipe the response to the client", ->
@proxy.pipe
.calledWith(@res)
.should.equal true
describe "with project history disabled", ->
beforeEach ->
@settings.apis.project_history.enabled = false
@HistoryController.proxyToHistoryApi @req, @res, @next
it "should call the track changes api", ->
@request
.calledWith({
url: "#{@settings.apis.trackchanges.url}#{@req.url}"
method: @req.method
headers:
"X-User-Id": @user_id
})
.should.equal true
describe "with an error", ->
beforeEach ->
@HistoryController.proxyToHistoryApi @req, @res, @next
@proxy.events["error"].call(@proxy, @error = new Error("oops"))
it "should pass the error up the call chain", ->
@next.calledWith(@error).should.equal true
describe "initializeProject", ->
describe "with project history enabled", ->
beforeEach ->
@settings.apis.project_history.enabled = true
describe "project history returns a successful response", ->
beforeEach ->
@overleaf_id = 1234
@res = statusCode: 200
@body = JSON.stringify(project: id: @overleaf_id)
@request.post = sinon.stub().callsArgWith(1, null, @res, @body)
@HistoryController.initializeProject @callback
it "should call the project history api", ->
@request.post.calledWith(
url: "#{@settings.apis.project_history.url}/project"
).should.equal true
it "should return the callback with the overleaf id", ->
@callback.calledWithExactly(null, { @overleaf_id }).should.equal true
describe "project history returns a response without the project id", ->
beforeEach ->
@res = statusCode: 200
@body = JSON.stringify(project: {})
@request.post = sinon.stub().callsArgWith(1, null, @res, @body)
@HistoryController.initializeProject @callback
it "should return the callback with an error", ->
@callback
.calledWith(sinon.match.has("message", "project-history did not provide an id"))
.should.equal true
describe "project history returns a unsuccessful response", ->
beforeEach ->
@res = statusCode: 404
@request.post = sinon.stub().callsArgWith(1, null, @res)
@HistoryController.initializeProject @callback
it "should return the callback with an error", ->
@callback
.calledWith(sinon.match.has("message", "project-history returned a non-success status code: 404"))
.should.equal true
describe "project history errors", ->
beforeEach ->
@error = sinon.stub()
@request.post = sinon.stub().callsArgWith(1, @error)
@HistoryController.initializeProject @callback
it "should return the callback with the error", ->
@callback.calledWithExactly(@error).should.equal true
describe "with project history disabled", ->
beforeEach ->
@settings.apis.project_history.enabled = false
@HistoryController.initializeProject @callback
it "should return the callback", ->
@callback.calledWithExactly().should.equal true
@@ -1,63 +0,0 @@
chai = require('chai')
expect = chai.expect
chai.should()
sinon = require("sinon")
modulePath = "../../../../app/js/Features/History/HistoryManager"
SandboxedModule = require('sandboxed-module')
describe "HistoryManager", ->
beforeEach ->
@HistoryManager = SandboxedModule.require modulePath, requires:
"request" : @request = sinon.stub()
"settings-sharelatex": @settings =
apis:
trackchanges:
url: "trackchanges.sharelatex.com"
"logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub()}
@project_id = "project-id-123"
@callback = sinon.stub()
@request.post = sinon.stub()
describe "flushProject", ->
describe "with a successful response code", ->
beforeEach ->
@request.post = sinon.stub().callsArgWith(1, null, statusCode: 204, "")
@HistoryManager.flushProject @project_id, @callback
it "should flush the project in the track changes api", ->
@request.post
.calledWith("#{@settings.apis.trackchanges.url}/project/#{@project_id}/flush")
.should.equal true
it "should call the callback without an error", ->
@callback.calledWith(null).should.equal true
describe "with a failed response code", ->
beforeEach ->
@request.post = sinon.stub().callsArgWith(1, null, statusCode: 500, "")
@HistoryManager.flushProject @project_id, @callback
it "should call the callback with an error", ->
@callback.calledWith(new Error("track-changes api responded with a non-success code: 500")).should.equal true
it "should log the error", ->
@logger.error
.calledWith({
err: new Error("track-changes api responded with a non-success code: 500")
project_id: @project_id
}, "error flushing project in track-changes api")
.should.equal true
describe "ArchiveProject", ->
it "should call the post endpoint", (done)->
@request.post.callsArgWith(1, null, {})
@HistoryManager.archiveProject @project_id, (err)=>
@request.post.calledWith("#{@settings.apis.trackchanges.url}/project/#{@project_id}/archive")
done()
it "should return an error on a non success", (done)->
@request.post.callsArgWith(1, null, {statusCode:500})
@HistoryManager.archiveProject @project_id, (err)=>
expect(err).to.exist
done()
@@ -22,6 +22,8 @@ describe 'ProjectCreationHandler', ->
@._id = project_id
@owner_ref = options.owner_ref
@name = options.name
@overleaf =
history: {}
save: sinon.stub().callsArg(0)
rootFolder:[{
_id: rootFolderId
@@ -36,11 +38,13 @@ describe 'ProjectCreationHandler', ->
setRootDoc: sinon.stub().callsArg(2)
@ProjectDetailsHandler =
validateProjectName: sinon.stub().yields()
@HistoryController =
initializeProject: sinon.stub().callsArg(0)
@user =
@user =
first_name:"first name here"
last_name:"last name here"
ace:
ace:
spellCheckLanguage:"de"
@User = findById:sinon.stub().callsArgWith(2, null, @user)
@@ -49,6 +53,7 @@ describe 'ProjectCreationHandler', ->
'../../models/User': User:@User
'../../models/Project':{Project:@ProjectModel}
'../../models/Folder':{Folder:@FolderModel}
'../History/HistoryController': @HistoryController
'./ProjectEntityHandler':@ProjectEntityHandler
"./ProjectDetailsHandler":@ProjectDetailsHandler
"settings-sharelatex": @Settings = {}
@@ -60,32 +65,48 @@ describe 'ProjectCreationHandler', ->
describe 'Creating a Blank project', ->
beforeEach ->
@overleaf_id = 1234
@HistoryController.initializeProject = sinon.stub().callsArgWith(0, null, { @overleaf_id })
@ProjectModel::save = sinon.stub().callsArg(0)
describe "successfully", ->
it "should save the project", (done)->
@handler.createBlankProject ownerId, projectName, =>
@ProjectModel::save.called.should.equal true
done()
it "should return the project in the callback", (done)->
@handler.createBlankProject ownerId, projectName, (err, project)->
project.name.should.equal projectName
(project.owner_ref + "").should.equal ownerId
done()
it "should initialize the project overleaf if history id not provided", (done)->
@handler.createBlankProject ownerId, projectName, done
@HistoryController.initializeProject.calledWith().should.equal true
it "should set the overleaf id if overleaf id not provided", (done)->
@handler.createBlankProject ownerId, projectName, (err, project)=>
project.overleaf.history.id.should.equal @overleaf_id
done()
it "should set the overleaf id if overleaf id provided", (done)->
overleaf_id = 2345
@handler.createBlankProject ownerId, projectName, overleaf_id, (err, project)->
project.overleaf.history.id.should.equal overleaf_id
done()
it "should set the language from the user", (done)->
@handler.createBlankProject ownerId, projectName, (err, project)->
project.spellCheckLanguage.should.equal "de"
done()
it "should set the imageName to currentImageName if set", (done) ->
@Settings.currentImageName = "mock-image-name"
@handler.createBlankProject ownerId, projectName, (err, project)=>
project.imageName.should.equal @Settings.currentImageName
done()
it "should not set the imageName if no currentImageName", (done) ->
@Settings.currentImageName = null
@handler.createBlankProject ownerId, projectName, (err, project)=>
@@ -96,21 +117,21 @@ describe 'ProjectCreationHandler', ->
beforeEach ->
@ProjectModel::save = sinon.stub().callsArgWith(0, new Error("something went wrong"))
@handler.createBlankProject ownerId, projectName, @callback
it 'should return the error to the callback', ->
should.exist @callback.args[0][0]
describe "with an invalid name", ->
beforeEach ->
@ProjectDetailsHandler.validateProjectName = sinon.stub().yields(new Error("bad name"))
@handler.createBlankProject ownerId, projectName, @callback
it 'should return the error to the callback', ->
should.exist @callback.args[0][0]
it 'should not try to create the project', ->
@ProjectModel::save.called.should.equal false
describe 'Creating a basic project', ->
beforeEach ->
@@ -12,7 +12,7 @@ describe 'ProjectDetailsHandler', ->
beforeEach ->
@project_id = "321l3j1kjkjl"
@user_id = "user-id-123"
@project =
@project =
name: "project"
description: "this is a great project"
something:"should not exist"
@@ -20,7 +20,7 @@ describe 'ProjectDetailsHandler', ->
owner_ref: @user_id
@user =
features: "mock-features"
@ProjectGetter =
@ProjectGetter =
getProjectWithoutDocLines: sinon.stub().callsArgWith(1, null, @project)
getProject: sinon.stub().callsArgWith(2, null, @project)
@ProjectModel =
@@ -43,7 +43,7 @@ describe 'ProjectDetailsHandler', ->
describe "getDetails", ->
it "should find the project and owner", (done)->
@handler.getDetails @project_id, (err, details)=>
@handler.getDetails @project_id, (err, details)=>
details.name.should.equal @project.name
details.description.should.equal @project.description
details.compiler.should.equal @project.compiler
@@ -51,6 +51,13 @@ describe 'ProjectDetailsHandler', ->
assert.equal(details.something, undefined)
done()
it "should find overleaf metadata if it exists", (done)->
@project.overleaf = { id: 'id' }
@handler.getDetails @project_id, (err, details)=>
details.overleaf.should.equal @project.overleaf
assert.equal(details.something, undefined)
done()
it "should return an error for a non-existent project", (done)->
@ProjectGetter.getProject.callsArg(2, null, null)
err = new Errors.NotFoundError("project not found")
@@ -80,7 +87,7 @@ describe 'ProjectDetailsHandler', ->
@handler.getProjectDescription @project_id, (returnedErr, returnedDescription)=>
err.should.equal returnedErr
description.should.equal returnedDescription
done()
done()
describe "setProjectDescription", ->
@@ -111,7 +118,7 @@ describe 'ProjectDetailsHandler', ->
@handler.renameProject @project_id, @newName, =>
@tpdsUpdateSender.moveEntity.calledWith({project_id:@project_id, project_name:@project.name, newProjectName:@newName}).should.equal true
done()
it "should not do anything with an invalid name", (done) ->
@handler.validateProjectName = sinon.stub().yields(new Error("invalid name"))
@handler.renameProject @project_id, @newName, =>
@@ -120,6 +127,12 @@ describe 'ProjectDetailsHandler', ->
done()
describe "validateProjectName", ->
it "should reject undefined names", (done) ->
@handler.validateProjectName undefined, (error) ->
expect(error).to.exist
done()
it "should reject empty names", (done) ->
@handler.validateProjectName "", (error) ->
expect(error).to.exist
@@ -14,17 +14,17 @@ describe 'ProjectEntityHandler', ->
doc_id = '4eecb1c1bffa66588e0000a2'
folder_id = "4eecaffcbffa66588e000008"
rootFolderId = "4eecaffcbffa66588e000007"
beforeEach ->
@FileStoreHandler =
@FileStoreHandler =
uploadFileFromDisk:(project_id, fileRef, localImagePath, callback)->callback()
copyFile: sinon.stub().callsArgWith(4, null)
@tpdsUpdateSender =
addDoc:sinon.stub().callsArg(1)
addFile:sinon.stub().callsArg(1)
addFolder:sinon.stub().callsArg(1)
@rootFolder =
_id:rootFolderId,
@rootFolder =
_id:rootFolderId,
folders:[
{name:"level1", folders:[]}
]
@@ -46,7 +46,7 @@ describe 'ProjectEntityHandler', ->
@FileModel = class File
constructor:(options)->
{@name} = options
@._id = "file_id"
@._id = "file_id"
@rev = 0
@FolderModel = class Folder
constructor:(options)->
@@ -57,12 +57,12 @@ describe 'ProjectEntityHandler', ->
@ProjectModel.findById = (project_id, callback)=> callback(null, @project)
@ProjectModel.getProject = (project_id, fields, callback)=> callback(null, @project)
@ProjectGetter =
@ProjectGetter =
getProjectWithOnlyFolders : (project_id, callback)=> callback(null, @project)
getProjectWithoutDocLines : (project_id, callback)=> callback(null, @project)
getProject:sinon.stub()
@projectUpdater = markAsUpdated:sinon.stub()
@projectLocator =
@projectLocator =
findElement : sinon.stub()
@settings =
maxEntitiesPerProject:200
@@ -97,8 +97,8 @@ describe 'ProjectEntityHandler', ->
else
cb null, @parentFolder
@ProjectEntityHandler.addFolder = (project_id, parentFolder_id, folderName, callback)=>
callback null, {name:folderName}, @parentFolder_id
callback null, {name:folderName}, @parentFolder_id
it 'should return the root folder if the path is just a slash', (done)->
path = "/"
@ProjectEntityHandler.mkdirp project_id, path, (err, folders, lastFolder)=>
@@ -239,7 +239,7 @@ describe 'ProjectEntityHandler', ->
@ProjectEntityHandler._putElement = sinon.stub().callsArgWith(4, null, path: @pathAfterMove)
@ProjectGetter.getProject.callsArgWith(2, null, @project)
@tpdsUpdateSender.moveEntity = sinon.stub().callsArg(1)
describe "moving a doc", ->
beforeEach (done) ->
@docId = "4eecaffcbffa66588e000009"
@@ -257,10 +257,10 @@ describe 'ProjectEntityHandler', ->
it 'should remove the element from its current position', ->
@ProjectEntityHandler._removeElementFromMongoArray
.calledWith(@ProjectModel, project_id, @path.mongo ).should.equal true
it "should put the element back in the new folder", ->
@ProjectEntityHandler._putElement.calledWith(@project, folder_id, @doc, "docs").should.equal true
it 'should tell the third party data store', ->
@tpdsUpdateSender.moveEntity
.calledWith({
@@ -271,7 +271,7 @@ describe 'ProjectEntityHandler', ->
rev: @doc.rev
})
.should.equal true
describe "moving a folder", ->
beforeEach ->
@folder_id = "folder-to-move"
@@ -294,7 +294,7 @@ describe 'ProjectEntityHandler', ->
else
console.log "UNKNOWN ID", options
sinon.spy @projectLocator, "findElement"
describe "when the destination folder is outside the moving folder", ->
beforeEach (done) ->
@path.fileSystem = "/one/directory"
@@ -318,7 +318,7 @@ describe 'ProjectEntityHandler', ->
@path.mongo
)
.should.equal true
it "should put the element back in the new folder", ->
@ProjectEntityHandler._putElement
.calledWith(
@@ -328,7 +328,7 @@ describe 'ProjectEntityHandler', ->
"folder"
)
.should.equal true
it 'should tell the third party data store', ->
@tpdsUpdateSender.moveEntity
.calledWith({
@@ -339,7 +339,7 @@ describe 'ProjectEntityHandler', ->
rev: @folder.rev
})
.should.equal true
describe "when the destination folder is inside the moving folder", ->
beforeEach ->
@path.fileSystem = "/one/two"
@@ -355,7 +355,7 @@ describe 'ProjectEntityHandler', ->
project: @project
})
.should.equal true
it "should return an error", ->
@callback
.calledWith(new Error("destination folder is a child folder of me"))
@@ -385,16 +385,41 @@ describe 'ProjectEntityHandler', ->
@rev = 5
@version = 42
@ranges = {"mock": "ranges"}
@DocstoreManager.getDoc = sinon.stub().callsArgWith(3, null, @lines, @rev, @version, @ranges)
@ProjectEntityHandler.getDoc project_id, doc_id, @callback
it "should call the docstore", ->
@DocstoreManager.getDoc
.calledWith(project_id, doc_id)
.should.equal true
describe 'without pathname option', ->
beforeEach ->
@ProjectEntityHandler.getDoc project_id, doc_id, @callback
it "should call the docstore", ->
@DocstoreManager.getDoc
.calledWith(project_id, doc_id)
.should.equal true
it "should call the callback with the lines, version and rev", ->
@callback.calledWith(null, @lines, @rev, @version, @ranges).should.equal true
describe 'with pathname option', ->
beforeEach ->
@project = 'a project'
@path = mongo: "mongo.path", fileSystem: "/file/system/path"
@projectLocator.findElement = sinon.stub().callsArgWith(1, null, {}, @path)
@ProjectEntityHandler.getDoc project_id, doc_id, {pathname: true}, @callback
it "should call the project locator", ->
@projectLocator.findElement
.calledWith({project_id: project_id, element_id: doc_id, type: 'doc'})
.should.equal true
it "should call the docstore", ->
@DocstoreManager.getDoc
.calledWith(project_id, doc_id)
.should.equal true
it "should return the pathname if option given", ->
@callback.calledWith(null, @lines, @rev, @version, @ranges, @path.fileSystem).should.equal true
it "should call the callback with the lines, version and rev", ->
@callback.calledWith(null, @lines, @rev, @version, @ranges).should.equal true
describe 'addDoc', ->
beforeEach ->
@@ -876,7 +901,7 @@ describe 'ProjectEntityHandler', ->
path: path
})
.should.equal true
describe "setRootDoc", ->
it "should call Project.update", ->
@project_id = "project-id-123234adfs"
@@ -907,22 +932,22 @@ describe 'ProjectEntityHandler', ->
it 'should copy the file in FileStoreHandler', (done)->
@ProjectEntityHandler._putElement = sinon.stub().callsArgWith(4, null, {path:{fileSystem:"somehintg"}})
@ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)=>
@ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)=>
@FileStoreHandler.copyFile.calledWith(oldProject_id, oldFileRef._id, project_id, fileRef._id).should.equal true
done()
it 'should put file into folder by calling put element', (done)->
@ProjectEntityHandler._putElement = (passedProject, passedFolder_id, passedFileRef, passedType, callback)->
@ProjectEntityHandler._putElement = (passedProject, passedFolder_id, passedFileRef, passedType, callback)->
passedProject._id.should.equal project_id
passedFolder_id.should.equal folder_id
passedFileRef.name.should.equal fileName
passedType.should.equal 'file'
done()
@ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)->
@ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)->
it 'should return doc and parent folder', (done)->
@ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)->
@ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)->
parentFolder.should.equal folder_id
fileRef.name.should.equal fileName
done()
@@ -942,7 +967,7 @@ describe 'ProjectEntityHandler', ->
options.rev.should.equal 0
done()
@ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)->
@ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)->
describe "renameEntity", ->
@@ -1054,7 +1079,7 @@ describe 'ProjectEntityHandler', ->
@folder =
_id: ObjectId()
name: "someFolder"
@doc =
@doc =
_id: ObjectId()
name: "new.tex"
@path = mongo: "mongo.path", fileSystem: "/file/system/old.tex"
@@ -1064,7 +1089,7 @@ describe 'ProjectEntityHandler', ->
describe "updating the project", ->
it "should use the correct mongo path", (done)->
@ProjectEntityHandler._putElement @project, @folder._id, @doc, "docs", (err)=>
@@ -1089,12 +1114,12 @@ describe 'ProjectEntityHandler', ->
done()
it "should error if the element has no _id", (done)->
doc =
doc =
name:"something"
@ProjectEntityHandler._putElement @project, @folder._id, doc, "doc", (err)=>
@ProjectModel.update.called.should.equal false
done()
describe "_countElements", ->
@@ -1109,7 +1134,7 @@ describe 'ProjectEntityHandler', ->
fileRefs:{}
folders: [
{
docs:[_id:1234],
docs:[_id:1234],
fileRefs:[{_id:23123}, {_id:123213}, {_id:2312}]
folders:[
{
@@ -1131,7 +1156,7 @@ describe 'ProjectEntityHandler', ->
}
]
}
]
]
it "should return the correct number", (done)->
@ProjectEntityHandler._countElements @project, (err, count)->
@@ -1142,19 +1167,19 @@ describe 'ProjectEntityHandler', ->
@project.rootFolder[0].folders[0].folders = undefined
@ProjectEntityHandler._countElements @project, (err, count)->
count.should.equal 17
done()
done()
it "should deal with null docs", (done)->
@project.rootFolder[0].folders[0].docs = undefined
@ProjectEntityHandler._countElements @project, (err, count)->
count.should.equal 23
done()
done()
it "should deal with null fileRefs", (done)->
@project.rootFolder[0].folders[0].folders[0].fileRefs = undefined
@ProjectEntityHandler._countElements @project, (err, count)->
count.should.equal 23
done()
done()
@@ -10,24 +10,22 @@ describe "ArchiveManager", ->
beforeEach ->
@logger =
error: sinon.stub()
warn: sinon.stub()
err:->
log: sinon.stub()
@process = new events.EventEmitter
@process.stdout = new events.EventEmitter
@process.stderr = new events.EventEmitter
@child =
spawn: sinon.stub().returns(@process)
@metrics =
Timer: class Timer
done: sinon.stub()
@zipfile = new events.EventEmitter
@zipfile.readEntry = sinon.stub()
@zipfile.close = sinon.stub()
@ArchiveManager = SandboxedModule.require modulePath, requires:
"child_process": @child
"yauzl": @yauzl = {open: sinon.stub().callsArgWith(2, null, @zipfile)}
"logger-sharelatex": @logger
"metrics-sharelatex": @metrics
"fs": @fs = {}
"fs-extra": @fse = {}
describe "extractZipArchive", ->
beforeEach ->
@@ -39,10 +37,10 @@ describe "ArchiveManager", ->
describe "successfully", ->
beforeEach (done) ->
@ArchiveManager.extractZipArchive @source, @destination, done
@process.emit "close"
@zipfile.emit "end"
it "should run unzip", ->
@child.spawn.calledWithExactly("unzip", [@source, "-d", @destination]).should.equal true
it "should run yauzl", ->
@yauzl.open.calledWith(@source).should.equal true
it "should time the unzip", ->
@metrics.Timer::done.called.should.equal true
@@ -50,13 +48,12 @@ describe "ArchiveManager", ->
it "should log the unzip", ->
@logger.log.calledWith(sinon.match.any, "unzipping file").should.equal true
describe "with an error on stderr", ->
describe "with an error in the zip file header", ->
beforeEach (done) ->
@yauzl.open = sinon.stub().callsArgWith(2, new Error("Something went wrong"))
@ArchiveManager.extractZipArchive @source, @destination, (error) =>
@callback(error)
done()
@process.stderr.emit "data", "Something went wrong"
@process.emit "close"
it "should return the callback with an error", ->
@callback.calledWithExactly(new Error("Something went wrong")).should.equal true
@@ -74,60 +71,177 @@ describe "ArchiveManager", ->
it "should return the callback with an error", ->
@callback.calledWithExactly(new Error("zip_too_large")).should.equal true
it "should not call spawn", ->
@child.spawn.called.should.equal false
it "should not call yauzl.open", ->
@yauzl.open.called.should.equal false
describe "with an error on the process", ->
describe "with an error in the extracted files", ->
beforeEach (done) ->
@ArchiveManager.extractZipArchive @source, @destination, (error) =>
@callback(error)
done()
@process.emit "error", new Error("Something went wrong")
@zipfile.emit "error", new Error("Something went wrong")
it "should return the callback with an error", ->
@callback.calledWithExactly(new Error("Something went wrong")).should.equal true
it "should log out the error", ->
@logger.error.called.should.equal true
describe "with a relative extracted file path", ->
beforeEach (done) ->
@zipfile.openReadStream = sinon.stub()
@ArchiveManager.extractZipArchive @source, @destination, (error) =>
@callback(error)
done()
@zipfile.emit "entry", {fileName: "../testfile.txt"}
@zipfile.emit "end"
it "should not write try to read the file entry", ->
@zipfile.openReadStream.called.should.equal false
it "should log out a warning", ->
@logger.warn.called.should.equal true
describe "with an unnormalized extracted file path", ->
beforeEach (done) ->
@zipfile.openReadStream = sinon.stub()
@ArchiveManager.extractZipArchive @source, @destination, (error) =>
@callback(error)
done()
@zipfile.emit "entry", {fileName: "foo/./testfile.txt"}
@zipfile.emit "end"
it "should not write try to read the file entry", ->
@zipfile.openReadStream.called.should.equal false
it "should log out a warning", ->
@logger.warn.called.should.equal true
describe "with a directory entry", ->
beforeEach (done) ->
@zipfile.openReadStream = sinon.stub()
@ArchiveManager.extractZipArchive @source, @destination, (error) =>
@callback(error)
done()
@zipfile.emit "entry", {fileName: "testdir/"}
@zipfile.emit "end"
it "should not write try to read the entry", ->
@zipfile.openReadStream.called.should.equal false
it "should not log out a warning", ->
@logger.warn.called.should.equal false
describe "with an error opening the file read stream", ->
beforeEach (done) ->
@zipfile.openReadStream = sinon.stub().callsArgWith(1, new Error("Something went wrong"))
@writeStream = new events.EventEmitter
@ArchiveManager.extractZipArchive @source, @destination, (error) =>
@callback(error)
done()
@zipfile.emit "entry", {fileName: "testfile.txt"}
@zipfile.emit "end"
it "should return the callback with an error", ->
@callback.calledWithExactly(new Error("Something went wrong")).should.equal true
it "should log out the error", ->
@logger.error.called.should.equal true
it "should close the zipfile", ->
@zipfile.close.called.should.equal true
describe "with an error in the file read stream", ->
beforeEach (done) ->
@readStream = new events.EventEmitter
@readStream.pipe = sinon.stub()
@zipfile.openReadStream = sinon.stub().callsArgWith(1, null, @readStream)
@writeStream = new events.EventEmitter
@fs.createWriteStream = sinon.stub().returns @writeStream
@fse.ensureDir = sinon.stub().callsArg(1)
@ArchiveManager.extractZipArchive @source, @destination, (error) =>
@callback(error)
done()
@zipfile.emit "entry", {fileName: "testfile.txt"}
@readStream.emit "error", new Error("Something went wrong")
@zipfile.emit "end"
it "should return the callback with an error", ->
@callback.calledWithExactly(new Error("Something went wrong")).should.equal true
it "should log out the error", ->
@logger.error.called.should.equal true
it "should close the zipfile", ->
@zipfile.close.called.should.equal true
describe "with an error in the file write stream", ->
beforeEach (done) ->
@readStream = new events.EventEmitter
@readStream.pipe = sinon.stub()
@readStream.unpipe = sinon.stub()
@readStream.destroy = sinon.stub()
@zipfile.openReadStream = sinon.stub().callsArgWith(1, null, @readStream)
@writeStream = new events.EventEmitter
@fs.createWriteStream = sinon.stub().returns @writeStream
@fse.ensureDir = sinon.stub().callsArg(1)
@ArchiveManager.extractZipArchive @source, @destination, (error) =>
@callback(error)
done()
@zipfile.emit "entry", {fileName: "testfile.txt"}
@writeStream.emit "error", new Error("Something went wrong")
@zipfile.emit "end"
it "should return the callback with an error", ->
@callback.calledWithExactly(new Error("Something went wrong")).should.equal true
it "should log out the error", ->
@logger.error.called.should.equal true
it "should unpipe from the readstream", ->
@readStream.unpipe.called.should.equal true
it "should destroy the readstream", ->
@readStream.destroy.called.should.equal true
it "should close the zipfile", ->
@zipfile.close.called.should.equal true
describe "_isZipTooLarge", ->
beforeEach ->
@output = (totalSize)->" Length Date Time Name \n-------- ---- ---- ---- \n241 03-12-16 12:20 main.tex \n108801 03-12-16 12:20 ddd/x1J5kHh.jpg \n-------- ------- \n#{totalSize} 2 files\n"
it "should return false with small output", (done)->
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
isTooLarge.should.equal false
done()
@process.stdout.emit "data", @output("109042")
@process.emit "close"
@zipfile.emit "entry", {uncompressedSize: 109042}
@zipfile.emit "end"
it "should return true with large bytes", (done)->
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
isTooLarge.should.equal true
done()
@process.stdout.emit "data", @output("1090000000000000042")
@process.emit "close"
@zipfile.emit "entry", {uncompressedSize: 1090000000000000042}
@zipfile.emit "end"
it "should return error on no data", (done)->
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
expect(error).to.exist
done()
@process.stdout.emit "data", ""
@process.emit "close"
@zipfile.emit "entry", {}
@zipfile.emit "end"
it "should return error if it didn't get a number", (done)->
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
expect(error).to.exist
done()
@process.stdout.emit "data", @output("total_size_string")
@process.emit "close"
@zipfile.emit "entry", {uncompressedSize:"random-error"}
@zipfile.emit "end"
it "should return error if the is only a bit of data", (done)->
it "should return error if there is no data", (done)->
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
expect(error).to.exist
done()
@process.stdout.emit "data", " Length Date Time Name \n--------"
@process.emit "close"
@zipfile.emit "end"
describe "findTopLevelDirectory", ->
beforeEach ->
@@ -12,9 +12,9 @@ ObjectId = require("mongojs").ObjectId
describe "UserInfoController", ->
beforeEach ->
@UserDeleter =
@UserDeleter =
deleteUser: sinon.stub().callsArgWith(1)
@UserUpdater =
@UserUpdater =
updatePersonalInfo: sinon.stub()
@sanitizer = escape:(v)->v
sinon.spy @sanitizer, "escape"
@@ -50,23 +50,47 @@ describe "UserInfoController", ->
.should.equal true
describe "getPersonalInfo", ->
beforeEach ->
@user_id = ObjectId().toString()
@user =
_id: ObjectId(@user_id)
@req.params = user_id: @user_id
describe "when the user exists", ->
describe "when the user exists with sharelatex id", ->
beforeEach ->
@user_id = ObjectId().toString()
@user =
_id: ObjectId(@user_id)
@req.params = user_id: @user_id
@UserGetter.getUser = sinon.stub().callsArgWith(2, null, @user)
@UserInfoController.sendFormattedPersonalInfo = sinon.stub()
@UserInfoController.getPersonalInfo(@req, @res, @next)
it "should look up the user in the database", ->
@UserGetter.getUser
.calledWith(@user_id, { _id: true, first_name: true, last_name: true, email: true })
.calledWith(
{ _id: ObjectId(@user_id) },
{ _id: true, first_name: true, last_name: true, email: true }
).should.equal true
it "should send the formatted details back to the client", ->
@UserInfoController.sendFormattedPersonalInfo
.calledWith(@user, @res, @next)
.should.equal true
describe "when the user exists with overleaf id", ->
beforeEach ->
@user_id = 12345
@user =
_id: ObjectId()
overleaf:
id: @user_id
@req.params = user_id: @user_id.toString()
@UserGetter.getUser = sinon.stub().callsArgWith(2, null, @user)
@UserInfoController.sendFormattedPersonalInfo = sinon.stub()
@UserInfoController.getPersonalInfo(@req, @res, @next)
it "should look up the user in the database", ->
@UserGetter.getUser
.calledWith(
{ "overleaf.id": @user_id },
{ _id: true, first_name: true, last_name: true, email: true }
).should.equal true
it "should send the formatted details back to the client", ->
@UserInfoController.sendFormattedPersonalInfo
.calledWith(@user, @res, @next)
@@ -74,13 +98,24 @@ describe "UserInfoController", ->
describe "when the user does not exist", ->
beforeEach ->
@user_id = ObjectId().toString()
@req.params = user_id: @user_id
@UserGetter.getUser = sinon.stub().callsArgWith(2, null, null)
@UserInfoController.sendFormattedPersonalInfo = sinon.stub()
@UserInfoController.getPersonalInfo(@req, @res, @next)
it "should return 404 to the client", ->
@res.statusCode.should.equal 404
describe "when the user id is invalid", ->
beforeEach ->
@user_id = "invalid"
@req.params = user_id: @user_id
@UserGetter.getUser = sinon.stub().callsArgWith(2, null, null)
@UserInfoController.getPersonalInfo(@req, @res, @next)
it "should return 400 to the client", ->
@res.statusCode.should.equal 400
describe "sendFormattedPersonalInfo", ->
beforeEach ->
@user =
@@ -4,6 +4,9 @@ User = require "./helpers/User"
request = require "./helpers/request"
settings = require "settings-sharelatex"
MockDocstoreApi = require './helpers/MockDocstoreApi'
MockDocUpdaterApi = require './helpers/MockDocUpdaterApi'
try_read_access = (user, project_id, test, callback) ->
async.series [
(cb) ->
@@ -0,0 +1,15 @@
express = require("express")
app = express()
module.exports = MockDocUpdaterApi =
run: () ->
app.post "/project/:project_id/flush", (req, res, next) =>
res.sendStatus 200
app.listen 3003, (error) ->
throw error if error?
.on "error", (error) ->
console.error "error starting MockDocUpdaterApi:", error.message
process.exit(1)
MockDocUpdaterApi.run()
@@ -0,0 +1,33 @@
express = require("express")
bodyParser = require "body-parser"
app = express()
module.exports = MockDocStoreApi =
docs: {}
run: () ->
app.post "/project/:project_id/doc/:doc_id", bodyParser.json(), (req, res, next) =>
{project_id, doc_id} = req.params
{lines, version, ranges} = req.body
@docs[project_id] ?= {}
@docs[project_id][doc_id] = {lines, version, ranges}
@docs[project_id][doc_id].rev ?= 0
@docs[project_id][doc_id].rev += 1
res.json {
modified: true
rev: @docs[project_id][doc_id].rev
}
app.get "/project/:project_id/doc", (req, res, next) =>
docs = (doc for doc_id, doc of @docs[req.params.project_id])
res.send JSON.stringify docs
app.listen 3016, (error) ->
throw error if error?
.on "error", (error) ->
console.error "error starting MockDocStoreApi:", error.message
process.exit(1)
MockDocStoreApi.run()
+26
View File
@@ -0,0 +1,26 @@
#! /usr/bin/env bash
# If you're running on OS X, you probably need to manually
# 'rm -r node_modules/bcrypt; npm install bcrypt' inside
# the docker container, before it will start.
# npm rebuild bcrypt
echo ">> Starting server..."
grunt --no-color forever:app:start
echo ">> Server started"
sleep 5
echo ">> Running acceptance tests..."
grunt --no-color mochaTest:acceptance
_test_exit_code=$?
echo ">> Killing server"
grunt --no-color forever:app:stop
echo ">> Done"
exit $_test_exit_code