EmailSenderTests.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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. globals: {
  31. console: console
  32. },
  33. requires: {
  34. nodemailer: this.ses,
  35. 'nodemailer-mandrill-transport': {},
  36. 'settings-sharelatex': this.Settings,
  37. '../../infrastructure/RateLimiter': this.RateLimiter,
  38. 'logger-sharelatex': {
  39. log() {},
  40. warn() {},
  41. err() {}
  42. },
  43. 'metrics-sharelatex': {
  44. inc() {}
  45. }
  46. }
  47. })
  48. this.opts = {
  49. to: 'bob@bob.com',
  50. subject: 'new email',
  51. html: '<hello></hello>'
  52. }
  53. })
  54. describe('sendEmail', function() {
  55. it('should set the properties on the email to send', async function() {
  56. await this.EmailSender.promises.sendEmail(this.opts)
  57. expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
  58. html: this.opts.html,
  59. to: this.opts.to,
  60. subject: this.opts.subject
  61. })
  62. })
  63. it('should return a non-specific error', async function() {
  64. this.sesClient.sendMail.rejects(new Error('boom'))
  65. await expect(this.EmailSender.promises.sendEmail({})).to.be.rejectedWith(
  66. 'error sending message'
  67. )
  68. })
  69. it('should use the from address from settings', async function() {
  70. await this.EmailSender.promises.sendEmail(this.opts)
  71. expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
  72. from: this.Settings.email.fromAddress
  73. })
  74. })
  75. it('should use the reply to address from settings', async function() {
  76. await this.EmailSender.promises.sendEmail(this.opts)
  77. expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
  78. replyTo: this.Settings.email.replyToAddress
  79. })
  80. })
  81. it('should use the reply to address in options as an override', async function() {
  82. this.opts.replyTo = 'someone@else.com'
  83. await this.EmailSender.promises.sendEmail(this.opts)
  84. expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
  85. replyTo: this.opts.replyTo
  86. })
  87. })
  88. it('should not send an email when the rate limiter says no', async function() {
  89. this.opts.sendingUser_id = '12321312321'
  90. this.RateLimiter.promises.addCount.resolves(false)
  91. await expect(this.EmailSender.promises.sendEmail(this.opts)).to.be
  92. .rejected
  93. expect(this.sesClient.sendMail).not.to.have.been.called
  94. })
  95. it('should send the email when the rate limtier says continue', async function() {
  96. this.opts.sendingUser_id = '12321312321'
  97. this.RateLimiter.promises.addCount.resolves(true)
  98. await this.EmailSender.promises.sendEmail(this.opts)
  99. expect(this.sesClient.sendMail).to.have.been.called
  100. })
  101. it('should not check the rate limiter when there is no sendingUser_id', async function() {
  102. this.EmailSender.sendEmail(this.opts, () => {
  103. expect(this.sesClient.sendMail).to.have.been.called
  104. expect(this.RateLimiter.promises.addCount).not.to.have.been.called
  105. })
  106. })
  107. describe('with plain-text email content', function() {
  108. beforeEach(function() {
  109. this.opts.text = 'hello there'
  110. })
  111. it('should set the text property on the email to send', async function() {
  112. await this.EmailSender.promises.sendEmail(this.opts)
  113. expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
  114. html: this.opts.html,
  115. text: this.opts.text,
  116. to: this.opts.to,
  117. subject: this.opts.subject
  118. })
  119. })
  120. })
  121. })
  122. })