TeamInvitesHandlerTests.coffee 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. SandboxedModule = require('sandboxed-module')
  2. should = require('chai').should()
  3. sinon = require 'sinon'
  4. expect = require("chai").expect
  5. querystring = require 'querystring'
  6. modulePath = "../../../../app/js/Features/Subscription/TeamInvitesHandler"
  7. ObjectId = require("mongojs").ObjectId
  8. Errors = require("../../../../app/js/Features/Errors/Errors")
  9. describe "TeamInvitesHandler", ->
  10. beforeEach ->
  11. @manager = {
  12. id: "666666",
  13. first_name: "Daenerys"
  14. last_name: "Targaryen"
  15. email: "daenerys@example.com"
  16. }
  17. @token = "aaaaaaaaaaaaaaaaaaaaaa"
  18. @teamInvite = {
  19. email: "jorah@example.com",
  20. token: @token,
  21. }
  22. @subscription = {
  23. id: "55153a8014829a865bbf700d",
  24. _id: new ObjectId("55153a8014829a865bbf700d"),
  25. admin_id: @manager.id,
  26. groupPlan: true,
  27. member_ids: [],
  28. teamInvites: [ @teamInvite ],
  29. save: sinon.stub().yields(null),
  30. }
  31. @SubscriptionLocator = {
  32. getUsersSubscription: sinon.stub(),
  33. getSubscription: sinon.stub().yields(null, @subscription)
  34. }
  35. @UserGetter = {
  36. getUser: sinon.stub().yields(),
  37. getUserByAnyEmail: sinon.stub().yields()
  38. }
  39. @SubscriptionUpdater = {
  40. addUserToGroup: sinon.stub().yields()
  41. }
  42. @LimitationsManager = {
  43. teamHasReachedMemberLimit: sinon.stub().returns(false)
  44. }
  45. @Subscription = {
  46. findOne: sinon.stub().yields()
  47. update: sinon.stub().yields()
  48. }
  49. @EmailHandler = {
  50. sendEmail: sinon.stub().yields(null)
  51. }
  52. @newToken = "bbbbbbbbb"
  53. @crypto = {
  54. randomBytes: =>
  55. toString: sinon.stub().returns(@newToken)
  56. }
  57. @UserGetter.getUser.withArgs(@manager.id).yields(null, @manager)
  58. @UserGetter.getUserByAnyEmail.withArgs(@manager.email).yields(null, @manager)
  59. @SubscriptionLocator.getUsersSubscription.yields(null, @subscription)
  60. @Subscription.findOne.yields(null, @subscription)
  61. @TeamInvitesHandler = SandboxedModule.require modulePath, requires:
  62. "logger-sharelatex": { log: -> }
  63. "crypto": @crypto
  64. "settings-sharelatex": { siteUrl: "http://example.com" }
  65. "../../models/TeamInvite": { TeamInvite: @TeamInvite = {} }
  66. "../../models/Subscription": { Subscription: @Subscription }
  67. "../User/UserGetter": @UserGetter
  68. "./SubscriptionLocator": @SubscriptionLocator
  69. "./SubscriptionUpdater": @SubscriptionUpdater
  70. "./LimitationsManager": @LimitationsManager
  71. "../Email/EmailHandler": @EmailHandler
  72. "../Errors/Errors": Errors
  73. describe "getInvite", ->
  74. it "returns the invite if there's one", (done) ->
  75. @TeamInvitesHandler.getInvite @token, (err, invite, subscription) =>
  76. expect(err).to.eq(null)
  77. expect(invite).to.deep.eq(@teamInvite)
  78. expect(subscription).to.deep.eq(@subscription)
  79. done()
  80. it "returns teamNotFound if there's none", (done) ->
  81. @Subscription.findOne = sinon.stub().yields(null, null)
  82. @TeamInvitesHandler.getInvite @token, (err, invite, subscription) ->
  83. expect(err).to.be.instanceof(Errors.NotFoundError)
  84. done()
  85. describe "createInvite", ->
  86. it "adds the team invite to the subscription", (done) ->
  87. @TeamInvitesHandler.createInvite @manager.id, @subscription, "John.Snow@example.com", (err, invite) =>
  88. expect(err).to.eq(null)
  89. expect(invite.token).to.eq(@newToken)
  90. expect(invite.email).to.eq("john.snow@example.com")
  91. expect(invite.inviterName).to.eq("Daenerys Targaryen (daenerys@example.com)")
  92. expect(@subscription.teamInvites).to.deep.include(invite)
  93. done()
  94. it "sends an email", (done) ->
  95. @TeamInvitesHandler.createInvite @manager.id, @subscription, "John.Snow@example.com", (err, invite) =>
  96. @EmailHandler.sendEmail.calledWith("verifyEmailToJoinTeam",
  97. sinon.match({
  98. to: "john.snow@example.com",
  99. inviterName: "Daenerys Targaryen (daenerys@example.com)",
  100. acceptInviteUrl: "http://example.com/subscription/invites/#{@newToken}/"
  101. })
  102. ).should.equal true
  103. done()
  104. it "refreshes the existing invite if the email has already been invited", (done) ->
  105. originalInvite = Object.assign({}, @teamInvite)
  106. @TeamInvitesHandler.createInvite @manager.id, @subscription, originalInvite.email, (err, invite) =>
  107. expect(err).to.eq(null)
  108. expect(invite).to.exist
  109. expect(@subscription.teamInvites.length).to.eq 1
  110. expect(@subscription.teamInvites).to.deep.include invite
  111. expect(invite.email).to.eq originalInvite.email
  112. @subscription.save.calledOnce.should.eq true
  113. done()
  114. it "removes any legacy invite from the subscription", (done) ->
  115. @TeamInvitesHandler.createInvite @manager.id, @subscription, "John.Snow@example.com", (err, invite) =>
  116. @Subscription.update.calledWith(
  117. { _id: new ObjectId("55153a8014829a865bbf700d") },
  118. { '$pull': { invited_emails: "john.snow@example.com" } }
  119. ).should.eq true
  120. done()
  121. describe "createDomainInvite", ->
  122. beforeEach ->
  123. @licence =
  124. subscription_id: @subscription.id
  125. name: "Team Daenerys"
  126. @user =
  127. email: "John.Snow@example.com"
  128. it "adds the team invite to the subscription", (done) ->
  129. @TeamInvitesHandler.createDomainInvite @user, @licence, (err, invite) =>
  130. expect(err).to.eq(null)
  131. expect(invite.token).to.eq(@newToken)
  132. expect(invite.email).to.eq("john.snow@example.com")
  133. expect(invite.inviterName).to.eq("Team Daenerys")
  134. expect(@subscription.teamInvites).to.deep.include(invite)
  135. done()
  136. it "sends an email", (done) ->
  137. @TeamInvitesHandler.createDomainInvite @user, @licence, (err, invite) =>
  138. @EmailHandler.sendEmail.calledWith("verifyEmailToJoinTeam",
  139. sinon.match({
  140. to: "john.snow@example.com"
  141. inviterName: "Team Daenerys"
  142. acceptInviteUrl: "http://example.com/subscription/invites/#{@newToken}/"
  143. })
  144. ).should.equal true
  145. done()
  146. it "stripe licence from name", (done) ->
  147. @licence.name = 'Foo Licence'
  148. @TeamInvitesHandler.createDomainInvite @user, @licence, (err, invite) =>
  149. @EmailHandler.sendEmail.calledWith("verifyEmailToJoinTeam",
  150. sinon.match({
  151. inviterName: 'Foo'
  152. })
  153. ).should.equal true
  154. done()
  155. it "stripe site licence from name", (done) ->
  156. @licence.name = 'Foo Site Licence'
  157. @TeamInvitesHandler.createDomainInvite @user, @licence, (err, invite) =>
  158. @EmailHandler.sendEmail.calledWith("verifyEmailToJoinTeam",
  159. sinon.match({
  160. inviterName: 'Foo'
  161. })
  162. ).should.equal true
  163. done()
  164. describe "importInvite", ->
  165. beforeEach ->
  166. @sentAt = new Date()
  167. it "can imports an invite from v1", ->
  168. @TeamInvitesHandler.importInvite @subscription, "A-Team", "hannibal@a-team.org",
  169. "secret", @sentAt, (error) =>
  170. expect(error).not.to.exist
  171. @subscription.save.calledOnce.should.eq true
  172. invite = @subscription.teamInvites.find (i) -> i.email == "hannibal@a-team.org"
  173. expect(invite.token).to.eq("secret")
  174. expect(invite.sentAt).to.eq(@sentAt)
  175. describe "acceptInvite", ->
  176. beforeEach ->
  177. @user = {
  178. id: "123456789",
  179. first_name: "Tyrion",
  180. last_name: "Lannister",
  181. email: "tyrion@example.com"
  182. }
  183. @UserGetter.getUserByAnyEmail.withArgs(@user.email).yields(null, @user)
  184. @subscription.teamInvites.push({
  185. email: "john.snow@example.com",
  186. token: "dddddddd",
  187. inviterName: "Daenerys Targaryen (daenerys@example.com)"
  188. })
  189. it "adds the user to the team", (done) ->
  190. @TeamInvitesHandler.acceptInvite "dddddddd", @user.id, =>
  191. @SubscriptionUpdater.addUserToGroup.calledWith(@subscription._id, @user.id).should.eq true
  192. done()
  193. it "removes the invite from the subscription", (done) ->
  194. @TeamInvitesHandler.acceptInvite "dddddddd", @user.id, =>
  195. @Subscription.update.calledWith(
  196. { _id: new ObjectId("55153a8014829a865bbf700d") },
  197. { '$pull': { teamInvites: { email: 'john.snow@example.com' } } }
  198. ).should.eq true
  199. done()
  200. describe "revokeInvite", ->
  201. it "removes the team invite from the subscription", (done) ->
  202. @TeamInvitesHandler.revokeInvite @manager.id, @subscription, "jorah@example.com", =>
  203. @Subscription.update.calledWith(
  204. { _id: new ObjectId("55153a8014829a865bbf700d") },
  205. { '$pull': { teamInvites: { email: "jorah@example.com" } } }
  206. ).should.eq true
  207. @Subscription.update.calledWith(
  208. { _id: new ObjectId("55153a8014829a865bbf700d") },
  209. { '$pull': { invited_emails: "jorah@example.com" } }
  210. ).should.eq true
  211. done()
  212. describe "createTeamInvitesForLegacyInvitedEmail", (done) ->
  213. beforeEach ->
  214. @subscription.invited_emails = ["eddard@example.com", "robert@example.com"]
  215. @TeamInvitesHandler.createInvite = sinon.stub().yields(null)
  216. @SubscriptionLocator.getGroupsWithEmailInvite = sinon.stub().yields(null, [@subscription])
  217. it "sends an invitation email to addresses in the legacy invited_emails field", (done) ->
  218. @TeamInvitesHandler.createTeamInvitesForLegacyInvitedEmail "eddard@example.com", (err, invite) =>
  219. expect(err).not.to.exist
  220. @TeamInvitesHandler.createInvite.calledWith(
  221. @subscription.admin_id,
  222. @subscription,
  223. "eddard@example.com"
  224. ).should.eq true
  225. @TeamInvitesHandler.createInvite.callCount.should.eq 1
  226. done()
  227. describe "validation", ->
  228. it "doesn't create an invite if the team limit has been reached", (done) ->
  229. @LimitationsManager.teamHasReachedMemberLimit = sinon.stub().returns(true)
  230. @TeamInvitesHandler.createInvite @manager.id, @subscription, "John.Snow@example.com", (err, invite) =>
  231. expect(err).to.deep.equal(limitReached: true)
  232. done()
  233. it "doesn't create an invite if the subscription is not in a group plan", (done) ->
  234. @subscription.groupPlan = false
  235. @TeamInvitesHandler.createInvite @manager.id, @subscription, "John.Snow@example.com", (err, invite) =>
  236. expect(err).to.deep.equal(wrongPlan: true)
  237. done()
  238. it "doesn't create an invite if the user is already part of the team", (done) ->
  239. member = {
  240. id: "1a2b",
  241. _id: "1a2b",
  242. email: "tyrion@example.com"
  243. }
  244. @subscription.member_ids = [member.id]
  245. @UserGetter.getUserByAnyEmail.withArgs(member.email).yields(null, member)
  246. @TeamInvitesHandler.createInvite @manager.id, @subscription, "tyrion@example.com", (err, invite) =>
  247. expect(err).to.deep.equal(alreadyInTeam: true)
  248. expect(invite).not.to.exist
  249. done()