SubscriptionTests.coffee 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. expect = require('chai').expect
  2. async = require("async")
  3. User = require "./helpers/User"
  4. {Subscription} = require "../../../app/js/models/Subscription"
  5. {Institution} = require "../../../app/js/models/Institution"
  6. SubscriptionViewModelBuilder = require "../../../app/js/Features/Subscription/SubscriptionViewModelBuilder"
  7. MockRecurlyApi = require "./helpers/MockRecurlyApi"
  8. MockV1Api = require "./helpers/MockV1Api"
  9. describe 'Subscriptions', ->
  10. describe 'dashboard', ->
  11. before (done) ->
  12. @user = new User()
  13. @user.ensureUserExists done
  14. describe 'when the user has no subscription', ->
  15. before (done) ->
  16. SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel @user, (error, @data) =>
  17. return done(error) if error?
  18. done()
  19. it 'should return no personalSubscription', ->
  20. expect(@data.personalSubscription).to.equal null
  21. it 'should return no memberGroupSubscriptions', ->
  22. expect(@data.memberGroupSubscriptions).to.deep.equal []
  23. describe 'when the user has a subscription with recurly', ->
  24. before (done) ->
  25. MockRecurlyApi.accounts['mock-account-id'] = @accounts = {
  26. hosted_login_token: 'mock-login-token'
  27. }
  28. MockRecurlyApi.subscriptions['mock-subscription-id'] = @subscription = {
  29. plan_code: 'collaborator',
  30. tax_in_cents: 100,
  31. tax_rate: 0.2,
  32. unit_amount_in_cents: 500,
  33. currency: 'GBP',
  34. current_period_ends_at: new Date(2018,4,5),
  35. state: 'active',
  36. account_id: 'mock-account-id',
  37. trial_ends_at: new Date(2018, 6, 7)
  38. }
  39. MockRecurlyApi.coupons = @coupons = {
  40. 'test-coupon-1': { description: 'Test Coupon 1' }
  41. 'test-coupon-2': { description: 'Test Coupon 2' }
  42. 'test-coupon-3': { name: 'TestCoupon3' }
  43. }
  44. Subscription.create {
  45. admin_id: @user._id,
  46. manager_ids: [@user._id],
  47. recurlySubscription_id: 'mock-subscription-id',
  48. planCode: 'collaborator'
  49. }, (error) =>
  50. return done(error) if error?
  51. SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel @user, (error, @data) =>
  52. return done(error) if error?
  53. done()
  54. return
  55. after (done) ->
  56. MockRecurlyApi.accounts = {}
  57. MockRecurlyApi.subscriptions = {}
  58. MockRecurlyApi.coupons = {}
  59. MockRecurlyApi.redemptions = {}
  60. Subscription.remove {
  61. admin_id: @user._id
  62. }, done
  63. return
  64. it 'should return a personalSubscription with populated recurly data', ->
  65. subscription = @data.personalSubscription
  66. expect(subscription).to.exist
  67. expect(subscription.planCode).to.equal 'collaborator'
  68. expect(subscription.recurly).to.exist
  69. expect(subscription.recurly).to.deep.equal {
  70. "activeCoupons": []
  71. "billingDetailsLink": "https://test.recurly.com/account/billing_info/edit?ht=mock-login-token"
  72. "currency": "GBP"
  73. "nextPaymentDueAt": "5th May 2018"
  74. "price": "£6.00"
  75. "state": "active"
  76. "tax": 100
  77. "taxRate": 0.2
  78. "trial_ends_at": new Date(2018, 6, 7),
  79. "trialEndsAtFormatted": "7th July 2018"
  80. }
  81. it 'should return no memberGroupSubscriptions', ->
  82. expect(@data.memberGroupSubscriptions).to.deep.equal []
  83. it 'should include redeemed coupons', (done) ->
  84. MockRecurlyApi.redemptions['mock-account-id'] = [
  85. { state: 'active', coupon_code: 'test-coupon-1' }
  86. { state: 'inactive', coupon_code: 'test-coupon-2' }
  87. { state: 'active', coupon_code: 'test-coupon-3' }
  88. ]
  89. # rebuild the view model with the redemptions
  90. SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel @user, (error, data) ->
  91. expect(error).to.not.exist
  92. expect(data.personalSubscription.recurly.activeCoupons).to.deep.equal [
  93. {
  94. coupon_code: 'test-coupon-1',
  95. name: '',
  96. description: 'Test Coupon 1'
  97. }
  98. {
  99. coupon_code: 'test-coupon-3',
  100. name: 'TestCoupon3',
  101. description: ''
  102. }
  103. ]
  104. done()
  105. describe 'when the user has a subscription without recurly', ->
  106. before (done) ->
  107. Subscription.create {
  108. admin_id: @user._id,
  109. manager_ids: [@user._id],
  110. planCode: 'collaborator'
  111. }, (error) =>
  112. return done(error) if error?
  113. SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel @user, (error, @data) =>
  114. return done(error) if error?
  115. done()
  116. return
  117. after (done) ->
  118. Subscription.remove {
  119. admin_id: @user._id
  120. }, done
  121. return
  122. it 'should return a personalSubscription with no recurly data', ->
  123. subscription = @data.personalSubscription
  124. expect(subscription).to.exist
  125. expect(subscription.planCode).to.equal 'collaborator'
  126. expect(subscription.recurly).to.not.exist
  127. it 'should return no memberGroupSubscriptions', ->
  128. expect(@data.memberGroupSubscriptions).to.deep.equal []
  129. describe 'when the user is a member of a group subscription', ->
  130. before (done) ->
  131. @owner1 = new User()
  132. @owner2 = new User()
  133. async.series [
  134. (cb) => @owner1.ensureUserExists cb
  135. (cb) => @owner2.ensureUserExists cb
  136. (cb) => Subscription.create {
  137. admin_id: @owner1._id,
  138. manager_ids: [@owner1._id],
  139. planCode: 'collaborator',
  140. groupPlan: true,
  141. member_ids: [@user._id]
  142. }, cb
  143. (cb) => Subscription.create {
  144. admin_id: @owner2._id,
  145. manager_ids: [@owner2._id],
  146. planCode: 'collaborator',
  147. groupPlan: true,
  148. member_ids: [@user._id]
  149. }, cb
  150. ], (error) =>
  151. return done(error) if error?
  152. SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel @user, (error, @data) =>
  153. return done(error) if error?
  154. done()
  155. return
  156. after (done) ->
  157. Subscription.remove {
  158. admin_id: @owner1._id
  159. }, (error) =>
  160. return done(error) if error?
  161. Subscription.remove {
  162. admin_id: @owner2._id
  163. }, done
  164. return
  165. it 'should return no personalSubscription', ->
  166. expect(@data.personalSubscription).to.equal null
  167. it 'should return the two memberGroupSubscriptions', ->
  168. expect(@data.memberGroupSubscriptions.length).to.equal 2
  169. expect(
  170. # Mongoose populates the admin_id with the user
  171. @data.memberGroupSubscriptions[0].admin_id._id.toString()
  172. ).to.equal @owner1._id
  173. expect(
  174. @data.memberGroupSubscriptions[1].admin_id._id.toString()
  175. ).to.equal @owner2._id
  176. describe 'when the user is a manager of a group subscription', ->
  177. before (done) ->
  178. @owner1 = new User()
  179. @owner2 = new User()
  180. async.series [
  181. (cb) => @owner1.ensureUserExists cb
  182. (cb) => @owner2.ensureUserExists cb
  183. (cb) => Subscription.create {
  184. admin_id: @owner1._id,
  185. manager_ids: [@owner1._id, @user._id],
  186. planCode: 'collaborator',
  187. groupPlan: true
  188. }, cb
  189. ], (error) =>
  190. return done(error) if error?
  191. SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel @user, (error, @data) =>
  192. return done(error) if error?
  193. done()
  194. return
  195. after (done) ->
  196. Subscription.remove {
  197. admin_id: @owner1._id
  198. }, done
  199. return
  200. it 'should return no personalSubscription', ->
  201. expect(@data.personalSubscription).to.equal null
  202. it 'should return the managedGroupSubscriptions', ->
  203. expect(@data.managedGroupSubscriptions.length).to.equal 1
  204. subscription = @data.managedGroupSubscriptions[0]
  205. expect(
  206. # Mongoose populates the admin_id with the user
  207. subscription.admin_id._id.toString()
  208. ).to.equal @owner1._id
  209. expect(subscription.groupPlan).to.equal true
  210. describe 'when the user is a manager of an institution', ->
  211. before (done) ->
  212. @v1Id = MockV1Api.nextV1Id()
  213. async.series [
  214. (cb) =>
  215. Institution.create({
  216. v1Id: @v1Id,
  217. managerIds: [@user._id]
  218. }, cb)
  219. ], (error) =>
  220. return done(error) if error?
  221. SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel @user, (error, @data) =>
  222. return done(error) if error?
  223. done()
  224. return
  225. after (done) ->
  226. Institution.remove {
  227. v1Id: @v1Id
  228. }, done
  229. return
  230. it 'should return the managedInstitutions', ->
  231. expect(@data.managedInstitutions.length).to.equal 1
  232. institution = @data.managedInstitutions[0]
  233. expect(institution.v1Id).to.equal @v1Id
  234. expect(institution.name).to.equal "Institution #{@v1Id}"
  235. describe 'when the user is a member of an affiliation', ->
  236. before (done) ->
  237. v1Id = MockV1Api.nextV1Id()
  238. MockV1Api.setUser v1Id, {
  239. subscription: {},
  240. subscription_status: {}
  241. }
  242. MockV1Api.setAffiliations [{
  243. email: 'confirmed-affiliation-email@stanford.example.edu'
  244. institution: { name: 'Stanford', licence: 'pro_plus', confirmed: true }
  245. }, {
  246. email: 'unconfirmed-affiliation-email@harvard.example.edu'
  247. institution: { name: 'Harvard', licence: 'pro_plus', confirmed: true }
  248. }, {
  249. email: 'confirmed-affiliation-email@mit.example.edu'
  250. institution: { name: 'MIT', licence: 'pro_plus', confirmed: false }
  251. }]
  252. async.series [
  253. (cb) =>
  254. @user.setV1Id v1Id, cb
  255. (cb) =>
  256. @user.addEmail 'unconfirmed-affiliation-email@harvard.example.edu', cb
  257. (cb) =>
  258. @user.addEmail 'confirmed-affiliation-email@stanford.example.edu', cb
  259. (cb) =>
  260. @user.confirmEmail 'confirmed-affiliation-email@stanford.example.edu', cb
  261. (cb) =>
  262. @user.addEmail 'confirmed-affiliation-email@mit.example.edu', cb
  263. (cb) =>
  264. @user.confirmEmail 'confirmed-affiliation-email@mit.example.edu', cb
  265. ], (error) =>
  266. return done(error) if error?
  267. SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel @user, (error, @data) =>
  268. return done(error) if error?
  269. done()
  270. it 'should return only the affilations with confirmed institutions, and confirmed emails', ->
  271. expect(@data.confirmedMemberInstitutions).to.deep.equal [
  272. { name: 'Stanford', licence: 'pro_plus', confirmed: true }
  273. ]
  274. describe 'when the user has a v1 subscription', ->
  275. before (done) ->
  276. MockV1Api.setUser v1Id = MockV1Api.nextV1Id(), {
  277. subscription: @subscription = {
  278. trial: false,
  279. has_plan: true,
  280. teams: [{
  281. id: 56,
  282. name: 'Test team'
  283. }]
  284. }
  285. subscription_status: @subscription_status = {
  286. product: { 'mock': 'product' }
  287. team: null
  288. }
  289. }
  290. @user.setV1Id v1Id, (error) =>
  291. return done(error) if error?
  292. SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel @user, (error, @data) =>
  293. return done(error) if error?
  294. done()
  295. it 'should return no personalSubscription', ->
  296. expect(@data.personalSubscription).to.equal null
  297. it 'should return no memberGroupSubscriptions', ->
  298. expect(@data.memberGroupSubscriptions).to.deep.equal []
  299. it 'should return a v1SubscriptionStatus', ->
  300. expect(@data.v1SubscriptionStatus).to.deep.equal @subscription_status
  301. describe 'canceling', ->
  302. before (done) ->
  303. @user = new User()
  304. MockV1Api.setUser v1Id = MockV1Api.nextV1Id(), @v1_user = {}
  305. async.series [
  306. (cb) => @user.login(cb)
  307. (cb) => @user.setV1Id(v1Id, cb)
  308. ], (error) =>
  309. @user.request {
  310. method: 'POST',
  311. url: '/user/subscription/v1/cancel'
  312. }, (error, @response) =>
  313. return done(error) if error?
  314. done()
  315. it 'should tell v1 to cancel the subscription', ->
  316. expect(@v1_user.canceled).to.equal true
  317. it 'should redirect to the subscription dashboard', ->
  318. expect(@response.statusCode).to.equal 302
  319. expect(@response.headers.location).to.equal '/user/subscription'