NewsletterManagerTests.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. const { expect } = require('chai')
  2. const sinon = require('sinon')
  3. const SandboxedModule = require('sandboxed-module')
  4. const MODULE_PATH = '../../../../app/src/Features/Newsletter/NewsletterManager'
  5. describe('NewsletterManager', function () {
  6. beforeEach('setup mocks', function () {
  7. this.Settings = {
  8. mailchimp: {
  9. api_key: 'api_key',
  10. list_id: 'list_id',
  11. },
  12. }
  13. this.mailchimp = {
  14. put: sinon.stub(),
  15. patch: sinon.stub(),
  16. delete: sinon.stub(),
  17. }
  18. this.Mailchimp = sinon.stub().returns(this.mailchimp)
  19. this.mergeFields = {
  20. FNAME: 'Overleaf',
  21. LNAME: 'Duck',
  22. MONGO_ID: 'user_id',
  23. }
  24. this.NewsletterManager = SandboxedModule.require(MODULE_PATH, {
  25. requires: {
  26. 'mailchimp-api-v3': this.Mailchimp,
  27. '@overleaf/settings': this.Settings,
  28. },
  29. }).promises
  30. this.user = {
  31. _id: 'user_id',
  32. email: 'overleaf.duck@example.com',
  33. first_name: 'Overleaf',
  34. last_name: 'Duck',
  35. }
  36. // MD5 sum of the user email
  37. this.emailHash = 'c02f60ed0ef51818186274e406c9a48f'
  38. })
  39. describe('subscribe', function () {
  40. it('calls Mailchimp to subscribe the user', async function () {
  41. await this.NewsletterManager.subscribe(this.user)
  42. expect(this.mailchimp.put).to.have.been.calledWith(
  43. `/lists/list_id/members/${this.emailHash}`,
  44. {
  45. email_address: this.user.email,
  46. status: 'subscribed',
  47. status_if_new: 'subscribed',
  48. merge_fields: this.mergeFields,
  49. }
  50. )
  51. })
  52. })
  53. describe('unsubscribe', function () {
  54. describe('when unsubscribing normally', function () {
  55. it('calls Mailchimp to unsubscribe the user', async function () {
  56. await this.NewsletterManager.unsubscribe(this.user)
  57. expect(this.mailchimp.patch).to.have.been.calledWith(
  58. `/lists/list_id/members/${this.emailHash}`,
  59. {
  60. status: 'unsubscribed',
  61. merge_fields: this.mergeFields,
  62. }
  63. )
  64. })
  65. it('ignores a Mailchimp error about fake emails', async function () {
  66. this.mailchimp.patch.rejects(
  67. new Error(
  68. 'overleaf.duck@example.com looks fake or invalid, please enter a real email address'
  69. )
  70. )
  71. await expect(this.NewsletterManager.unsubscribe(this.user)).to.be
  72. .fulfilled
  73. })
  74. it('rejects on other errors', async function () {
  75. this.mailchimp.patch.rejects(
  76. new Error('something really wrong is happening')
  77. )
  78. await expect(this.NewsletterManager.unsubscribe(this.user)).to.be
  79. .rejected
  80. })
  81. })
  82. describe('when deleting', function () {
  83. it('calls Mailchimp to delete the user', async function () {
  84. await this.NewsletterManager.unsubscribe(this.user, { delete: true })
  85. expect(this.mailchimp.delete).to.have.been.calledWith(
  86. `/lists/list_id/members/${this.emailHash}`
  87. )
  88. })
  89. it('ignores a Mailchimp error about fake emails', async function () {
  90. this.mailchimp.delete.rejects(
  91. new Error(
  92. 'overleaf.duck@example.com looks fake or invalid, please enter a real email address'
  93. )
  94. )
  95. await expect(
  96. this.NewsletterManager.unsubscribe(this.user, { delete: true })
  97. ).to.be.fulfilled
  98. })
  99. it('rejects on other errors', async function () {
  100. this.mailchimp.delete.rejects(
  101. new Error('something really wrong is happening')
  102. )
  103. await expect(
  104. this.NewsletterManager.unsubscribe(this.user, { delete: true })
  105. ).to.be.rejected
  106. })
  107. })
  108. })
  109. describe('changeEmail', function () {
  110. it('calls Mailchimp to change the subscriber email', async function () {
  111. await this.NewsletterManager.changeEmail(
  112. this.user,
  113. 'overleaf.squirrel@example.com'
  114. )
  115. expect(this.mailchimp.patch).to.have.been.calledWith(
  116. `/lists/list_id/members/${this.emailHash}`,
  117. {
  118. email_address: 'overleaf.squirrel@example.com',
  119. merge_fields: this.mergeFields,
  120. }
  121. )
  122. })
  123. it('deletes the old email if changing the address fails', async function () {
  124. this.mailchimp.patch
  125. .withArgs(`/lists/list_id/members/${this.emailHash}`, {
  126. email_address: 'overleaf.squirrel@example.com',
  127. merge_fields: this.mergeFields,
  128. })
  129. .rejects(new Error('that did not work'))
  130. await expect(
  131. this.NewsletterManager.changeEmail(
  132. this.user,
  133. 'overleaf.squirrel@example.com'
  134. )
  135. ).to.be.rejected
  136. expect(this.mailchimp.delete).to.have.been.calledWith(
  137. `/lists/list_id/members/${this.emailHash}`
  138. )
  139. })
  140. it('does not reject on non-fatal error ', async function () {
  141. const nonFatalError = new Error('merge fields were invalid')
  142. this.mailchimp.patch.rejects(nonFatalError)
  143. await expect(
  144. this.NewsletterManager.changeEmail(
  145. this.user,
  146. 'overleaf.squirrel@example.com'
  147. )
  148. ).to.be.fulfilled
  149. })
  150. it('rejects on any other error', async function () {
  151. const fatalError = new Error('fatal error')
  152. this.mailchimp.patch.rejects(fatalError)
  153. await expect(
  154. this.NewsletterManager.changeEmail(
  155. this.user,
  156. 'overleaf.squirrel@example.com'
  157. )
  158. ).to.be.rejected
  159. })
  160. })
  161. })