diff --git a/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee b/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee index beac6f2b45..bb9e013abe 100644 --- a/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee +++ b/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee @@ -10,19 +10,19 @@ module.exports = CollaboratorsInviteController = inviteToProject: (req, res, next) -> projectId = req.params.Project_id email = req.body.email - sendingUserId = req.session?.user_id + sendingUserId = req.session?.user?._id logger.log {projectId, email, sendingUserId}, "inviting to project" - LimitationsManager.canAddXCollaborators project_id, 1, (error, allowed) => + LimitationsManager.canAddXCollaborators projectId, 1, (error, allowed) => return next(error) if error? if !allowed logger.log {projectId, email, sendingUserId}, "not allowed to invite more users to project" - return res.json {} + return res.json {inviteId: null} {email, privileges} = req.body email = mimelib.parseAddresses(email or "")[0]?.address?.toLowerCase() if !email? or email == "" logger.log {projectId, email, sendingUserId}, "invalid email address" - return res.status(400).send("invalid email address") - CollaboratorsInviteHandler.inviteToProject projectId, sendingUserId, email, priveleges, (err, invite) -> + return res.sendStatus(400) + CollaboratorsInviteHandler.inviteToProject projectId, sendingUserId, email, privileges, (err, invite) -> if err? logger.err {projectId, email, sendingUserId}, "error creating project invite" return next(err) @@ -37,7 +37,7 @@ module.exports = CollaboratorsInviteController = if err? logger.err {projectId, inviteId}, "error revoking invite" return next(err) - res.status(201).send() + res.sendStatus(201) viewInvite: (req, res, next) -> projectId = req.params.Project_id @@ -49,7 +49,7 @@ module.exports = CollaboratorsInviteController = return next(err) if !invite logger.log {projectId, token}, "no invite found for token" - return res.redirect("/") + return res.sendStatus(404) res.render "project/invite", {invite} acceptInvite: (req, res, next) -> @@ -59,6 +59,6 @@ module.exports = CollaboratorsInviteController = logger.log {projectId, inviteId}, "accepting invite" CollaboratorsInviteHandler.acceptInvite projectId, inviteId, currentUser, (err) -> if err? - logger.err {projectId, token}, "error getting invite by token" + logger.err {projectId, inviteId}, "error getting invite by token" return next(err) - rest.status(201).send() + res.sendStatus(201) diff --git a/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee b/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee index f22bcb4c4f..2d9932c093 100644 --- a/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee +++ b/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee @@ -11,7 +11,7 @@ Errors = require "../Errors/Errors" module.experts = CollaboratorsInviteHandler = - inviteToProject: (projectId, sendingUserId, email, priveleges, callback=(err,invite)->) -> + inviteToProject: (projectId, sendingUserId, email, privileges, callback=(err,invite)->) -> revokeInvite: (projectId, inviteId, callback=(err)->) -> diff --git a/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteControllerTests.coffee b/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteControllerTests.coffee index fb9e77f952..3a7ad86761 100644 --- a/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteControllerTests.coffee @@ -18,12 +18,106 @@ describe "CollaboratorsInviteController", -> '../Subscription/LimitationsManager' : @LimitationsManager = {} '../Project/ProjectEditorHandler' : @ProjectEditorHandler = {} '../User/UserGetter': @UserGetter = {} + 'logger-sharelatex': @logger = {err: sinon.stub(), error: sinon.stub(), log: sinon.stub()} @res = new MockResponse() @req = new MockRequest() @project_id = "project-id-123" @callback = sinon.stub() + describe 'inviteToProject', -> + + beforeEach -> + @targetEmail = "user@example.com" + @req.params = + Project_id: @project_id + @req.session = + user: _id: @current_user_id = "current-user-id" + @req.body = + email: @targetEmail + privileges: @privileges = "readAndWrite" + @res.json = sinon.stub() + @res.sendStatus = sinon.stub() + @invite = { + _id: ObjectId(), + token: "htnseuthaouse", + sendingUserId: @current_user_id, + projectId: @targetEmail, + targetEmail: 'user@example.com' + createdAt: new Date(), + expiresAt: new Date() + } + @LimitationsManager.canAddXCollaborators = sinon.stub().callsArgWith(2, null, true) + @CollaboratorsInviteHandler.inviteToProject = sinon.stub().callsArgWith(4, null, @invite) + @callback = sinon.stub() + @next = sinon.stub() + + describe 'when all goes well', -> + + beforeEach -> + @LimitationsManager.canAddXCollaborators = sinon.stub().callsArgWith(2, null, true) + @CollaboratorsInviteController.inviteToProject @req, @res, @next + + it 'should produce json response', -> + @res.json.callCount.should.equal 1 + ({inviteId: @invite._id}).should.deep.equal(@res.json.firstCall.args[0]) + + it 'should have called canAddXCollaborators', -> + @LimitationsManager.canAddXCollaborators.callCount.should.equal 1 + @LimitationsManager.canAddXCollaborators.calledWith(@project_id).should.equal true + + it 'should have called inviteToProject', -> + @CollaboratorsInviteHandler.inviteToProject.callCount.should.equal 1 + @CollaboratorsInviteHandler.inviteToProject.calledWith(@project_id,@current_user_id,@targetEmail,@privileges).should.equal true + + describe 'when the user is not allowed to add more collaborators', -> + + beforeEach -> + @LimitationsManager.canAddXCollaborators = sinon.stub().callsArgWith(2, null, false) + @CollaboratorsInviteController.inviteToProject @req, @res, @next + + it 'should produce json response without inviteId', -> + @res.json.callCount.should.equal 1 + ({inviteId: null}).should.deep.equal(@res.json.firstCall.args[0]) + + it 'should not have called inviteToProject', -> + @CollaboratorsInviteHandler.inviteToProject.callCount.should.equal 0 + + describe 'when canAddXCollaborators produces an error', -> + + beforeEach -> + @err = new Error('woops') + @LimitationsManager.canAddXCollaborators = sinon.stub().callsArgWith(2, @err) + @CollaboratorsInviteController.inviteToProject @req, @res, @next + + it 'should call next with an error', -> + @next.callCount.should.equal 1 + @next.calledWith(@err).should.equal true + + it 'should not have called inviteToProject', -> + @CollaboratorsInviteHandler.inviteToProject.callCount.should.equal 0 + + describe 'when inviteToProject produces an error', -> + + beforeEach -> + @err = new Error('woops') + @CollaboratorsInviteHandler.inviteToProject = sinon.stub().callsArgWith(4, @err) + @CollaboratorsInviteController.inviteToProject @req, @res, @next + + it 'should call next with an error', -> + @next.callCount.should.equal 1 + @next.calledWith(@err).should.equal true + + it 'should have called canAddXCollaborators', -> + @LimitationsManager.canAddXCollaborators.callCount.should.equal 1 + @LimitationsManager.canAddXCollaborators.calledWith(@project_id).should.equal true + + it 'should have called inviteToProject', -> + @CollaboratorsInviteHandler.inviteToProject.callCount.should.equal 1 + @CollaboratorsInviteHandler.inviteToProject.calledWith(@project_id,@current_user_id,@targetEmail,@privileges).should.equal true + + ## + describe "viewInvite", -> beforeEach -> @@ -33,6 +127,7 @@ describe "CollaboratorsInviteController", -> @req.session = user: _id: @current_user_id = "current-user-id" @res.render = sinon.stub() + @res.sendStatus = sinon.stub() @invite = { _id: ObjectId(), token: "htnseuthaouse", @@ -54,4 +149,129 @@ describe "CollaboratorsInviteController", -> it 'should render the view template', -> @res.render.callCount.should.equal 1 - @res.render.firstCall.args[0].should.equal 'project/invite' + @res.render.calledWith('project/invite').should.equal true + + it 'should not call next', -> + @next.callCount.should.equal 0 + + it 'should call getInviteByToken', -> + @CollaboratorsInviteHandler.getInviteByToken.callCount.should.equal 1 + + describe 'when the getInviteByToken produces an error', -> + + beforeEach -> + @err = new Error('woops') + @CollaboratorsInviteHandler.getInviteByToken.callsArgWith(2, @err) + @CollaboratorsInviteController.viewInvite @req, @res, @next + + it 'should call next with the error', -> + @next.callCount.should.equal 1 + @next.calledWith(@err).should.equal true + + it 'should call getInviteByToken', -> + @CollaboratorsInviteHandler.getInviteByToken.callCount.should.equal 1 + + describe 'when the getInviteByToken does not produce an invite', -> + + beforeEach -> + @CollaboratorsInviteHandler.getInviteByToken.callsArgWith(2, null, null) + @CollaboratorsInviteController.viewInvite @req, @res, @next + + it 'should produce a 404 response', -> + @res.sendStatus.callCount.should.equal 1 + @res.sendStatus.calledWith(404).should.equal true + + it 'should not render the view template', -> + @res.render.callCount.should.equal 0 + + it 'should not call next', -> + @next.callCount.should.equal 0 + + it 'should call getInviteByToken', -> + @CollaboratorsInviteHandler.getInviteByToken.callCount.should.equal 1 + + describe "revokeInvite", -> + + beforeEach -> + @req.params = + Project_id: @project_id + invite_id: @invite_id = "thuseoautoh" + @req.session = + user: _id: @current_user_id = "current-user-id" + @res.render = sinon.stub() + @res.sendStatus = sinon.stub() + @CollaboratorsInviteHandler.revokeInvite = sinon.stub().callsArgWith(2, null) + @callback = sinon.stub() + @next = sinon.stub() + + describe 'when revokeInvite does not produce an error', -> + + beforeEach -> + @CollaboratorsInviteController.revokeInvite @req, @res, @next + + it 'should produce a 201 response', -> + @res.sendStatus.callCount.should.equal 1 + @res.sendStatus.calledWith(201).should.equal true + + it 'should have called revokeInvite', -> + @CollaboratorsInviteHandler.revokeInvite.callCount.should.equal 1 + + describe 'when revokeInvite produces an error', -> + + beforeEach -> + @err = new Error('woops') + @CollaboratorsInviteHandler.revokeInvite = sinon.stub().callsArgWith(2, @err) + @CollaboratorsInviteController.revokeInvite @req, @res, @next + + it 'should not produce a 201 response', -> + @res.sendStatus.callCount.should.equal 0 + + it 'should call next with the error', -> + @next.callCount.should.equal 1 + @next.calledWith(@err).should.equal true + + it 'should have called revokeInvite', -> + @CollaboratorsInviteHandler.revokeInvite.callCount.should.equal 1 + + describe "acceptInvite", -> + + beforeEach -> + @req.params = + Project_id: @project_id + invite_id: @invite_id = "thuseoautoh" + @req.session = + user: _id: @current_user_id = "current-user-id" + @res.render = sinon.stub() + @res.sendStatus = sinon.stub() + @CollaboratorsInviteHandler.acceptInvite = sinon.stub().callsArgWith(3, null) + @callback = sinon.stub() + @next = sinon.stub() + + describe 'when acceptInvite does not produce an error', -> + + beforeEach -> + @CollaboratorsInviteController.acceptInvite @req, @res, @next + + it 'should produce a 201 response', -> + @res.sendStatus.callCount.should.equal 1 + @res.sendStatus.calledWith(201).should.equal true + + it 'should have called acceptInvite', -> + @CollaboratorsInviteHandler.acceptInvite.callCount.should.equal 1 + + describe 'when revokeInvite produces an error', -> + + beforeEach -> + @err = new Error('woops') + @CollaboratorsInviteHandler.acceptInvite = sinon.stub().callsArgWith(3, @err) + @CollaboratorsInviteController.acceptInvite @req, @res, @next + + it 'should not produce a 201 response', -> + @res.sendStatus.callCount.should.equal 0 + + it 'should call next with the error', -> + @next.callCount.should.equal 1 + @next.calledWith(@err).should.equal true + + it 'should have called acceptInvite', -> + @CollaboratorsInviteHandler.acceptInvite.callCount.should.equal 1