EmailSenderTests.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. const SandboxedModule = require('sandboxed-module')
  2. const path = require('path')
  3. const sinon = require('sinon')
  4. const { expect } = require('chai')
  5. const MODULE_PATH = path.join(
  6. __dirname,
  7. '../../../../app/src/Features/Email/EmailSender.js'
  8. )
  9. describe('EmailSender', function () {
  10. beforeEach(function () {
  11. this.rateLimiter = {
  12. consume: sinon.stub().resolves(),
  13. }
  14. this.RateLimiter = {
  15. RateLimiter: sinon.stub().returns(this.rateLimiter),
  16. }
  17. this.Settings = {
  18. email: {
  19. transport: 'ses',
  20. parameters: {
  21. AWSAccessKeyID: 'key',
  22. AWSSecretKey: 'secret',
  23. },
  24. fromAddress: 'bob@bob.com',
  25. replyToAddress: 'sally@gmail.com',
  26. },
  27. }
  28. this.sesClient = { sendMail: sinon.stub().resolves() }
  29. this.ses = { createTransport: () => this.sesClient }
  30. this.EmailSender = SandboxedModule.require(MODULE_PATH, {
  31. requires: {
  32. nodemailer: this.ses,
  33. 'nodemailer-ses-transport': sinon.stub(),
  34. '@overleaf/settings': this.Settings,
  35. '../../infrastructure/RateLimiter': this.RateLimiter,
  36. '@overleaf/metrics': {
  37. inc() {},
  38. },
  39. },
  40. })
  41. this.opts = {
  42. to: 'bob@bob.com',
  43. subject: 'new email',
  44. html: '<hello></hello>',
  45. }
  46. })
  47. describe('sendEmail', function () {
  48. it('should set the properties on the email to send', async function () {
  49. await this.EmailSender.promises.sendEmail(this.opts)
  50. expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
  51. html: this.opts.html,
  52. to: this.opts.to,
  53. subject: this.opts.subject,
  54. })
  55. })
  56. it('should return a non-specific error', async function () {
  57. this.sesClient.sendMail.rejects(new Error('boom'))
  58. await expect(this.EmailSender.promises.sendEmail({})).to.be.rejectedWith(
  59. 'error sending message'
  60. )
  61. })
  62. it('should use the from address from settings', async function () {
  63. await this.EmailSender.promises.sendEmail(this.opts)
  64. expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
  65. from: this.Settings.email.fromAddress,
  66. })
  67. })
  68. it('should use the reply to address from settings', async function () {
  69. await this.EmailSender.promises.sendEmail(this.opts)
  70. expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
  71. replyTo: this.Settings.email.replyToAddress,
  72. })
  73. })
  74. it('should use the reply to address in options as an override', async function () {
  75. this.opts.replyTo = 'someone@else.com'
  76. await this.EmailSender.promises.sendEmail(this.opts)
  77. expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
  78. replyTo: this.opts.replyTo,
  79. })
  80. })
  81. it('should not send an email when the rate limiter says no', async function () {
  82. this.opts.sendingUser_id = '12321312321'
  83. this.rateLimiter.consume.rejects({ remainingPoints: 0 })
  84. await expect(this.EmailSender.promises.sendEmail(this.opts)).to.be
  85. .rejected
  86. expect(this.sesClient.sendMail).not.to.have.been.called
  87. })
  88. it('should send the email when the rate limtier says continue', async function () {
  89. this.opts.sendingUser_id = '12321312321'
  90. await this.EmailSender.promises.sendEmail(this.opts)
  91. expect(this.sesClient.sendMail).to.have.been.called
  92. })
  93. it('should not check the rate limiter when there is no sendingUser_id', async function () {
  94. this.EmailSender.sendEmail(this.opts, () => {
  95. expect(this.sesClient.sendMail).to.have.been.called
  96. expect(this.rateLimiter.consume).not.to.have.been.called
  97. })
  98. })
  99. describe('with plain-text email content', function () {
  100. beforeEach(function () {
  101. this.opts.text = 'hello there'
  102. })
  103. it('should set the text property on the email to send', async function () {
  104. await this.EmailSender.promises.sendEmail(this.opts)
  105. expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
  106. html: this.opts.html,
  107. text: this.opts.text,
  108. to: this.opts.to,
  109. subject: this.opts.subject,
  110. })
  111. })
  112. })
  113. })
  114. })