EmailSenderTests.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  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. promises: {
  13. addCount: sinon.stub(),
  14. },
  15. }
  16. this.Settings = {
  17. email: {
  18. transport: 'ses',
  19. parameters: {
  20. AWSAccessKeyID: 'key',
  21. AWSSecretKey: 'secret',
  22. },
  23. fromAddress: 'bob@bob.com',
  24. replyToAddress: 'sally@gmail.com',
  25. },
  26. }
  27. this.sesClient = { sendMail: sinon.stub().resolves() }
  28. this.ses = { createTransport: () => this.sesClient }
  29. this.EmailSender = SandboxedModule.require(MODULE_PATH, {
  30. requires: {
  31. nodemailer: this.ses,
  32. 'nodemailer-ses-transport': sinon.stub(),
  33. 'nodemailer-mandrill-transport': {},
  34. 'settings-sharelatex': 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.promises.addCount.resolves(false)
  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. this.RateLimiter.promises.addCount.resolves(true)
  91. await this.EmailSender.promises.sendEmail(this.opts)
  92. expect(this.sesClient.sendMail).to.have.been.called
  93. })
  94. it('should not check the rate limiter when there is no sendingUser_id', async function () {
  95. this.EmailSender.sendEmail(this.opts, () => {
  96. expect(this.sesClient.sendMail).to.have.been.called
  97. expect(this.RateLimiter.promises.addCount).not.to.have.been.called
  98. })
  99. })
  100. describe('with plain-text email content', function () {
  101. beforeEach(function () {
  102. this.opts.text = 'hello there'
  103. })
  104. it('should set the text property on the email to send', async function () {
  105. await this.EmailSender.promises.sendEmail(this.opts)
  106. expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
  107. html: this.opts.html,
  108. text: this.opts.text,
  109. to: this.opts.to,
  110. subject: this.opts.subject,
  111. })
  112. })
  113. })
  114. })
  115. })