UserEmailsControllerTests.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838
  1. const sinon = require('sinon')
  2. const assertCalledWith = sinon.assert.calledWith
  3. const assertNotCalled = sinon.assert.notCalled
  4. const { assert, expect } = require('chai')
  5. const modulePath = '../../../../app/src/Features/User/UserEmailsController.js'
  6. const SandboxedModule = require('sandboxed-module')
  7. const MockRequest = require('../helpers/MockRequest')
  8. const MockResponse = require('../helpers/MockResponse')
  9. const Errors = require('../../../../app/src/Features/Errors/Errors')
  10. describe('UserEmailsController', function () {
  11. beforeEach(function () {
  12. this.req = new MockRequest()
  13. this.req.sessionID = Math.random().toString()
  14. this.res = new MockResponse()
  15. this.next = sinon.stub()
  16. this.user = {
  17. _id: 'mock-user-id',
  18. email: 'example@overleaf.com',
  19. emails: [],
  20. }
  21. this.UserGetter = {
  22. getUser: sinon.stub().yields(),
  23. getUserFullEmails: sinon.stub(),
  24. getUserByAnyEmail: sinon.stub(),
  25. promises: {
  26. ensureUniqueEmailAddress: sinon.stub().resolves(),
  27. getUser: sinon.stub().resolves(this.user),
  28. },
  29. }
  30. this.SessionManager = {
  31. getSessionUser: sinon.stub().returns(this.user),
  32. getLoggedInUserId: sinon.stub().returns(this.user._id),
  33. setInSessionUser: sinon.stub(),
  34. }
  35. this.Features = {
  36. hasFeature: sinon.stub(),
  37. }
  38. this.UserSessionsManager = {
  39. revokeAllUserSessions: sinon.stub().yields(),
  40. }
  41. this.UserUpdater = {
  42. addEmailAddress: sinon.stub(),
  43. removeEmailAddress: sinon.stub(),
  44. setDefaultEmailAddress: sinon.stub(),
  45. updateV1AndSetDefaultEmailAddress: sinon.stub(),
  46. promises: {
  47. addEmailAddress: sinon.stub().resolves(),
  48. confirmEmail: sinon.stub().resolves(),
  49. },
  50. }
  51. this.EmailHelper = { parseEmail: sinon.stub() }
  52. this.endorseAffiliation = sinon.stub().yields()
  53. this.InstitutionsAPI = {
  54. endorseAffiliation: this.endorseAffiliation,
  55. }
  56. this.HttpErrorHandler = { conflict: sinon.stub() }
  57. this.AnalyticsManager = {
  58. recordEventForUser: sinon.stub(),
  59. }
  60. this.UserAuditLogHandler = {
  61. addEntry: sinon.stub().yields(),
  62. promises: {
  63. addEntry: sinon.stub().resolves(),
  64. },
  65. }
  66. this.rateLimiter = {
  67. consume: sinon.stub().resolves(),
  68. }
  69. this.RateLimiter = {
  70. RateLimiter: sinon.stub().returns(this.rateLimiter),
  71. }
  72. this.AuthenticationController = {
  73. getRedirectFromSession: sinon.stub().returns(null),
  74. }
  75. this.UserEmailsController = SandboxedModule.require(modulePath, {
  76. requires: {
  77. '../Authentication/AuthenticationController':
  78. this.AuthenticationController,
  79. '../Authentication/SessionManager': this.SessionManager,
  80. '../../infrastructure/Features': this.Features,
  81. './UserSessionsManager': this.UserSessionsManager,
  82. './UserGetter': this.UserGetter,
  83. './UserUpdater': this.UserUpdater,
  84. '../Email/EmailHandler': (this.EmailHandler = {
  85. promises: {
  86. sendEmail: sinon.stub().resolves(),
  87. },
  88. }),
  89. '../Helpers/EmailHelper': this.EmailHelper,
  90. './UserEmailsConfirmationHandler': (this.UserEmailsConfirmationHandler =
  91. {
  92. sendReconfirmationEmail: sinon.stub(),
  93. promises: {
  94. sendConfirmationEmail: sinon.stub().resolves(),
  95. },
  96. }),
  97. '../Institutions/InstitutionsAPI': this.InstitutionsAPI,
  98. '../Errors/HttpErrorHandler': this.HttpErrorHandler,
  99. '../Analytics/AnalyticsManager': this.AnalyticsManager,
  100. './UserAuditLogHandler': this.UserAuditLogHandler,
  101. '../../infrastructure/RateLimiter': this.RateLimiter,
  102. },
  103. })
  104. })
  105. describe('List', function () {
  106. beforeEach(function () {})
  107. it('lists emails', function (done) {
  108. const fullEmails = [{ some: 'data' }]
  109. this.UserGetter.getUserFullEmails.callsArgWith(1, null, fullEmails)
  110. this.UserEmailsController.list(this.req, {
  111. json: response => {
  112. assert.deepEqual(response, fullEmails)
  113. assertCalledWith(this.UserGetter.getUserFullEmails, this.user._id)
  114. done()
  115. },
  116. })
  117. })
  118. })
  119. describe('Add', function () {
  120. beforeEach(function () {
  121. this.newEmail = 'new_email@baz.com'
  122. this.req.body = {
  123. email: this.newEmail,
  124. university: { name: 'University Name' },
  125. department: 'Department',
  126. role: 'Role',
  127. }
  128. this.EmailHelper.parseEmail.returns(this.newEmail)
  129. this.UserEmailsConfirmationHandler.sendConfirmationEmail = sinon
  130. .stub()
  131. .yields()
  132. })
  133. it('passed audit log to addEmailAddress', function (done) {
  134. this.res.sendStatus = sinon.stub()
  135. this.res.sendStatus.callsFake(() => {
  136. const addCall = this.UserUpdater.promises.addEmailAddress.lastCall
  137. expect(addCall.args[3]).to.deep.equal({
  138. initiatorId: this.user._id,
  139. ipAddress: this.req.ip,
  140. })
  141. done()
  142. })
  143. this.UserEmailsController.add(this.req, this.res)
  144. })
  145. it('adds new email', function (done) {
  146. this.UserEmailsController.add(
  147. this.req,
  148. {
  149. sendStatus: code => {
  150. code.should.equal(204)
  151. assertCalledWith(this.EmailHelper.parseEmail, this.newEmail)
  152. assertCalledWith(
  153. this.UserUpdater.promises.addEmailAddress,
  154. this.user._id,
  155. this.newEmail
  156. )
  157. const affiliationOptions =
  158. this.UserUpdater.promises.addEmailAddress.lastCall.args[2]
  159. Object.keys(affiliationOptions).length.should.equal(3)
  160. affiliationOptions.university.should.equal(this.req.body.university)
  161. affiliationOptions.department.should.equal(this.req.body.department)
  162. affiliationOptions.role.should.equal(this.req.body.role)
  163. done()
  164. },
  165. },
  166. this.next
  167. )
  168. })
  169. it('sends a security alert email', function (done) {
  170. this.res.sendStatus = sinon.stub()
  171. this.res.sendStatus.callsFake(() => {
  172. const emailCall = this.EmailHandler.promises.sendEmail.getCall(0)
  173. emailCall.args[0].should.to.equal('securityAlert')
  174. emailCall.args[1].to.should.equal(this.user.email)
  175. emailCall.args[1].actionDescribed.should.contain(
  176. 'a secondary email address'
  177. )
  178. emailCall.args[1].to.should.equal(this.user.email)
  179. emailCall.args[1].message[0].should.contain(this.newEmail)
  180. done()
  181. })
  182. this.UserEmailsController.add(this.req, this.res)
  183. })
  184. it('sends an email confirmation', function (done) {
  185. this.UserEmailsController.add(
  186. this.req,
  187. {
  188. sendStatus: code => {
  189. code.should.equal(204)
  190. assertCalledWith(
  191. this.UserEmailsConfirmationHandler.promises.sendConfirmationEmail,
  192. this.user._id,
  193. this.newEmail
  194. )
  195. done()
  196. },
  197. },
  198. this.next
  199. )
  200. })
  201. it('handles email parse error', function (done) {
  202. this.EmailHelper.parseEmail.returns(null)
  203. this.UserEmailsController.add(
  204. this.req,
  205. {
  206. sendStatus: code => {
  207. code.should.equal(422)
  208. assertNotCalled(this.UserUpdater.promises.addEmailAddress)
  209. done()
  210. },
  211. },
  212. this.next
  213. )
  214. })
  215. it('should pass the error to the next handler when adding the email fails', function (done) {
  216. this.UserUpdater.promises.addEmailAddress.rejects(new Error())
  217. this.UserEmailsController.add(this.req, this.res, error => {
  218. expect(error).to.be.instanceof(Error)
  219. done()
  220. })
  221. })
  222. it('should call the HTTP conflict handler when the email already exists', function (done) {
  223. this.UserUpdater.promises.addEmailAddress.rejects(
  224. new Errors.EmailExistsError()
  225. )
  226. this.HttpErrorHandler.conflict = sinon.spy((req, res, message) => {
  227. req.should.exist
  228. res.should.exist
  229. message.should.equal('email_already_registered')
  230. done()
  231. })
  232. this.UserEmailsController.add(this.req, this.res, this.next)
  233. })
  234. it("should call the HTTP conflict handler when there's a domain matching error", function (done) {
  235. this.UserUpdater.promises.addEmailAddress.rejects(
  236. new Error('422: Email does not belong to university')
  237. )
  238. this.HttpErrorHandler.conflict = sinon.spy((req, res, message) => {
  239. req.should.exist
  240. res.should.exist
  241. message.should.equal('email_does_not_belong_to_university')
  242. done()
  243. })
  244. this.UserEmailsController.add(this.req, this.res, this.next)
  245. })
  246. it('should fail to add new emails when the limit has been reached', function (done) {
  247. this.user.emails = []
  248. for (let i = 0; i < 10; i++) {
  249. this.user.emails.push({ email: `example${i}@overleaf.com` })
  250. }
  251. this.UserEmailsController.add(
  252. this.req,
  253. {
  254. status: code => {
  255. expect(code).to.equal(422)
  256. return {
  257. json: error => {
  258. expect(error.message).to.equal('secondary email limit exceeded')
  259. done()
  260. },
  261. }
  262. },
  263. },
  264. this.next
  265. )
  266. })
  267. })
  268. describe('addWithConfirmationCode', function () {
  269. beforeEach(function () {
  270. this.newEmail = 'new_email@baz.com'
  271. this.req.body = {
  272. email: this.newEmail,
  273. }
  274. this.EmailHelper.parseEmail.returns(this.newEmail)
  275. this.UserEmailsConfirmationHandler.promises.sendConfirmationCode = sinon
  276. .stub()
  277. .resolves({
  278. confirmCode: '123456',
  279. confirmCodeExpiresTimestamp: new Date(),
  280. })
  281. })
  282. it('sends an email confirmation', function (done) {
  283. this.UserEmailsController.addWithConfirmationCode(this.req, {
  284. json: ({ redir }) => {
  285. redir.should.equal('/user/emails/confirm-secondary')
  286. assertCalledWith(
  287. this.UserEmailsConfirmationHandler.promises.sendConfirmationCode,
  288. this.newEmail,
  289. true
  290. )
  291. done()
  292. },
  293. })
  294. })
  295. it('handles email parse error', function (done) {
  296. this.EmailHelper.parseEmail.returns(null)
  297. this.UserEmailsController.addWithConfirmationCode(this.req, {
  298. sendStatus: code => {
  299. code.should.equal(422)
  300. done()
  301. },
  302. })
  303. })
  304. it('handles when the email already exists', function (done) {
  305. this.UserGetter.promises.ensureUniqueEmailAddress.rejects(
  306. new Errors.EmailExistsError()
  307. )
  308. this.UserEmailsController.addWithConfirmationCode(this.req, {
  309. status: code => {
  310. code.should.equal(409)
  311. return { json: () => done() }
  312. },
  313. })
  314. })
  315. it('should fail to add new emails when the limit has been reached', function (done) {
  316. this.user.emails = []
  317. for (let i = 0; i < 10; i++) {
  318. this.user.emails.push({ email: `example${i}@overleaf.com` })
  319. }
  320. this.UserEmailsController.addWithConfirmationCode(this.req, {
  321. status: code => {
  322. expect(code).to.equal(422)
  323. return {
  324. json: error => {
  325. expect(error.message).to.equal('secondary email limit exceeded')
  326. done()
  327. },
  328. }
  329. },
  330. })
  331. })
  332. })
  333. describe('checkSecondaryEmailConfirmationCode', function () {
  334. beforeEach(function () {
  335. this.newEmail = 'new_email@baz.com'
  336. this.req.session.pendingSecondaryEmail = {
  337. confirmCode: '123456',
  338. email: this.newEmail,
  339. confirmCodeExpiresTimestamp: new Date(Math.max),
  340. }
  341. })
  342. describe('with a valid confirmation code', function () {
  343. beforeEach(function () {
  344. this.req.body = {
  345. code: '123456',
  346. }
  347. })
  348. it('adds the email', function (done) {
  349. this.UserEmailsController.checkSecondaryEmailConfirmationCode(
  350. this.req,
  351. {
  352. json: () => {
  353. assertCalledWith(
  354. this.UserUpdater.promises.addEmailAddress,
  355. this.user._id,
  356. this.newEmail
  357. )
  358. assertCalledWith(
  359. this.UserUpdater.promises.confirmEmail,
  360. this.user._id,
  361. this.newEmail
  362. )
  363. done()
  364. },
  365. }
  366. )
  367. })
  368. it('redirects to /project', function (done) {
  369. this.UserEmailsController.checkSecondaryEmailConfirmationCode(
  370. this.req,
  371. {
  372. json: ({ redir }) => {
  373. redir.should.equal('/project')
  374. done()
  375. },
  376. }
  377. )
  378. })
  379. })
  380. describe('with an invalid confirmation code', function () {
  381. beforeEach(function () {
  382. this.req.body = {
  383. code: '999999',
  384. }
  385. })
  386. it('does not add the email', function (done) {
  387. this.UserEmailsController.checkSecondaryEmailConfirmationCode(
  388. this.req,
  389. {
  390. status: () => {
  391. assertNotCalled(this.UserUpdater.promises.addEmailAddress)
  392. assertNotCalled(this.UserUpdater.promises.confirmEmail)
  393. done()
  394. return { json: this.next }
  395. },
  396. }
  397. )
  398. })
  399. it('responds with a 403', function (done) {
  400. this.UserEmailsController.checkSecondaryEmailConfirmationCode(
  401. this.req,
  402. {
  403. status: code => {
  404. code.should.equal(403)
  405. done()
  406. return { json: this.next }
  407. },
  408. }
  409. )
  410. })
  411. })
  412. })
  413. describe('resendSecondaryEmailConfirmationCode', function () {
  414. beforeEach(function () {
  415. this.newEmail = 'new_email@baz.com'
  416. this.req.session.pendingSecondaryEmail = {
  417. confirmCode: '123456',
  418. email: this.newEmail,
  419. confirmCodeExpiresTimestamp: new Date(Math.max),
  420. }
  421. this.UserEmailsConfirmationHandler.promises.sendConfirmationCode = sinon
  422. .stub()
  423. .resolves({
  424. confirmCode: '123456',
  425. confirmCodeExpiresTimestamp: new Date(),
  426. })
  427. })
  428. it('should send the email', function (done) {
  429. this.UserEmailsController.resendSecondaryEmailConfirmationCode(this.req, {
  430. status: code => {
  431. code.should.equal(200)
  432. assertCalledWith(
  433. this.UserEmailsConfirmationHandler.promises.sendConfirmationCode,
  434. this.newEmail,
  435. true
  436. )
  437. done()
  438. return { json: this.next }
  439. },
  440. })
  441. })
  442. })
  443. describe('remove', function () {
  444. beforeEach(function () {
  445. this.email = 'email_to_remove@bar.com'
  446. this.req.body.email = this.email
  447. this.EmailHelper.parseEmail.returns(this.email)
  448. })
  449. it('removes email', function (done) {
  450. const auditLog = {
  451. initiatorId: this.user._id,
  452. ipAddress: this.req.ip,
  453. }
  454. this.UserUpdater.removeEmailAddress.callsArgWith(3, null)
  455. this.UserEmailsController.remove(this.req, {
  456. sendStatus: code => {
  457. code.should.equal(200)
  458. assertCalledWith(this.EmailHelper.parseEmail, this.email)
  459. assertCalledWith(
  460. this.UserUpdater.removeEmailAddress,
  461. this.user._id,
  462. this.email,
  463. auditLog
  464. )
  465. done()
  466. },
  467. })
  468. })
  469. it('handles email parse error', function (done) {
  470. this.EmailHelper.parseEmail.returns(null)
  471. this.UserEmailsController.remove(this.req, {
  472. sendStatus: code => {
  473. code.should.equal(422)
  474. assertNotCalled(this.UserUpdater.removeEmailAddress)
  475. done()
  476. },
  477. })
  478. })
  479. })
  480. describe('setDefault', function () {
  481. beforeEach(function () {
  482. this.email = 'email_to_set_default@bar.com'
  483. this.req.body.email = this.email
  484. this.EmailHelper.parseEmail.returns(this.email)
  485. this.SessionManager.setInSessionUser.returns(null)
  486. })
  487. it('sets default email', function (done) {
  488. this.UserUpdater.setDefaultEmailAddress.yields()
  489. this.UserEmailsController.setDefault(this.req, {
  490. sendStatus: code => {
  491. code.should.equal(200)
  492. assertCalledWith(this.EmailHelper.parseEmail, this.email)
  493. assertCalledWith(
  494. this.SessionManager.setInSessionUser,
  495. this.req.session,
  496. {
  497. email: this.email,
  498. }
  499. )
  500. assertCalledWith(
  501. this.UserUpdater.setDefaultEmailAddress,
  502. this.user._id,
  503. this.email
  504. )
  505. done()
  506. },
  507. })
  508. })
  509. it('handles email parse error', function (done) {
  510. this.EmailHelper.parseEmail.returns(null)
  511. this.UserEmailsController.setDefault(this.req, {
  512. sendStatus: code => {
  513. code.should.equal(422)
  514. assertNotCalled(this.UserUpdater.setDefaultEmailAddress)
  515. done()
  516. },
  517. })
  518. })
  519. it('should reset the users other sessions', function (done) {
  520. this.UserUpdater.setDefaultEmailAddress.yields()
  521. this.res.callback = () => {
  522. expect(
  523. this.UserSessionsManager.revokeAllUserSessions
  524. ).to.have.been.calledWith(this.user, [this.req.sessionID])
  525. done()
  526. }
  527. this.UserEmailsController.setDefault(this.req, this.res, done)
  528. })
  529. it('handles error from revoking sessions and returns 200', function (done) {
  530. this.UserUpdater.setDefaultEmailAddress.yields()
  531. const redisError = new Error('redis error')
  532. this.UserSessionsManager.revokeAllUserSessions = sinon
  533. .stub()
  534. .yields(redisError)
  535. this.res.callback = () => {
  536. expect(this.res.statusCode).to.equal(200)
  537. // give revoke process time to run
  538. setTimeout(() => {
  539. expect(this.logger.warn).to.have.been.calledWith(
  540. sinon.match({ err: redisError }),
  541. 'failed revoking secondary sessions after changing default email'
  542. )
  543. done()
  544. })
  545. }
  546. this.UserEmailsController.setDefault(this.req, this.res, done)
  547. })
  548. })
  549. describe('endorse', function () {
  550. beforeEach(function () {
  551. this.email = 'email_to_endorse@bar.com'
  552. this.req.body.email = this.email
  553. this.EmailHelper.parseEmail.returns(this.email)
  554. })
  555. it('endorses affiliation', function (done) {
  556. this.req.body.role = 'Role'
  557. this.req.body.department = 'Department'
  558. this.UserEmailsController.endorse(this.req, {
  559. sendStatus: code => {
  560. code.should.equal(204)
  561. assertCalledWith(
  562. this.endorseAffiliation,
  563. this.user._id,
  564. this.email,
  565. 'Role',
  566. 'Department'
  567. )
  568. done()
  569. },
  570. })
  571. })
  572. })
  573. describe('confirm', function () {
  574. beforeEach(function () {
  575. this.UserEmailsConfirmationHandler.confirmEmailFromToken = sinon
  576. .stub()
  577. .yields(null, { userId: this.user._id, email: this.user.email })
  578. this.res = {
  579. sendStatus: sinon.stub(),
  580. json: sinon.stub(),
  581. }
  582. this.res.status = sinon.stub().returns(this.res)
  583. this.next = sinon.stub()
  584. this.token = 'mock-token'
  585. this.req.body = { token: this.token }
  586. this.req.ip = '0.0.0.0'
  587. })
  588. describe('successfully', function () {
  589. beforeEach(function () {
  590. this.UserEmailsController.confirm(this.req, this.res, this.next)
  591. })
  592. it('should confirm the email from the token', function () {
  593. this.UserEmailsConfirmationHandler.confirmEmailFromToken
  594. .calledWith(this.req, this.token)
  595. .should.equal(true)
  596. })
  597. it('should return a 200 status', function () {
  598. this.res.sendStatus.calledWith(200).should.equal(true)
  599. })
  600. it('should log the confirmation to the audit log', function () {
  601. sinon.assert.calledWith(
  602. this.UserAuditLogHandler.addEntry,
  603. this.user._id,
  604. 'confirm-email',
  605. this.user._id,
  606. this.req.ip,
  607. {
  608. token: this.token.substring(0, 10),
  609. email: this.user.email,
  610. }
  611. )
  612. })
  613. })
  614. describe('without a token', function () {
  615. beforeEach(function () {
  616. this.req.body.token = null
  617. this.UserEmailsController.confirm(this.req, this.res, this.next)
  618. })
  619. it('should return a 422 status', function () {
  620. this.res.status.calledWith(422).should.equal(true)
  621. })
  622. })
  623. describe('when confirming fails', function () {
  624. beforeEach(function () {
  625. this.UserEmailsConfirmationHandler.confirmEmailFromToken = sinon
  626. .stub()
  627. .yields(new Errors.NotFoundError('not found'))
  628. this.UserEmailsController.confirm(this.req, this.res, this.next)
  629. })
  630. it('should return a 404 error code with a message', function () {
  631. this.res.status.calledWith(404).should.equal(true)
  632. this.res.json
  633. .calledWith({
  634. message: this.req.i18n.translate('confirmation_token_invalid'),
  635. })
  636. .should.equal(true)
  637. })
  638. })
  639. })
  640. describe('resendConfirmation', function () {
  641. beforeEach(function () {
  642. this.EmailHelper.parseEmail.returnsArg(0)
  643. this.UserGetter.getUserByAnyEmail.yields(undefined, {
  644. _id: this.user._id,
  645. })
  646. this.req = {
  647. body: {},
  648. }
  649. this.res = {
  650. sendStatus: sinon.stub(),
  651. }
  652. this.next = sinon.stub()
  653. this.UserEmailsConfirmationHandler.sendConfirmationEmail = sinon
  654. .stub()
  655. .yields()
  656. })
  657. it('should send the email', function (done) {
  658. this.req = {
  659. body: {
  660. email: 'test@example.com',
  661. },
  662. }
  663. this.UserEmailsController.sendReconfirmation(
  664. this.req,
  665. this.res,
  666. this.next
  667. )
  668. expect(this.UserEmailsConfirmationHandler.sendReconfirmationEmail).to.have
  669. .been.calledOnce
  670. done()
  671. })
  672. it('should return 422 if email not valid', function (done) {
  673. this.req = {
  674. body: {},
  675. }
  676. this.UserEmailsController.resendConfirmation(
  677. this.req,
  678. this.res,
  679. this.next
  680. )
  681. expect(this.UserEmailsConfirmationHandler.sendConfirmationEmail).to.not
  682. .have.been.called
  683. expect(this.res.sendStatus.lastCall.args[0]).to.equal(422)
  684. done()
  685. })
  686. describe('email on another user account', function () {
  687. beforeEach(function () {
  688. this.UserGetter.getUserByAnyEmail.yields(undefined, {
  689. _id: 'another-user-id',
  690. })
  691. })
  692. it('should return 422', function (done) {
  693. this.req = {
  694. body: {
  695. email: 'test@example.com',
  696. },
  697. }
  698. this.UserEmailsController.resendConfirmation(
  699. this.req,
  700. this.res,
  701. this.next
  702. )
  703. expect(this.UserEmailsConfirmationHandler.sendConfirmationEmail).to.not
  704. .have.been.called
  705. expect(this.res.sendStatus.lastCall.args[0]).to.equal(422)
  706. done()
  707. })
  708. })
  709. })
  710. describe('sendReconfirmation', function () {
  711. beforeEach(function () {
  712. this.res.sendStatus = sinon.stub()
  713. this.UserGetter.getUserByAnyEmail.yields(undefined, {
  714. _id: this.user._id,
  715. })
  716. this.EmailHelper.parseEmail.returnsArg(0)
  717. })
  718. it('should send the email', function (done) {
  719. this.req = {
  720. body: {
  721. email: 'test@example.com',
  722. },
  723. }
  724. this.UserEmailsController.sendReconfirmation(
  725. this.req,
  726. this.res,
  727. this.next
  728. )
  729. expect(this.UserEmailsConfirmationHandler.sendReconfirmationEmail).to.have
  730. .been.calledOnce
  731. done()
  732. })
  733. it('should return 400 if email not valid', function (done) {
  734. this.req = {
  735. body: {},
  736. }
  737. this.UserEmailsController.sendReconfirmation(
  738. this.req,
  739. this.res,
  740. this.next
  741. )
  742. expect(this.UserEmailsConfirmationHandler.sendReconfirmationEmail).to.not
  743. .have.been.called
  744. expect(this.res.sendStatus.lastCall.args[0]).to.equal(400)
  745. done()
  746. })
  747. describe('email on another user account', function () {
  748. beforeEach(function () {
  749. this.UserGetter.getUserByAnyEmail.yields(undefined, {
  750. _id: 'another-user-id',
  751. })
  752. })
  753. it('should return 422', function (done) {
  754. this.req = {
  755. body: {
  756. email: 'test@example.com',
  757. },
  758. }
  759. this.UserEmailsController.sendReconfirmation(
  760. this.req,
  761. this.res,
  762. this.next
  763. )
  764. expect(this.UserEmailsConfirmationHandler.sendReconfirmationEmail).to
  765. .not.have.been.called
  766. expect(this.res.sendStatus.lastCall.args[0]).to.equal(422)
  767. done()
  768. })
  769. })
  770. })
  771. })