UserUpdaterTests.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /* eslint-disable
  2. max-len,
  3. no-return-assign,
  4. no-unused-vars,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  12. */
  13. const should = require('chai').should()
  14. const SandboxedModule = require('sandboxed-module')
  15. const assert = require('assert')
  16. const path = require('path')
  17. const sinon = require('sinon')
  18. const modulePath = path.join(
  19. __dirname,
  20. '../../../../app/src/Features/User/UserUpdater'
  21. )
  22. const { expect } = require('chai')
  23. const tk = require('timekeeper')
  24. describe('UserUpdater', function() {
  25. beforeEach(function() {
  26. tk.freeze(Date.now())
  27. this.mongojs = {
  28. db: {},
  29. ObjectId(id) {
  30. return id
  31. }
  32. }
  33. this.UserGetter = {
  34. getUserEmail: sinon.stub(),
  35. getUserByAnyEmail: sinon.stub(),
  36. ensureUniqueEmailAddress: sinon.stub()
  37. }
  38. this.logger = {
  39. err: sinon.stub(),
  40. log() {},
  41. warn: sinon.stub()
  42. }
  43. this.addAffiliation = sinon.stub().yields()
  44. this.removeAffiliation = sinon.stub().callsArgWith(2, null)
  45. this.refreshFeatures = sinon.stub().yields()
  46. this.NewsletterManager = { changeEmail: sinon.stub() }
  47. this.UserUpdater = SandboxedModule.require(modulePath, {
  48. globals: {
  49. console: console
  50. },
  51. requires: {
  52. 'logger-sharelatex': this.logger,
  53. '../../infrastructure/mongojs': this.mongojs,
  54. 'metrics-sharelatex': {
  55. timeAsyncMethod: sinon.stub()
  56. },
  57. './UserGetter': this.UserGetter,
  58. '../Institutions/InstitutionsAPI': {
  59. addAffiliation: this.addAffiliation,
  60. removeAffiliation: this.removeAffiliation
  61. },
  62. '../Subscription/FeaturesUpdater': {
  63. refreshFeatures: this.refreshFeatures
  64. },
  65. 'settings-sharelatex': (this.settings = {}),
  66. request: (this.request = {}),
  67. '../Newsletter/NewsletterManager': this.NewsletterManager
  68. }
  69. })
  70. this.stubbedUser = {
  71. _id: '3131231',
  72. name: 'bob',
  73. email: 'hello@world.com'
  74. }
  75. this.newEmail = 'bob@bob.com'
  76. this.callback = sinon.stub()
  77. })
  78. afterEach(() => tk.reset())
  79. describe('changeEmailAddress', function() {
  80. beforeEach(function() {
  81. this.UserGetter.getUserEmail.callsArgWith(1, null, this.stubbedUser.email)
  82. this.UserUpdater.addEmailAddress = sinon.stub().callsArgWith(2)
  83. this.UserUpdater.setDefaultEmailAddress = sinon.stub().yields()
  84. return (this.UserUpdater.removeEmailAddress = sinon
  85. .stub()
  86. .callsArgWith(2))
  87. })
  88. it('change email', function(done) {
  89. return this.UserUpdater.changeEmailAddress(
  90. this.stubbedUser._id,
  91. this.newEmail,
  92. err => {
  93. should.not.exist(err)
  94. this.UserUpdater.addEmailAddress
  95. .calledWith(this.stubbedUser._id, this.newEmail)
  96. .should.equal(true)
  97. this.UserUpdater.setDefaultEmailAddress
  98. .calledWith(this.stubbedUser._id, this.newEmail)
  99. .should.equal(true)
  100. this.UserUpdater.removeEmailAddress
  101. .calledWith(this.stubbedUser._id, this.stubbedUser.email)
  102. .should.equal(true)
  103. return done()
  104. }
  105. )
  106. })
  107. it('validates email', function(done) {
  108. return this.UserUpdater.changeEmailAddress(
  109. this.stubbedUser._id,
  110. 'foo',
  111. err => {
  112. should.exist(err)
  113. return done()
  114. }
  115. )
  116. })
  117. it('handle error', function(done) {
  118. this.UserUpdater.removeEmailAddress.callsArgWith(2, new Error('nope'))
  119. return this.UserUpdater.changeEmailAddress(
  120. this.stubbedUser._id,
  121. this.newEmail,
  122. err => {
  123. should.exist(err)
  124. return done()
  125. }
  126. )
  127. })
  128. })
  129. describe('addEmailAddress', function() {
  130. beforeEach(function() {
  131. this.UserGetter.ensureUniqueEmailAddress = sinon.stub().callsArgWith(1)
  132. return (this.UserUpdater.updateUser = sinon.stub().callsArgWith(2, null))
  133. })
  134. it('add email', function(done) {
  135. return this.UserUpdater.addEmailAddress(
  136. this.stubbedUser._id,
  137. this.newEmail,
  138. err => {
  139. this.UserGetter.ensureUniqueEmailAddress.called.should.equal(true)
  140. should.not.exist(err)
  141. const reversedHostname = this.newEmail
  142. .split('@')[1]
  143. .split('')
  144. .reverse()
  145. .join('')
  146. this.UserUpdater.updateUser
  147. .calledWith(this.stubbedUser._id, {
  148. $push: {
  149. emails: {
  150. email: this.newEmail,
  151. createdAt: sinon.match.date,
  152. reversedHostname
  153. }
  154. }
  155. })
  156. .should.equal(true)
  157. return done()
  158. }
  159. )
  160. })
  161. it('add affiliation', function(done) {
  162. const affiliationOptions = {
  163. university: { id: 1 },
  164. role: 'Prof',
  165. department: 'Math'
  166. }
  167. return this.UserUpdater.addEmailAddress(
  168. this.stubbedUser._id,
  169. this.newEmail,
  170. affiliationOptions,
  171. err => {
  172. should.not.exist(err)
  173. this.addAffiliation.calledOnce.should.equal(true)
  174. const { args } = this.addAffiliation.lastCall
  175. args[0].should.equal(this.stubbedUser._id)
  176. args[1].should.equal(this.newEmail)
  177. args[2].should.equal(affiliationOptions)
  178. return done()
  179. }
  180. )
  181. })
  182. it('handle error', function(done) {
  183. this.UserUpdater.updateUser = sinon
  184. .stub()
  185. .callsArgWith(2, new Error('nope'))
  186. return this.UserUpdater.addEmailAddress(
  187. this.stubbedUser._id,
  188. this.newEmail,
  189. err => {
  190. this.logger.warn.called.should.equal(true)
  191. should.exist(err)
  192. return done()
  193. }
  194. )
  195. })
  196. it('handle affiliation error', function(done) {
  197. const body = { errors: 'affiliation error message' }
  198. this.addAffiliation.callsArgWith(3, new Error('nope'))
  199. return this.UserUpdater.addEmailAddress(
  200. this.stubbedUser._id,
  201. this.newEmail,
  202. err => {
  203. should.exist(err)
  204. this.UserUpdater.updateUser.called.should.equal(false)
  205. return done()
  206. }
  207. )
  208. })
  209. it('validates email', function(done) {
  210. return this.UserUpdater.addEmailAddress(
  211. this.stubbedUser._id,
  212. 'bar',
  213. err => {
  214. should.exist(err)
  215. return done()
  216. }
  217. )
  218. })
  219. })
  220. describe('removeEmailAddress', function() {
  221. beforeEach(function() {
  222. return (this.UserUpdater.updateUser = sinon
  223. .stub()
  224. .callsArgWith(2, null, { nMatched: 1 }))
  225. })
  226. it('remove email', function(done) {
  227. return this.UserUpdater.removeEmailAddress(
  228. this.stubbedUser._id,
  229. this.newEmail,
  230. err => {
  231. should.not.exist(err)
  232. this.UserUpdater.updateUser
  233. .calledWith(
  234. { _id: this.stubbedUser._id, email: { $ne: this.newEmail } },
  235. { $pull: { emails: { email: this.newEmail } } }
  236. )
  237. .should.equal(true)
  238. return done()
  239. }
  240. )
  241. })
  242. it('remove affiliation', function(done) {
  243. return this.UserUpdater.removeEmailAddress(
  244. this.stubbedUser._id,
  245. this.newEmail,
  246. err => {
  247. should.not.exist(err)
  248. this.removeAffiliation.calledOnce.should.equal(true)
  249. const { args } = this.removeAffiliation.lastCall
  250. args[0].should.equal(this.stubbedUser._id)
  251. args[1].should.equal(this.newEmail)
  252. return done()
  253. }
  254. )
  255. })
  256. it('handle error', function(done) {
  257. this.UserUpdater.updateUser = sinon
  258. .stub()
  259. .callsArgWith(2, new Error('nope'))
  260. return this.UserUpdater.removeEmailAddress(
  261. this.stubbedUser._id,
  262. this.newEmail,
  263. err => {
  264. should.exist(err)
  265. return done()
  266. }
  267. )
  268. })
  269. it('handle missed update', function(done) {
  270. this.UserUpdater.updateUser = sinon.stub().callsArgWith(2, null, { n: 0 })
  271. return this.UserUpdater.removeEmailAddress(
  272. this.stubbedUser._id,
  273. this.newEmail,
  274. err => {
  275. should.exist(err)
  276. return done()
  277. }
  278. )
  279. })
  280. it('handle affiliation error', function(done) {
  281. this.removeAffiliation.callsArgWith(2, new Error('nope'))
  282. return this.UserUpdater.removeEmailAddress(
  283. this.stubbedUser._id,
  284. this.newEmail,
  285. err => {
  286. should.exist(err)
  287. this.UserUpdater.updateUser.called.should.equal(false)
  288. return done()
  289. }
  290. )
  291. })
  292. it('validates email', function(done) {
  293. return this.UserUpdater.removeEmailAddress(
  294. this.stubbedUser._id,
  295. 'baz',
  296. err => {
  297. should.exist(err)
  298. return done()
  299. }
  300. )
  301. })
  302. })
  303. describe('setDefaultEmailAddress', function() {
  304. beforeEach(function() {
  305. this.stubbedUser.emails = [
  306. {
  307. email: this.newEmail,
  308. confirmedAt: new Date()
  309. }
  310. ]
  311. this.UserGetter.getUser = sinon.stub().yields(null, this.stubbedUser)
  312. this.NewsletterManager.changeEmail.callsArgWith(2, null)
  313. })
  314. it('set default', function(done) {
  315. this.UserUpdater.updateUser = sinon.stub().callsArgWith(2, null, { n: 1 })
  316. this.UserUpdater.setDefaultEmailAddress(
  317. this.stubbedUser._id,
  318. this.newEmail,
  319. err => {
  320. should.not.exist(err)
  321. this.UserUpdater.updateUser
  322. .calledWith(
  323. { _id: this.stubbedUser._id, 'emails.email': this.newEmail },
  324. { $set: { email: this.newEmail } }
  325. )
  326. .should.equal(true)
  327. done()
  328. }
  329. )
  330. })
  331. it('set changed the email in newsletter', function(done) {
  332. this.UserUpdater.updateUser = sinon.stub().callsArgWith(2, null, { n: 1 })
  333. this.UserUpdater.setDefaultEmailAddress(
  334. this.stubbedUser._id,
  335. this.newEmail,
  336. err => {
  337. should.not.exist(err)
  338. this.NewsletterManager.changeEmail
  339. .calledWith(this.stubbedUser.email, this.newEmail)
  340. .should.equal(true)
  341. done()
  342. }
  343. )
  344. })
  345. it('handle error', function(done) {
  346. this.UserUpdater.updateUser = sinon
  347. .stub()
  348. .callsArgWith(2, new Error('nope'))
  349. this.UserUpdater.setDefaultEmailAddress(
  350. this.stubbedUser._id,
  351. this.newEmail,
  352. err => {
  353. should.exist(err)
  354. done()
  355. }
  356. )
  357. })
  358. it('handle missed update', function(done) {
  359. this.UserUpdater.updateUser = sinon.stub().callsArgWith(2, null, { n: 0 })
  360. this.UserUpdater.setDefaultEmailAddress(
  361. this.stubbedUser._id,
  362. this.newEmail,
  363. err => {
  364. should.exist(err)
  365. done()
  366. }
  367. )
  368. })
  369. it('validates email', function(done) {
  370. this.UserUpdater.setDefaultEmailAddress(
  371. this.stubbedUser._id,
  372. '.edu',
  373. err => {
  374. should.exist(err)
  375. done()
  376. }
  377. )
  378. })
  379. describe('when email not confirmed', () => {
  380. beforeEach(function() {
  381. this.stubbedUser.emails = [
  382. {
  383. email: this.newEmail,
  384. confirmedAt: null
  385. }
  386. ]
  387. this.UserGetter.getUser = sinon.stub().yields(null, this.stubbedUser)
  388. this.UserUpdater.updateUser = sinon.stub()
  389. this.NewsletterManager.changeEmail = sinon.stub()
  390. })
  391. it('should callback with error', function() {
  392. this.UserUpdater.setDefaultEmailAddress(
  393. this.stubbedUser._id,
  394. this.newEmail,
  395. this.callback
  396. )
  397. this.callback.firstCall.args[0].name.should.equal(
  398. 'UnconfirmedEmailError'
  399. )
  400. this.UserUpdater.updateUser.callCount.should.equal(0)
  401. this.NewsletterManager.changeEmail.callCount.should.equal(0)
  402. })
  403. })
  404. describe('when email does not belong to user', () => {
  405. beforeEach(function() {
  406. this.stubbedUser.emails = []
  407. this.UserGetter.getUser = sinon.stub().yields(null, this.stubbedUser)
  408. this.UserUpdater.updateUser = sinon.stub()
  409. this.NewsletterManager.changeEmail = sinon.stub()
  410. })
  411. it('should callback with error', function() {
  412. this.UserUpdater.setDefaultEmailAddress(
  413. this.stubbedUser._id,
  414. this.newEmail,
  415. this.callback
  416. )
  417. this.callback.firstCall.args[0].name.should.equal('Error')
  418. this.UserUpdater.updateUser.callCount.should.equal(0)
  419. this.NewsletterManager.changeEmail.callCount.should.equal(0)
  420. })
  421. })
  422. })
  423. describe('confirmEmail', function() {
  424. beforeEach(function() {
  425. return (this.UserUpdater.updateUser = sinon
  426. .stub()
  427. .callsArgWith(2, null, { n: 1 }))
  428. })
  429. it('should update the email record', function(done) {
  430. return this.UserUpdater.confirmEmail(
  431. this.stubbedUser._id,
  432. this.newEmail,
  433. err => {
  434. should.not.exist(err)
  435. this.UserUpdater.updateUser
  436. .calledWith(
  437. { _id: this.stubbedUser._id, 'emails.email': this.newEmail },
  438. { $set: { 'emails.$.confirmedAt': new Date() } }
  439. )
  440. .should.equal(true)
  441. return done()
  442. }
  443. )
  444. })
  445. it('add affiliation', function(done) {
  446. return this.UserUpdater.confirmEmail(
  447. this.stubbedUser._id,
  448. this.newEmail,
  449. err => {
  450. should.not.exist(err)
  451. this.addAffiliation.calledOnce.should.equal(true)
  452. sinon.assert.calledWith(
  453. this.addAffiliation,
  454. this.stubbedUser._id,
  455. this.newEmail,
  456. { confirmedAt: new Date() }
  457. )
  458. return done()
  459. }
  460. )
  461. })
  462. it('handle error', function(done) {
  463. this.UserUpdater.updateUser = sinon
  464. .stub()
  465. .callsArgWith(2, new Error('nope'))
  466. return this.UserUpdater.confirmEmail(
  467. this.stubbedUser._id,
  468. this.newEmail,
  469. err => {
  470. should.exist(err)
  471. return done()
  472. }
  473. )
  474. })
  475. it('handle missed update', function(done) {
  476. this.UserUpdater.updateUser = sinon.stub().callsArgWith(2, null, { n: 0 })
  477. return this.UserUpdater.confirmEmail(
  478. this.stubbedUser._id,
  479. this.newEmail,
  480. err => {
  481. should.exist(err)
  482. return done()
  483. }
  484. )
  485. })
  486. it('validates email', function(done) {
  487. return this.UserUpdater.confirmEmail(this.stubbedUser._id, '@', err => {
  488. should.exist(err)
  489. return done()
  490. })
  491. })
  492. it('handle affiliation error', function(done) {
  493. this.addAffiliation.callsArgWith(3, new Error('nope'))
  494. return this.UserUpdater.confirmEmail(
  495. this.stubbedUser._id,
  496. this.newEmail,
  497. err => {
  498. should.exist(err)
  499. this.UserUpdater.updateUser.called.should.equal(false)
  500. return done()
  501. }
  502. )
  503. })
  504. it('refresh features', function(done) {
  505. return this.UserUpdater.confirmEmail(
  506. this.stubbedUser._id,
  507. this.newEmail,
  508. err => {
  509. should.not.exist(err)
  510. sinon.assert.calledWith(this.refreshFeatures, this.stubbedUser._id)
  511. return done()
  512. }
  513. )
  514. })
  515. })
  516. })