SubscriptionControllerTests.coffee 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. SandboxedModule = require('sandboxed-module')
  2. sinon = require 'sinon'
  3. should = require("chai").should()
  4. MockRequest = require "../helpers/MockRequest"
  5. MockResponse = require "../helpers/MockResponse"
  6. modulePath = '../../../../app/js/Features/Subscription/SubscriptionController'
  7. mockSubscriptions =
  8. "subscription-123-active":
  9. uuid: "subscription-123-active"
  10. plan:
  11. name: "Gold"
  12. plan_code: "gold"
  13. current_period_ends_at: new Date()
  14. state: "active"
  15. unit_amount_in_cents: 999
  16. account:
  17. account_code: "user-123"
  18. describe "SubscriptionController sanboxed", ->
  19. beforeEach ->
  20. @user = {}
  21. @activeRecurlySubscription = mockSubscriptions["subscription-123-active"]
  22. @SecurityManager =
  23. getCurrentUser: sinon.stub().callsArgWith(1, null, @user)
  24. @SubscriptionHandler =
  25. createSubscription: sinon.stub().callsArgWith(2)
  26. updateSubscription: sinon.stub().callsArgWith(3)
  27. reactivateSubscription: sinon.stub().callsArgWith(1)
  28. cancelSubscription: sinon.stub().callsArgWith(1)
  29. recurlyCallback: sinon.stub().callsArgWith(1)
  30. startFreeTrial: sinon.stub()
  31. @PlansLocator =
  32. findLocalPlanInSettings: sinon.stub()
  33. @LimitationsManager =
  34. userHasSubscriptionOrIsGroupMember: sinon.stub()
  35. userHasSubscription : sinon.stub()
  36. @RecurlyWrapper =
  37. sign: sinon.stub().callsArgWith(1, null, "somthing")
  38. @SubscriptionViewModelBuilder =
  39. buildUsersSubscriptionViewModel:sinon.stub().callsArgWith(1, null, @activeRecurlySubscription)
  40. buildViewModel: sinon.stub()
  41. @settings =
  42. coupon_codes:
  43. upgradeToAnnualPromo:
  44. student:"STUDENTCODEHERE"
  45. collaborator:"COLLABORATORCODEHERE"
  46. apis:
  47. recurly:
  48. subdomain:"sl.recurly.com"
  49. siteUrl: "http://de.sharelatex.dev:3000"
  50. @SubscriptionController = SandboxedModule.require modulePath, requires:
  51. '../../managers/SecurityManager': @SecurityManager
  52. './SubscriptionHandler': @SubscriptionHandler
  53. "./PlansLocator": @PlansLocator
  54. './SubscriptionViewModelBuilder': @SubscriptionViewModelBuilder
  55. "./LimitationsManager": @LimitationsManager
  56. './RecurlyWrapper': @RecurlyWrapper
  57. "logger-sharelatex": log:->
  58. "settings-sharelatex": @settings
  59. @res = new MockResponse()
  60. @req = new MockRequest()
  61. @req.body = {}
  62. @req.query =
  63. planCode:"123123"
  64. describe "editBillingDetailsPage", ->
  65. describe "with a user with a subscription", ->
  66. beforeEach (done) ->
  67. @LimitationsManager.userHasSubscription.callsArgWith(1, null, true)
  68. @user.id = @activeRecurlySubscription.account.account_code
  69. @res.callback = done
  70. @SubscriptionController.editBillingDetailsPage(@req, @res)
  71. it "should render the edit billing details page", ->
  72. @res.rendered.should.equal true
  73. @res.renderedTemplate.should.equal "subscriptions/edit-billing-details"
  74. it "should set the correct variables for the template", ->
  75. should.exist @res.renderedVariables.signature
  76. @res.renderedVariables.successURL.should.equal "#{@settings.siteUrl}/user/subscription/update"
  77. @res.renderedVariables.user.id.should.equal @user.id
  78. describe "with a user without subscription", ->
  79. beforeEach (done) ->
  80. @res.callback = done
  81. @LimitationsManager.userHasSubscription.callsArgWith(1, null, false)
  82. @SubscriptionController.reactivateSubscription @req, @res
  83. it "should redirect to the subscription dashboard", ->
  84. @res.redirected.should.equal true
  85. @res.redirectedTo.should.equal "/user/subscription"
  86. describe "paymentPage", ->
  87. describe "with a user without a subscription", ->
  88. beforeEach ->
  89. @LimitationsManager.userHasSubscription.callsArgWith(1, null, false)
  90. @PlansLocator.findLocalPlanInSettings.returns({})
  91. describe "with a valid plan code", ->
  92. it "should render the new subscription page", (done)->
  93. @res.render = (page, opts)=>
  94. page.should.equal "subscriptions/new"
  95. done()
  96. @SubscriptionController.paymentPage @req, @res
  97. it "should set the successURL", (done)->
  98. @req.session._csrf = @csrfToken = "mock-csrf-token"
  99. @res.render = (page, opts)=>
  100. url = JSON.parse(opts.subscriptionFormOptions).successURL
  101. url.should.equal("#{@settings.siteUrl}/user/subscription/create?_csrf=#{@csrfToken}")
  102. done()
  103. @SubscriptionController.paymentPage @req, @res
  104. describe "with a user with subscription", ->
  105. it "should redirect to the subscription dashboard", (done)->
  106. @LimitationsManager.userHasSubscription.callsArgWith(1, null, true)
  107. @res.redirect = (url)=>
  108. url.should.equal "/user/subscription"
  109. done()
  110. @SubscriptionController.paymentPage(@req, @res)
  111. describe "with an invalid plan code", ->
  112. it "should redirect to the subscription dashboard", (done)->
  113. @LimitationsManager.userHasSubscription.callsArgWith(1, null, false)
  114. @PlansLocator.findLocalPlanInSettings.returns(null)
  115. @res.redirect = (url)=>
  116. url.should.equal "/user/subscription"
  117. done()
  118. @SubscriptionController.paymentPage(@req, @res)
  119. describe "successful_subscription", ->
  120. beforeEach (done) ->
  121. @SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel.callsArgWith(1, null, {})
  122. @res.callback = done
  123. @SubscriptionController.successful_subscription @req, @res
  124. describe "userSubscriptionPage", ->
  125. describe "with a user without a subscription", ->
  126. beforeEach (done) ->
  127. @res.callback = done
  128. @LimitationsManager.userHasSubscriptionOrIsGroupMember.callsArgWith(1, null, false)
  129. @SubscriptionController.userSubscriptionPage @req, @res
  130. it "should redirect to the plans page", ->
  131. @res.redirected.should.equal true
  132. @res.redirectedTo.should.equal "/user/subscription/plans"
  133. describe "with a user with a paid subscription", ->
  134. beforeEach (done) ->
  135. @res.callback = done
  136. @SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel.callsArgWith(1, null, @activeRecurlySubscription)
  137. @LimitationsManager.userHasSubscriptionOrIsGroupMember.callsArgWith(1, null, true)
  138. @SubscriptionController.userSubscriptionPage @req, @res
  139. it "should render the dashboard", (done)->
  140. @res.rendered.should.equal true
  141. @res.renderedTemplate.should.equal "subscriptions/dashboard"
  142. done()
  143. it "should set the correct subscription details", ->
  144. @res.renderedVariables.subscription.should.deep.equal @activeRecurlySubscription
  145. describe "with a user with a free trial", ->
  146. beforeEach (done) ->
  147. @res.callback = done
  148. @SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel.callsArgWith(1, null, @activeRecurlySubscription)
  149. @LimitationsManager.userHasSubscriptionOrIsGroupMember.callsArgWith(1, null, true)
  150. @SubscriptionController.userSubscriptionPage @req, @res
  151. it "should render the dashboard", ->
  152. @res.renderedTemplate.should.equal "subscriptions/dashboard"
  153. it "should set the correct subscription details", ->
  154. @res.renderedVariables.subscription.should.deep.equal @activeRecurlySubscription
  155. describe "when its a custom subscription which is non recurly", ->
  156. beforeEach ()->
  157. @LimitationsManager.userHasSubscriptionOrIsGroupMember.callsArgWith(1, null, true, {customAccount:true})
  158. @SubscriptionController.userSubscriptionPage @req, @res
  159. it "should redirect to /user/subscription/custom_account", ->
  160. @res.redirectedTo.should.equal("/user/subscription/custom_account")
  161. describe "userCustomSubscriptionPage", ->
  162. beforeEach (done) ->
  163. @res.callback = done
  164. @LimitationsManager.userHasSubscriptionOrIsGroupMember.callsArgWith(1, null, true)
  165. @SubscriptionController.userCustomSubscriptionPage @req, @res
  166. it "should render the page", (done)->
  167. @res.rendered.should.equal true
  168. @res.renderedTemplate.should.equal "subscriptions/custom_account"
  169. done()
  170. describe "createSubscription", ->
  171. beforeEach (done)->
  172. @res =
  173. redirect:->
  174. done()
  175. sinon.spy @res, "redirect"
  176. @req.body.recurly_token = "1234"
  177. @SubscriptionController.createSubscription @req, @res
  178. it "should send the user and subscriptionId to the handler", (done)->
  179. @SubscriptionHandler.createSubscription.calledWith(@user, @req.body.recurly_token).should.equal true
  180. done()
  181. it "should redurect to the subscription page", (done)->
  182. @res.redirect.calledWith("/user/subscription/thank-you").should.equal true
  183. done()
  184. describe "updateSubscription via post", ->
  185. beforeEach (done)->
  186. @res =
  187. redirect:->
  188. done()
  189. sinon.spy @res, "redirect"
  190. @plan_code = "1234"
  191. @req.body.plan_code = @plan_code
  192. @SubscriptionController.updateSubscription @req, @res
  193. it "should send the user and subscriptionId to the handler", (done)->
  194. @SubscriptionHandler.updateSubscription.calledWith(@user, @plan_code).should.equal true
  195. done()
  196. it "should redurect to the subscription page", (done)->
  197. @res.redirect.calledWith("/user/subscription").should.equal true
  198. done()
  199. describe "reactivateSubscription", ->
  200. beforeEach (done)->
  201. @res =
  202. redirect:->
  203. done()
  204. sinon.spy @res, "redirect"
  205. @SubscriptionController.reactivateSubscription @req, @res
  206. it "should tell the handler to reactivate this user", (done)->
  207. @SubscriptionHandler.reactivateSubscription.calledWith(@user).should.equal true
  208. done()
  209. it "should redurect to the subscription page", (done)->
  210. @res.redirect.calledWith("/user/subscription").should.equal true
  211. done()
  212. describe "cancelSubscription", ->
  213. beforeEach (done)->
  214. @res =
  215. redirect:->
  216. done()
  217. sinon.spy @res, "redirect"
  218. @SubscriptionController.cancelSubscription @req, @res
  219. it "should tell the handler to cancel this user", (done)->
  220. @SubscriptionHandler.cancelSubscription.calledWith(@user).should.equal true
  221. done()
  222. it "should redurect to the subscription page", (done)->
  223. @res.redirect.calledWith("/user/subscription").should.equal true
  224. done()
  225. describe "recurly callback", ->
  226. describe "with a actionable request", ->
  227. beforeEach (done)->
  228. @req =
  229. body:
  230. expired_subscription_notification:
  231. subscription:
  232. uuid: @activeRecurlySubscription.uuid
  233. @res = send:->
  234. done()
  235. sinon.spy @res, "send"
  236. @SubscriptionController.recurlyCallback @req, @res
  237. it "should tell the SubscriptionHandler to process the recurly callback", (done)->
  238. @SubscriptionHandler.recurlyCallback.called.should.equal true
  239. done()
  240. it "should send a 200", (done)->
  241. @res.send.calledWith(200)
  242. done()
  243. describe "with a non-actionable request", ->
  244. beforeEach (done) ->
  245. @user.id = @activeRecurlySubscription.account.account_code
  246. @req =
  247. body:
  248. new_subscription_notification:
  249. subscription:
  250. uuid: @activeRecurlySubscription.uuid
  251. @res = send:->
  252. done()
  253. sinon.spy @res, "send"
  254. @SubscriptionController.recurlyCallback @req, @res
  255. it "should not call the subscriptionshandler", ->
  256. @SubscriptionHandler.recurlyCallback.called.should.equal false
  257. it "should respond with a 200 status", ->
  258. @res.send.calledWith(200)
  259. describe "renderUpgradeToAnnualPlanPage", ->
  260. it "should redirect to the plans page if the user does not have a subscription", (done)->
  261. @LimitationsManager.userHasSubscription.callsArgWith(1, null, false)
  262. @res.redirect = (url)->
  263. url.should.equal "/user/subscription/plans"
  264. done()
  265. @SubscriptionController.renderUpgradeToAnnualPlanPage @req, @res
  266. it "should pass the plan code to the view - student", (done)->
  267. @LimitationsManager.userHasSubscription.callsArgWith(1, null, true, {planCode:"Student free trial 14 days"})
  268. @res.render = (view, opts)->
  269. view.should.equal "subscriptions/upgradeToAnnual"
  270. opts.planName.should.equal "student"
  271. done()
  272. @SubscriptionController.renderUpgradeToAnnualPlanPage @req, @res
  273. it "should pass the plan code to the view - collaborator", (done)->
  274. @LimitationsManager.userHasSubscription.callsArgWith(1, null, true, {planCode:"free trial for Collaborator free trial 14 days"})
  275. @res.render = (view, opts)->
  276. opts.planName.should.equal "collaborator"
  277. done()
  278. @SubscriptionController.renderUpgradeToAnnualPlanPage @req, @res
  279. it "should pass annual as the plan name if the user is already on an annual plan", (done)->
  280. @LimitationsManager.userHasSubscription.callsArgWith(1, null, true, {planCode:"student annual with free trial"})
  281. @res.render = (view, opts)->
  282. opts.planName.should.equal "annual"
  283. done()
  284. @SubscriptionController.renderUpgradeToAnnualPlanPage @req, @res
  285. describe "processUpgradeToAnnualPlan", ->
  286. beforeEach ->
  287. it "should tell the subscription handler to update the subscription with the annual plan and apply a coupon code", (done)->
  288. @req.body =
  289. planName:"student"
  290. @res.send = ()=>
  291. @SubscriptionHandler.updateSubscription.calledWith(@user, "student-annual", "STUDENTCODEHERE").should.equal true
  292. done()
  293. @SubscriptionController.processUpgradeToAnnualPlan @req, @res
  294. it "should get the collaborator coupon code", (done)->
  295. @req.body =
  296. planName:"collaborator"
  297. @res.send = (url)=>
  298. @SubscriptionHandler.updateSubscription.calledWith(@user, "collaborator-annual", "COLLABORATORCODEHERE").should.equal true
  299. done()
  300. @SubscriptionController.processUpgradeToAnnualPlan @req, @res