UserRegistrationHandlerTests.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /* eslint-disable
  2. node/handle-callback-err,
  3. max-len,
  4. no-return-assign,
  5. no-unused-vars,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. const SandboxedModule = require('sandboxed-module')
  15. const assert = require('assert')
  16. const path = require('path')
  17. const modulePath = path.join(
  18. __dirname,
  19. '../../../../app/src/Features/User/UserRegistrationHandler'
  20. )
  21. const sinon = require('sinon')
  22. const { expect } = require('chai')
  23. const EmailHelper = require('../../../../app/src/Features/Helpers/EmailHelper')
  24. describe('UserRegistrationHandler', function () {
  25. beforeEach(function () {
  26. this.user = { _id: (this.user_id = '31j2lk21kjl') }
  27. this.User = { updateOne: sinon.stub().callsArgWith(2) }
  28. this.UserGetter = { getUserByAnyEmail: sinon.stub() }
  29. this.UserCreator = {
  30. createNewUser: sinon.stub().callsArgWith(2, null, this.user),
  31. }
  32. this.AuthenticationManager = {
  33. validateEmail: sinon.stub().returns(null),
  34. validatePassword: sinon.stub().returns(null),
  35. setUserPassword: sinon.stub().callsArgWith(2),
  36. }
  37. this.NewsLetterManager = { subscribe: sinon.stub().callsArgWith(1) }
  38. this.EmailHandler = { sendEmail: sinon.stub().callsArgWith(2) }
  39. this.OneTimeTokenHandler = { getNewToken: sinon.stub() }
  40. this.handler = SandboxedModule.require(modulePath, {
  41. requires: {
  42. '../../models/User': { User: this.User },
  43. './UserGetter': this.UserGetter,
  44. './UserCreator': this.UserCreator,
  45. '../Authentication/AuthenticationManager': this.AuthenticationManager,
  46. '../Newsletter/NewsletterManager': this.NewsLetterManager,
  47. crypto: (this.crypto = {}),
  48. '../Email/EmailHandler': this.EmailHandler,
  49. '../Security/OneTimeTokenHandler': this.OneTimeTokenHandler,
  50. '../Analytics/AnalyticsManager': (this.AnalyticsManager = {
  51. recordEvent: sinon.stub(),
  52. }),
  53. '@overleaf/settings': (this.settings = {
  54. siteUrl: 'http://sl.example.com',
  55. }),
  56. '../Helpers/EmailHelper': EmailHelper,
  57. },
  58. })
  59. return (this.passingRequest = {
  60. email: 'something@email.com',
  61. password: '123',
  62. })
  63. })
  64. describe('validate Register Request', function () {
  65. it('allows passing validation through', function () {
  66. const result = this.handler._registrationRequestIsValid(
  67. this.passingRequest
  68. )
  69. return result.should.equal(true)
  70. })
  71. describe('failing email validation', function () {
  72. beforeEach(function () {
  73. return this.AuthenticationManager.validateEmail.returns({
  74. message: 'email not set',
  75. })
  76. })
  77. it('does not allow through', function () {
  78. const result = this.handler._registrationRequestIsValid(
  79. this.passingRequest
  80. )
  81. return result.should.equal(false)
  82. })
  83. })
  84. describe('failing password validation', function () {
  85. beforeEach(function () {
  86. return this.AuthenticationManager.validatePassword.returns({
  87. message: 'password is too short',
  88. })
  89. })
  90. it('does not allow through', function () {
  91. const result = this.handler._registrationRequestIsValid(
  92. this.passingRequest
  93. )
  94. return result.should.equal(false)
  95. })
  96. })
  97. })
  98. describe('registerNewUser', function () {
  99. describe('holdingAccount', function (done) {
  100. beforeEach(function () {
  101. this.user.holdingAccount = true
  102. this.handler._registrationRequestIsValid = sinon.stub().returns(true)
  103. return this.UserGetter.getUserByAnyEmail.callsArgWith(
  104. 1,
  105. null,
  106. this.user
  107. )
  108. })
  109. it('should not create a new user if there is a holding account there', function (done) {
  110. return this.handler.registerNewUser(this.passingRequest, err => {
  111. this.UserCreator.createNewUser.called.should.equal(false)
  112. return done()
  113. })
  114. })
  115. it('should set holding account to false', function (done) {
  116. return this.handler.registerNewUser(this.passingRequest, err => {
  117. const update = this.User.updateOne.args[0]
  118. assert.deepEqual(update[0], { _id: this.user._id })
  119. assert.deepEqual(update[1], { $set: { holdingAccount: false } })
  120. return done()
  121. })
  122. })
  123. })
  124. describe('invalidRequest', function () {
  125. it('should not create a new user if the the request is not valid', function (done) {
  126. this.handler._registrationRequestIsValid = sinon.stub().returns(false)
  127. return this.handler.registerNewUser(this.passingRequest, err => {
  128. expect(err).to.exist
  129. this.UserCreator.createNewUser.called.should.equal(false)
  130. return done()
  131. })
  132. })
  133. it('should return email registered in the error if there is a non holdingAccount there', function (done) {
  134. this.UserGetter.getUserByAnyEmail.callsArgWith(
  135. 1,
  136. null,
  137. (this.user = { holdingAccount: false })
  138. )
  139. return this.handler.registerNewUser(
  140. this.passingRequest,
  141. (err, user) => {
  142. expect(err).to.be.instanceOf(Error)
  143. expect(err).to.have.property('message', 'EmailAlreadyRegistered')
  144. user.should.deep.equal(this.user)
  145. return done()
  146. }
  147. )
  148. })
  149. })
  150. describe('validRequest', function () {
  151. beforeEach(function () {
  152. this.handler._registrationRequestIsValid = sinon.stub().returns(true)
  153. return this.UserGetter.getUserByAnyEmail.callsArgWith(1)
  154. })
  155. it('should create a new user', function (done) {
  156. return this.handler.registerNewUser(this.passingRequest, err => {
  157. this.UserCreator.createNewUser
  158. .calledWith({
  159. email: this.passingRequest.email,
  160. holdingAccount: false,
  161. first_name: this.passingRequest.first_name,
  162. last_name: this.passingRequest.last_name,
  163. })
  164. .should.equal(true)
  165. return done()
  166. })
  167. })
  168. it('lower case email', function (done) {
  169. this.passingRequest.email = 'soMe@eMail.cOm'
  170. return this.handler.registerNewUser(this.passingRequest, err => {
  171. this.UserCreator.createNewUser.args[0][0].email.should.equal(
  172. 'some@email.com'
  173. )
  174. return done()
  175. })
  176. })
  177. it('trim white space from email', function (done) {
  178. this.passingRequest.email = ' some@email.com '
  179. return this.handler.registerNewUser(this.passingRequest, err => {
  180. this.UserCreator.createNewUser.args[0][0].email.should.equal(
  181. 'some@email.com'
  182. )
  183. return done()
  184. })
  185. })
  186. it('should set the password', function (done) {
  187. return this.handler.registerNewUser(this.passingRequest, err => {
  188. this.AuthenticationManager.setUserPassword
  189. .calledWith(this.user, this.passingRequest.password)
  190. .should.equal(true)
  191. return done()
  192. })
  193. })
  194. it('should add the user to the newsletter if accepted terms', function (done) {
  195. this.passingRequest.subscribeToNewsletter = 'true'
  196. return this.handler.registerNewUser(this.passingRequest, err => {
  197. this.NewsLetterManager.subscribe
  198. .calledWith(this.user)
  199. .should.equal(true)
  200. return done()
  201. })
  202. })
  203. it('should not add the user to the newsletter if not accepted terms', function (done) {
  204. return this.handler.registerNewUser(this.passingRequest, err => {
  205. this.NewsLetterManager.subscribe
  206. .calledWith(this.user)
  207. .should.equal(false)
  208. return done()
  209. })
  210. })
  211. it('should track the registration event', function (done) {
  212. return this.handler.registerNewUser(this.passingRequest, err => {
  213. this.AnalyticsManager.recordEvent
  214. .calledWith(this.user._id, 'user-registered')
  215. .should.equal(true)
  216. return done()
  217. })
  218. })
  219. })
  220. it('should call the ReferalAllocator', function (done) {
  221. return done()
  222. })
  223. })
  224. describe('registerNewUserAndSendActivationEmail', function () {
  225. beforeEach(function () {
  226. this.email = 'Email@example.com'
  227. this.crypto.randomBytes = sinon.stub().returns({
  228. toString: () => {
  229. return (this.password = 'mock-password')
  230. },
  231. })
  232. this.OneTimeTokenHandler.getNewToken.yields(
  233. null,
  234. (this.token = 'mock-token')
  235. )
  236. this.handler.registerNewUser = sinon.stub()
  237. return (this.callback = sinon.stub())
  238. })
  239. describe('with a new user', function () {
  240. beforeEach(function () {
  241. this.user.email = this.email.toLowerCase()
  242. this.handler.registerNewUser.callsArgWith(1, null, this.user)
  243. return this.handler.registerNewUserAndSendActivationEmail(
  244. this.email,
  245. this.callback
  246. )
  247. })
  248. it('should ask the UserRegistrationHandler to register user', function () {
  249. return this.handler.registerNewUser
  250. .calledWith({
  251. email: this.email,
  252. password: this.password,
  253. })
  254. .should.equal(true)
  255. })
  256. it('should generate a new password reset token', function () {
  257. const data = {
  258. user_id: this.user._id.toString(),
  259. email: this.user.email,
  260. }
  261. return this.OneTimeTokenHandler.getNewToken
  262. .calledWith('password', data, { expiresIn: 7 * 24 * 60 * 60 })
  263. .should.equal(true)
  264. })
  265. it('should send a registered email', function () {
  266. return this.EmailHandler.sendEmail
  267. .calledWith('registered', {
  268. to: this.user.email,
  269. setNewPasswordUrl: `${this.settings.siteUrl}/user/activate?token=${this.token}&user_id=${this.user_id}`,
  270. })
  271. .should.equal(true)
  272. })
  273. it('should return the user', function () {
  274. return this.callback
  275. .calledWith(
  276. null,
  277. this.user,
  278. `${this.settings.siteUrl}/user/activate?token=${this.token}&user_id=${this.user_id}`
  279. )
  280. .should.equal(true)
  281. })
  282. })
  283. describe('with a user that already exists', function () {
  284. beforeEach(function () {
  285. this.handler.registerNewUser.callsArgWith(
  286. 1,
  287. new Error('EmailAlreadyRegistered'),
  288. this.user
  289. )
  290. return this.handler.registerNewUserAndSendActivationEmail(
  291. this.email,
  292. this.callback
  293. )
  294. })
  295. it('should still generate a new password token and email', function () {
  296. this.OneTimeTokenHandler.getNewToken.called.should.equal(true)
  297. return this.EmailHandler.sendEmail.called.should.equal(true)
  298. })
  299. })
  300. })
  301. })