RecurlyEventHandlerTests.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. const SandboxedModule = require('sandboxed-module')
  2. const { ObjectId } = require('mongodb-legacy')
  3. const sinon = require('sinon')
  4. const modulePath =
  5. '../../../../app/src/Features/Subscription/RecurlyEventHandler'
  6. describe('RecurlyEventHandler', function () {
  7. beforeEach(function () {
  8. this.userId = '123abc234bcd456cde567def'
  9. this.planCode = 'collaborator-annual'
  10. this.eventData = {
  11. account: {
  12. account_code: this.userId,
  13. },
  14. subscription: {
  15. uuid: '8435ad98c1ce45da99b07f6a6a2e780f',
  16. plan: {
  17. plan_code: 'collaborator-annual',
  18. },
  19. quantity: 1,
  20. state: 'active',
  21. trial_started_at: new Date('2021-01-01 12:34:56'),
  22. trial_ends_at: new Date('2021-01-08 12:34:56'),
  23. current_period_started_at: new Date('2021-01-01 12:34:56'),
  24. current_period_ends_at: new Date('2021-01-08 12:34:56'),
  25. },
  26. }
  27. this.RecurlyEventHandler = SandboxedModule.require(modulePath, {
  28. requires: {
  29. 'mongodb-legacy': { ObjectId },
  30. './SubscriptionEmailHandler': (this.SubscriptionEmailHandler = {
  31. sendTrialOnboardingEmail: sinon.stub(),
  32. }),
  33. '../Analytics/AnalyticsManager': (this.AnalyticsManager = {
  34. recordEventForUserInBackground: sinon.stub(),
  35. setUserPropertyForUserInBackground: sinon.stub(),
  36. }),
  37. '../SplitTests/SplitTestHandler': (this.SplitTestHandler = {
  38. promises: {
  39. getAssignmentForUser: sinon.stub().resolves({
  40. variant: 'default',
  41. }),
  42. },
  43. }),
  44. },
  45. })
  46. })
  47. it('with new_subscription_notification - free trial', function () {
  48. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  49. 'new_subscription_notification',
  50. this.eventData
  51. )
  52. sinon.assert.calledWith(
  53. this.AnalyticsManager.recordEventForUserInBackground,
  54. this.userId,
  55. 'subscription-started',
  56. {
  57. plan_code: this.planCode,
  58. quantity: 1,
  59. is_trial: true,
  60. subscriptionId: this.eventData.subscription.uuid,
  61. }
  62. )
  63. sinon.assert.calledWith(
  64. this.AnalyticsManager.setUserPropertyForUserInBackground,
  65. this.userId,
  66. 'subscription-plan-code',
  67. this.planCode
  68. )
  69. sinon.assert.calledWith(
  70. this.AnalyticsManager.setUserPropertyForUserInBackground,
  71. this.userId,
  72. 'subscription-state',
  73. 'active'
  74. )
  75. sinon.assert.calledWith(
  76. this.AnalyticsManager.setUserPropertyForUserInBackground,
  77. this.userId,
  78. 'subscription-is-trial',
  79. true
  80. )
  81. })
  82. it('sends free trial onboarding email if user starting a trial', async function () {
  83. await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  84. 'new_subscription_notification',
  85. this.eventData
  86. )
  87. sinon.assert.called(this.SubscriptionEmailHandler.sendTrialOnboardingEmail)
  88. })
  89. it('with new_subscription_notification - no free trial', function () {
  90. this.eventData.subscription.current_period_started_at = new Date(
  91. '2021-02-10 12:34:56'
  92. )
  93. this.eventData.subscription.current_period_ends_at = new Date(
  94. '2021-02-17 12:34:56'
  95. )
  96. this.eventData.subscription.quantity = 3
  97. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  98. 'new_subscription_notification',
  99. this.eventData
  100. )
  101. sinon.assert.calledWith(
  102. this.AnalyticsManager.recordEventForUserInBackground,
  103. this.userId,
  104. 'subscription-started',
  105. {
  106. plan_code: this.planCode,
  107. quantity: 3,
  108. is_trial: false,
  109. subscriptionId: this.eventData.subscription.uuid,
  110. }
  111. )
  112. sinon.assert.calledWith(
  113. this.AnalyticsManager.setUserPropertyForUserInBackground,
  114. this.userId,
  115. 'subscription-state',
  116. 'active'
  117. )
  118. sinon.assert.calledWith(
  119. this.AnalyticsManager.setUserPropertyForUserInBackground,
  120. this.userId,
  121. 'subscription-is-trial',
  122. false
  123. )
  124. })
  125. it('with updated_subscription_notification', function () {
  126. this.planCode = 'new-plan-code'
  127. this.eventData.subscription.plan.plan_code = this.planCode
  128. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  129. 'updated_subscription_notification',
  130. this.eventData
  131. )
  132. sinon.assert.calledWith(
  133. this.AnalyticsManager.recordEventForUserInBackground,
  134. this.userId,
  135. 'subscription-updated',
  136. {
  137. plan_code: this.planCode,
  138. quantity: 1,
  139. is_trial: true,
  140. subscriptionId: this.eventData.subscription.uuid,
  141. }
  142. )
  143. sinon.assert.calledWith(
  144. this.AnalyticsManager.setUserPropertyForUserInBackground,
  145. this.userId,
  146. 'subscription-plan-code',
  147. this.planCode
  148. )
  149. sinon.assert.calledWith(
  150. this.AnalyticsManager.setUserPropertyForUserInBackground,
  151. this.userId,
  152. 'subscription-state',
  153. 'active'
  154. )
  155. sinon.assert.calledWith(
  156. this.AnalyticsManager.setUserPropertyForUserInBackground,
  157. this.userId,
  158. 'subscription-is-trial',
  159. true
  160. )
  161. })
  162. it('with canceled_subscription_notification', async function () {
  163. this.eventData.subscription.state = 'cancelled'
  164. await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  165. 'canceled_subscription_notification',
  166. this.eventData
  167. )
  168. sinon.assert.calledWith(
  169. this.AnalyticsManager.recordEventForUserInBackground,
  170. this.userId,
  171. 'subscription-cancelled',
  172. {
  173. plan_code: this.planCode,
  174. quantity: 1,
  175. is_trial: true,
  176. subscriptionId: this.eventData.subscription.uuid,
  177. }
  178. )
  179. sinon.assert.calledWith(
  180. this.AnalyticsManager.setUserPropertyForUserInBackground,
  181. this.userId,
  182. 'subscription-state',
  183. 'cancelled'
  184. )
  185. sinon.assert.calledWith(
  186. this.AnalyticsManager.setUserPropertyForUserInBackground,
  187. this.userId,
  188. 'subscription-is-trial',
  189. true
  190. )
  191. })
  192. it('with expired_subscription_notification', function () {
  193. this.eventData.subscription.state = 'expired'
  194. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  195. 'expired_subscription_notification',
  196. this.eventData
  197. )
  198. sinon.assert.calledWith(
  199. this.AnalyticsManager.recordEventForUserInBackground,
  200. this.userId,
  201. 'subscription-expired',
  202. {
  203. plan_code: this.planCode,
  204. quantity: 1,
  205. is_trial: true,
  206. subscriptionId: this.eventData.subscription.uuid,
  207. }
  208. )
  209. sinon.assert.calledWith(
  210. this.AnalyticsManager.setUserPropertyForUserInBackground,
  211. this.userId,
  212. 'subscription-plan-code',
  213. this.planCode
  214. )
  215. sinon.assert.calledWith(
  216. this.AnalyticsManager.setUserPropertyForUserInBackground,
  217. this.userId,
  218. 'subscription-state',
  219. 'expired'
  220. )
  221. sinon.assert.calledWith(
  222. this.AnalyticsManager.setUserPropertyForUserInBackground,
  223. this.userId,
  224. 'subscription-is-trial',
  225. true
  226. )
  227. })
  228. it('with renewed_subscription_notification', function () {
  229. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  230. 'renewed_subscription_notification',
  231. this.eventData
  232. )
  233. sinon.assert.calledWith(
  234. this.AnalyticsManager.recordEventForUserInBackground,
  235. this.userId,
  236. 'subscription-renewed',
  237. {
  238. plan_code: this.planCode,
  239. quantity: 1,
  240. is_trial: true,
  241. subscriptionId: this.eventData.subscription.uuid,
  242. }
  243. )
  244. })
  245. it('with reactivated_account_notification', function () {
  246. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  247. 'reactivated_account_notification',
  248. this.eventData
  249. )
  250. sinon.assert.calledWith(
  251. this.AnalyticsManager.recordEventForUserInBackground,
  252. this.userId,
  253. 'subscription-reactivated',
  254. {
  255. plan_code: this.planCode,
  256. quantity: 1,
  257. subscriptionId: this.eventData.subscription.uuid,
  258. }
  259. )
  260. })
  261. it('with paid_charge_invoice_notification', function () {
  262. const invoice = {
  263. invoice_number: 1234,
  264. currency: 'USD',
  265. state: 'paid',
  266. total_in_cents: 720,
  267. tax_in_cents: 12,
  268. address: {
  269. country: 'Liurnia',
  270. },
  271. collection_method: 'automatic',
  272. subscription_ids: ['abcd1234', 'defa3214'],
  273. }
  274. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  275. 'paid_charge_invoice_notification',
  276. {
  277. account: {
  278. account_code: this.userId,
  279. },
  280. invoice,
  281. }
  282. )
  283. sinon.assert.calledWith(
  284. this.AnalyticsManager.recordEventForUserInBackground,
  285. this.userId,
  286. 'subscription-invoice-collected',
  287. {
  288. invoiceNumber: invoice.invoice_number,
  289. currency: invoice.currency,
  290. totalInCents: invoice.total_in_cents,
  291. taxInCents: invoice.tax_in_cents,
  292. country: invoice.address.country,
  293. collectionMethod: invoice.collection_method,
  294. subscriptionId1: invoice.subscription_ids[0],
  295. subscriptionId2: invoice.subscription_ids[1],
  296. }
  297. )
  298. })
  299. it('with paid_charge_invoice_notification and total_in_cents 0', function () {
  300. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  301. 'paid_charge_invoice_notification',
  302. {
  303. account: {
  304. account_code: this.userId,
  305. },
  306. invoice: {
  307. state: 'paid',
  308. total_in_cents: 0,
  309. },
  310. }
  311. )
  312. sinon.assert.notCalled(this.AnalyticsManager.recordEventForUserInBackground)
  313. })
  314. it('with closed_invoice_notification', function () {
  315. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  316. 'closed_invoice_notification',
  317. {
  318. account: {
  319. account_code: this.userId,
  320. },
  321. invoice: {
  322. state: 'collected',
  323. total_in_cents: 720,
  324. },
  325. }
  326. )
  327. sinon.assert.calledWith(
  328. this.AnalyticsManager.recordEventForUserInBackground,
  329. this.userId,
  330. 'subscription-invoice-collected'
  331. )
  332. })
  333. it('with closed_invoice_notification and total_in_cents 0', function () {
  334. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  335. 'closed_invoice_notification',
  336. {
  337. account: {
  338. account_code: this.userId,
  339. },
  340. invoice: {
  341. state: 'collected',
  342. total_in_cents: 0,
  343. },
  344. }
  345. )
  346. sinon.assert.notCalled(this.AnalyticsManager.recordEventForUserInBackground)
  347. })
  348. it('nothing is called with invalid account code', function () {
  349. this.eventData.account.account_code = 'foo_bar'
  350. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  351. 'new_subscription_notification',
  352. this.eventData
  353. )
  354. sinon.assert.notCalled(this.AnalyticsManager.recordEventForUserInBackground)
  355. sinon.assert.notCalled(
  356. this.AnalyticsManager.setUserPropertyForUserInBackground
  357. )
  358. sinon.assert.notCalled(
  359. this.AnalyticsManager.setUserPropertyForUserInBackground
  360. )
  361. sinon.assert.notCalled(
  362. this.AnalyticsManager.setUserPropertyForUserInBackground
  363. )
  364. })
  365. })