|
|
@@ -0,0 +1,267 @@
|
|
|
+SandboxedModule = require('sandboxed-module')
|
|
|
+should = require('chai').should()
|
|
|
+sinon = require 'sinon'
|
|
|
+expect = require("chai").expect
|
|
|
+querystring = require 'querystring'
|
|
|
+modulePath = "../../../../app/js/Features/Subscription/TeamInvitesHandler"
|
|
|
+
|
|
|
+ObjectId = require("mongojs").ObjectId
|
|
|
+Errors = require("../../../../app/js/Features/Errors/Errors")
|
|
|
+
|
|
|
+describe "TeamInvitesHandler", ->
|
|
|
+ beforeEach ->
|
|
|
+ @manager = {
|
|
|
+ id: "666666",
|
|
|
+ first_name: "Daenerys"
|
|
|
+ last_name: "Targaryen"
|
|
|
+ email: "daenerys@example.com"
|
|
|
+ }
|
|
|
+
|
|
|
+ @token = "aaaaaaaaaaaaaaaaaaaaaa"
|
|
|
+
|
|
|
+ @teamInvite = {
|
|
|
+ email: "jorah@example.com",
|
|
|
+ token: @token,
|
|
|
+ }
|
|
|
+
|
|
|
+ @subscription = {
|
|
|
+ id: "55153a8014829a865bbf700d",
|
|
|
+ admin_id: @manager.id,
|
|
|
+ groupPlan: true,
|
|
|
+ member_ids: [],
|
|
|
+ teamInvites: [ @teamInvite ],
|
|
|
+ save: sinon.stub().yields(null),
|
|
|
+ }
|
|
|
+
|
|
|
+ @SubscriptionLocator = {
|
|
|
+ getUsersSubscription: sinon.stub(),
|
|
|
+ getSubscription: sinon.stub().yields(null, @subscription)
|
|
|
+ }
|
|
|
+
|
|
|
+ @UserGetter = {
|
|
|
+ getUser: sinon.stub().yields(),
|
|
|
+ getUserByAnyEmail: sinon.stub().yields()
|
|
|
+ }
|
|
|
+
|
|
|
+ @SubscriptionUpdater = {
|
|
|
+ addUserToGroup: sinon.stub().yields()
|
|
|
+ }
|
|
|
+
|
|
|
+ @LimitationsManager = {
|
|
|
+ teamHasReachedMemberLimit: sinon.stub().returns(false)
|
|
|
+ }
|
|
|
+
|
|
|
+ @Subscription = {
|
|
|
+ findOne: sinon.stub().yields()
|
|
|
+ update: sinon.stub().yields()
|
|
|
+ }
|
|
|
+
|
|
|
+ @EmailHandler = {
|
|
|
+ sendEmail: sinon.stub().yields(null)
|
|
|
+ }
|
|
|
+
|
|
|
+ @newToken = "bbbbbbbbb"
|
|
|
+
|
|
|
+ @crypto = {
|
|
|
+ randomBytes: =>
|
|
|
+ toString: sinon.stub().returns(@newToken)
|
|
|
+ }
|
|
|
+
|
|
|
+ @UserGetter.getUser.withArgs(@manager.id).yields(null, @manager)
|
|
|
+ @UserGetter.getUserByAnyEmail.withArgs(@manager.email).yields(null, @manager)
|
|
|
+
|
|
|
+ @SubscriptionLocator.getUsersSubscription.yields(null, @subscription)
|
|
|
+ @Subscription.findOne.yields(null, @subscription)
|
|
|
+
|
|
|
+ @TeamInvitesHandler = SandboxedModule.require modulePath, requires:
|
|
|
+ "logger-sharelatex": { log: -> }
|
|
|
+ "crypto": @crypto
|
|
|
+ "settings-sharelatex": { siteUrl: "http://example.com" }
|
|
|
+ "../../models/TeamInvite": { TeamInvite: @TeamInvite = {} }
|
|
|
+ "../../models/Subscription": { Subscription: @Subscription }
|
|
|
+ "../User/UserGetter": @UserGetter
|
|
|
+ "./SubscriptionLocator": @SubscriptionLocator
|
|
|
+ "./SubscriptionUpdater": @SubscriptionUpdater
|
|
|
+ "./LimitationsManager": @LimitationsManager
|
|
|
+ "../Email/EmailHandler": @EmailHandler
|
|
|
+ "../Errors/Errors": Errors
|
|
|
+
|
|
|
+ describe "getInvite", ->
|
|
|
+ it "returns the invite if there's one", (done) ->
|
|
|
+ @TeamInvitesHandler.getInvite @token, (err, invite, subscription) =>
|
|
|
+ expect(err).to.eq(null)
|
|
|
+ expect(invite).to.deep.eq(@teamInvite)
|
|
|
+ expect(subscription).to.deep.eq(@subscription)
|
|
|
+ done()
|
|
|
+
|
|
|
+ it "returns teamNotFound if there's none", (done) ->
|
|
|
+ @Subscription.findOne = sinon.stub().yields(null, null)
|
|
|
+
|
|
|
+ @TeamInvitesHandler.getInvite @token, (err, invite, subscription) ->
|
|
|
+ expect(err).to.be.instanceof(Errors.NotFoundError)
|
|
|
+ done()
|
|
|
+
|
|
|
+ describe "createInvite", ->
|
|
|
+ it "adds the team invite to the subscription", (done) ->
|
|
|
+ @TeamInvitesHandler.createInvite @manager.id, "John.Snow@example.com", (err, invite) =>
|
|
|
+ expect(err).to.eq(null)
|
|
|
+ expect(invite.token).to.eq(@newToken)
|
|
|
+ expect(invite.email).to.eq("john.snow@example.com")
|
|
|
+ expect(invite.inviterName).to.eq("Daenerys Targaryen (daenerys@example.com)")
|
|
|
+ expect(@subscription.teamInvites).to.deep.include(invite)
|
|
|
+ done()
|
|
|
+
|
|
|
+ it "sends an email", (done) ->
|
|
|
+ @TeamInvitesHandler.createInvite @manager.id, "John.Snow@example.com", (err, invite) =>
|
|
|
+ @EmailHandler.sendEmail.calledWith("verifyEmailToJoinTeam",
|
|
|
+ sinon.match({
|
|
|
+ to: "john.snow@example.com",
|
|
|
+ inviterName: "Daenerys Targaryen (daenerys@example.com)",
|
|
|
+ acceptInviteUrl: "http://example.com/subscription/invites/#{@newToken}/"
|
|
|
+ })
|
|
|
+ ).should.equal true
|
|
|
+ done()
|
|
|
+
|
|
|
+ it "refreshes the existing invite if the email has already been invited", (done) ->
|
|
|
+ originalInvite = Object.assign({}, @teamInvite)
|
|
|
+
|
|
|
+ @TeamInvitesHandler.createInvite @manager.id, originalInvite.email, (err, invite) =>
|
|
|
+ expect(err).to.eq(null)
|
|
|
+ expect(invite).to.exist
|
|
|
+
|
|
|
+ expect(@subscription.teamInvites.length).to.eq 1
|
|
|
+ expect(@subscription.teamInvites).to.deep.include invite
|
|
|
+
|
|
|
+ expect(invite.email).to.eq originalInvite.email
|
|
|
+
|
|
|
+ @subscription.save.calledOnce.should.eq true
|
|
|
+
|
|
|
+ done()
|
|
|
+
|
|
|
+ it "removes any legacy invite from the subscription", (done) ->
|
|
|
+ @TeamInvitesHandler.createInvite @manager.id, "John.Snow@example.com", (err, invite) =>
|
|
|
+ @Subscription.update.calledWith(
|
|
|
+ { _id: new ObjectId("55153a8014829a865bbf700d") },
|
|
|
+ { '$pull': { invited_emails: "john.snow@example.com" } }
|
|
|
+ ).should.eq true
|
|
|
+ done()
|
|
|
+
|
|
|
+ describe "createDomainInvite", ->
|
|
|
+ beforeEach ->
|
|
|
+ @licence =
|
|
|
+ subscription_id: @subscription.id
|
|
|
+ name: "Team Daenerys"
|
|
|
+
|
|
|
+ @user =
|
|
|
+ email: "John.Snow@example.com"
|
|
|
+
|
|
|
+ it "adds the team invite to the subscription", (done) ->
|
|
|
+ @TeamInvitesHandler.createDomainInvite @user, @licence, (err, invite) =>
|
|
|
+ expect(err).to.eq(null)
|
|
|
+ expect(invite.token).to.eq(@newToken)
|
|
|
+ expect(invite.email).to.eq("john.snow@example.com")
|
|
|
+ expect(invite.inviterName).to.eq("Team Daenerys")
|
|
|
+ expect(@subscription.teamInvites).to.deep.include(invite)
|
|
|
+ done()
|
|
|
+
|
|
|
+ it "sends an email", (done) ->
|
|
|
+ @TeamInvitesHandler.createDomainInvite @user, @licence, (err, invite) =>
|
|
|
+ @EmailHandler.sendEmail.calledWith("verifyEmailToJoinTeam",
|
|
|
+ sinon.match({
|
|
|
+ to: "john.snow@example.com"
|
|
|
+ inviterName: "Team Daenerys"
|
|
|
+ acceptInviteUrl: "http://example.com/subscription/invites/#{@newToken}/"
|
|
|
+ })
|
|
|
+ ).should.equal true
|
|
|
+ done()
|
|
|
+
|
|
|
+ describe "acceptInvite", ->
|
|
|
+ beforeEach ->
|
|
|
+ @user = {
|
|
|
+ id: "123456789",
|
|
|
+ first_name: "Tyrion",
|
|
|
+ last_name: "Lannister",
|
|
|
+ email: "tyrion@example.com"
|
|
|
+ }
|
|
|
+
|
|
|
+ @UserGetter.getUserByAnyEmail.withArgs(@user.email).yields(null, @user)
|
|
|
+
|
|
|
+ @subscription.teamInvites.push({
|
|
|
+ email: "john.snow@example.com",
|
|
|
+ token: "dddddddd",
|
|
|
+ inviterName: "Daenerys Targaryen (daenerys@example.com)"
|
|
|
+ })
|
|
|
+
|
|
|
+ it "adds the user to the team", (done) ->
|
|
|
+ @TeamInvitesHandler.acceptInvite "dddddddd", @user.id, =>
|
|
|
+ @SubscriptionUpdater.addUserToGroup.calledWith(@manager.id, @user.id).should.eq true
|
|
|
+ done()
|
|
|
+
|
|
|
+ it "removes the invite from the subscription", (done) ->
|
|
|
+ @TeamInvitesHandler.acceptInvite "dddddddd", @user.id, =>
|
|
|
+ @Subscription.update.calledWith(
|
|
|
+ { _id: new ObjectId("55153a8014829a865bbf700d") },
|
|
|
+ { '$pull': { teamInvites: { email: 'john.snow@example.com' } } }
|
|
|
+ ).should.eq true
|
|
|
+ done()
|
|
|
+
|
|
|
+ describe "revokeInvite", ->
|
|
|
+ it "removes the team invite from the subscription", (done) ->
|
|
|
+ @TeamInvitesHandler.revokeInvite @manager.id, "jorah@example.com", =>
|
|
|
+ @Subscription.update.calledWith(
|
|
|
+ { _id: new ObjectId("55153a8014829a865bbf700d") },
|
|
|
+ { '$pull': { teamInvites: { email: "jorah@example.com" } } }
|
|
|
+ ).should.eq true
|
|
|
+
|
|
|
+ @Subscription.update.calledWith(
|
|
|
+ { _id: new ObjectId("55153a8014829a865bbf700d") },
|
|
|
+ { '$pull': { invited_emails: "jorah@example.com" } }
|
|
|
+ ).should.eq true
|
|
|
+ done()
|
|
|
+
|
|
|
+ describe "createTeamInvitesForLegacyInvitedEmail", (done) ->
|
|
|
+ beforeEach ->
|
|
|
+ @subscription.invited_emails = ["eddard@example.com", "robert@example.com"]
|
|
|
+ @TeamInvitesHandler.createInvite = sinon.stub().yields(null)
|
|
|
+ @SubscriptionLocator.getGroupsWithEmailInvite = sinon.stub().yields(null, [@subscription])
|
|
|
+
|
|
|
+ it "sends an invitation email to addresses in the legacy invited_emails field", (done) ->
|
|
|
+ @TeamInvitesHandler.createTeamInvitesForLegacyInvitedEmail "eddard@example.com", (err, invite) =>
|
|
|
+ expect(err).not.to.exist
|
|
|
+
|
|
|
+ @TeamInvitesHandler.createInvite.calledWith(
|
|
|
+ @subscription.admin_id,
|
|
|
+ "eddard@example.com"
|
|
|
+ ).should.eq true
|
|
|
+
|
|
|
+ @TeamInvitesHandler.createInvite.callCount.should.eq 1
|
|
|
+
|
|
|
+ done()
|
|
|
+
|
|
|
+ describe "validation", ->
|
|
|
+ it "doesn't create an invite if the team limit has been reached", (done) ->
|
|
|
+ @LimitationsManager.teamHasReachedMemberLimit = sinon.stub().returns(true)
|
|
|
+ @TeamInvitesHandler.createInvite @manager.id, "John.Snow@example.com", (err, invite) =>
|
|
|
+ expect(err).to.deep.equal(limitReached: true)
|
|
|
+ done()
|
|
|
+
|
|
|
+ it "doesn't create an invite if the subscription is not in a group plan", (done) ->
|
|
|
+ @subscription.groupPlan = false
|
|
|
+ @TeamInvitesHandler.createInvite @manager.id, "John.Snow@example.com", (err, invite) =>
|
|
|
+ expect(err).to.deep.equal(wrongPlan: true)
|
|
|
+ done()
|
|
|
+
|
|
|
+ it "doesn't create an invite if the user is already part of the team", (done) ->
|
|
|
+ member = {
|
|
|
+ id: "1a2b",
|
|
|
+ _id: "1a2b",
|
|
|
+ email: "tyrion@example.com"
|
|
|
+ }
|
|
|
+
|
|
|
+ @subscription.member_ids = [member.id]
|
|
|
+ @UserGetter.getUserByAnyEmail.withArgs(member.email).yields(null, member)
|
|
|
+
|
|
|
+ @TeamInvitesHandler.createInvite @manager.id, "tyrion@example.com", (err, invite) =>
|
|
|
+ expect(err).to.deep.equal(alreadyInTeam: true)
|
|
|
+ expect(invite).not.to.exist
|
|
|
+ done()
|