From 258289e65aa44dd454183e9506df398f1e35a250 Mon Sep 17 00:00:00 2001 From: Antoine Clausse Date: Wed, 5 Jun 2024 12:24:20 +0200 Subject: [PATCH] [web] Promisify LaunchpadController (#18497) * Remove unused exports and decaffeinate comment * Update export format * Make `launchpadPage` async * Make `_atLeastOneAdminExists` async * Make `sendTestEmail` async * Make `registerExternalAuthAdmin` async * Make `registerAdmin` async * Update tests LaunchpadControllerTests.js * Use `expressify(...)` * Fix unit tests: export `LaunchpadController` * Fix unit tests by awaiting expressified functions (could include breaking changes!) * Call `.exec()` after `User.updateOne(...)` * Revert promisification of `expressify` * Revert some `return` removal in tests GitOrigin-RevId: c73a38bf1cf798830df046fe96d3516178655ea6 --- .../launchpad/app/src/LaunchpadController.js | 349 +++++++-------- .../test/unit/src/LaunchpadControllerTests.js | 412 +++++++++--------- 2 files changed, 362 insertions(+), 399 deletions(-) diff --git a/services/web/modules/launchpad/app/src/LaunchpadController.js b/services/web/modules/launchpad/app/src/LaunchpadController.js index a3b8a5b639..23e98a5ac8 100644 --- a/services/web/modules/launchpad/app/src/LaunchpadController.js +++ b/services/web/modules/launchpad/app/src/LaunchpadController.js @@ -1,25 +1,10 @@ -/* eslint-disable - n/handle-callback-err, - max-len, - no-unused-vars, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -let LaunchpadController const OError = require('@overleaf/o-error') +const { expressify } = require('@overleaf/promise-utils') const Settings = require('@overleaf/settings') const Path = require('path') -const Url = require('url') const logger = require('@overleaf/logger') -const metrics = require('@overleaf/metrics') const UserRegistrationHandler = require('../../../../app/src/Features/User/UserRegistrationHandler') const EmailHandler = require('../../../../app/src/Features/Email/EmailHandler') -const _ = require('lodash') const UserGetter = require('../../../../app/src/Features/User/UserGetter') const { User } = require('../../../../app/src/models/User') const AuthenticationManager = require('../../../../app/src/Features/Authentication/AuthenticationManager') @@ -29,7 +14,7 @@ const { hasAdminAccess, } = require('../../../../app/src/Features/Helpers/AdminAuthorizationHelper') -module.exports = LaunchpadController = { +const _LaunchpadController = { _getAuthMethod() { if (Settings.ldap) { return 'ldap' @@ -40,65 +25,47 @@ module.exports = LaunchpadController = { } }, - launchpadPage(req, res, next) { + async launchpadPage(req, res) { // TODO: check if we're using external auth? // * how does all this work with ldap and saml? const sessionUser = SessionManager.getSessionUser(req.session) const authMethod = LaunchpadController._getAuthMethod() - LaunchpadController._atLeastOneAdminExists(function (err, adminUserExists) { - if (err != null) { - return next(err) - } - if (!sessionUser) { - if (!adminUserExists) { - res.render(Path.resolve(__dirname, '../views/launchpad'), { - adminUserExists, - authMethod, - }) - } else { - AuthenticationController.setRedirectInSession(req) - res.redirect('/login') - } + const adminUserExists = await LaunchpadController._atLeastOneAdminExists() + if (!sessionUser) { + if (!adminUserExists) { + res.render(Path.resolve(__dirname, '../views/launchpad'), { + adminUserExists, + authMethod, + }) } else { - UserGetter.getUser( - sessionUser._id, - { isAdmin: 1 }, - function (err, user) { - if (err != null) { - return next(err) - } - if (hasAdminAccess(user)) { - res.render(Path.resolve(__dirname, '../views/launchpad'), { - wsUrl: Settings.wsUrl, - adminUserExists, - authMethod, - }) - } else { - res.redirect('/restricted') - } - } - ) + AuthenticationController.setRedirectInSession(req) + res.redirect('/login') + } + } else { + const user = await UserGetter.promises.getUser(sessionUser._id, { + isAdmin: 1, + }) + if (hasAdminAccess(user)) { + res.render(Path.resolve(__dirname, '../views/launchpad'), { + wsUrl: Settings.wsUrl, + adminUserExists, + authMethod, + }) + } else { + res.redirect('/restricted') } - }) - }, - - _atLeastOneAdminExists(callback) { - if (callback == null) { - callback = function () {} } - UserGetter.getUser( - { isAdmin: true }, - { _id: 1, isAdmin: 1 }, - function (err, user) { - if (err != null) { - return callback(err) - } - callback(null, user != null) - } - ) }, - sendTestEmail(req, res, next) { + async _atLeastOneAdminExists() { + const user = await UserGetter.promises.getUser( + { isAdmin: true }, + { _id: 1, isAdmin: 1 } + ) + return Boolean(user) + }, + + async sendTestEmail(req, res) { const { email } = req.body if (!email) { logger.debug({}, 'no email address supplied') @@ -108,20 +75,20 @@ module.exports = LaunchpadController = { } logger.debug({ email }, 'sending test email') const emailOptions = { to: email } - EmailHandler.sendEmail('testEmail', emailOptions, function (err) { - if (err != null) { - OError.tag(err, 'error sending test email', { - email, - }) - return next(err) - } + try { + await EmailHandler.promises.sendEmail('testEmail', emailOptions) logger.debug({ email }, 'sent test email') res.json({ message: res.locals.translate('email_sent') }) - }) + } catch (err) { + OError.tag(err, 'error sending test email', { + email, + }) + throw err + } }, registerExternalAuthAdmin(authMethod) { - return function (req, res, next) { + return expressify(async function (req, res) { if (LaunchpadController._getAuthMethod() !== authMethod) { logger.debug( { authMethod }, @@ -136,70 +103,65 @@ module.exports = LaunchpadController = { } logger.debug({ email }, 'attempted register first admin user') - LaunchpadController._atLeastOneAdminExists(function (err, exists) { - if (err != null) { - return next(err) - } - if (exists) { - logger.debug( - { email }, - 'already have at least one admin user, disallow' - ) - return res.sendStatus(403) - } + const exists = await LaunchpadController._atLeastOneAdminExists() - const body = { - email, - password: 'password_here', - first_name: email, - last_name: '', - } + if (exists) { logger.debug( - { body, authMethod }, - 'creating admin account for specified external-auth user' + { email }, + 'already have at least one admin user, disallow' ) + return res.sendStatus(403) + } - UserRegistrationHandler.registerNewUser(body, function (err, user) { - if (err != null) { - OError.tag(err, 'error with registerNewUser', { - email, - authMethod, - }) - return next(err) - } + const body = { + email, + password: 'password_here', + first_name: email, + last_name: '', + } + logger.debug( + { body, authMethod }, + 'creating admin account for specified external-auth user' + ) - // Ignore spurious floating promises warning until we promisify - // eslint-disable-next-line @typescript-eslint/no-floating-promises - User.updateOne( - { _id: user._id }, - { - $set: { isAdmin: true }, - emails: [{ email }], - }, - function (err) { - if (err != null) { - OError.tag(err, 'error setting user to admin', { - user_id: user._id, - }) - return next(err) - } - - AuthenticationController.setRedirectInSession(req, '/launchpad') - logger.debug( - { email, userId: user._id, authMethod }, - 'created first admin account' - ) - - res.json({ redir: '/launchpad', email }) - } - ) + let user + try { + user = await UserRegistrationHandler.promises.registerNewUser(body) + } catch (err) { + OError.tag(err, 'error with registerNewUser', { + email, + authMethod, }) - }) - } + throw err + } + + try { + await User.updateOne( + { _id: user._id }, + { + $set: { isAdmin: true }, + emails: [{ email }], + } + ).exec() + } catch (err) { + OError.tag(err, 'error setting user to admin', { + user_id: user._id, + }) + throw err + } + + AuthenticationController.setRedirectInSession(req, '/launchpad') + logger.debug( + { email, userId: user._id, authMethod }, + 'created first admin account' + ) + + res.json({ redir: '/launchpad', email }) + }) }, - registerAdmin(req, res, next) { + async registerAdmin(req, res) { const { email } = req.body const { password } = req.body if (!email || !password) { @@ -208,71 +170,70 @@ module.exports = LaunchpadController = { } logger.debug({ email }, 'attempted register first admin user') - LaunchpadController._atLeastOneAdminExists(function (err, exists) { - if (err != null) { - return next(err) - } + const exists = await LaunchpadController._atLeastOneAdminExists() - if (exists) { - logger.debug( - { email: req.body.email }, - 'already have at least one admin user, disallow' - ) - return res.status(403).json({ - message: { type: 'error', text: 'admin user already exists' }, - }) - } - - const invalidEmail = AuthenticationManager.validateEmail(email) - if (invalidEmail) { - return res - .status(400) - .json({ message: { type: 'error', text: invalidEmail.message } }) - } - - const invalidPassword = AuthenticationManager.validatePassword( - password, - email + if (exists) { + logger.debug( + { email: req.body.email }, + 'already have at least one admin user, disallow' ) - if (invalidPassword) { - return res - .status(400) - .json({ message: { type: 'error', text: invalidPassword.message } }) - } - - const body = { email, password } - UserRegistrationHandler.registerNewUser(body, function (err, user) { - if (err != null) { - return next(err) - } - - logger.debug({ userId: user._id }, 'making user an admin') - // Ignore spurious floating promises warning until we promisify - // eslint-disable-next-line @typescript-eslint/no-floating-promises - User.updateOne( - { _id: user._id }, - { - $set: { - isAdmin: true, - emails: [{ email }], - }, - }, - function (err) { - if (err != null) { - OError.tag(err, 'error setting user to admin', { - user_id: user._id, - }) - return next(err) - } - - logger.debug( - { email, userId: user._id }, - 'created first admin account' - ) - res.json({ redir: '/launchpad' }) - } - ) + return res.status(403).json({ + message: { type: 'error', text: 'admin user already exists' }, }) - }) + } + + const invalidEmail = AuthenticationManager.validateEmail(email) + if (invalidEmail) { + return res + .status(400) + .json({ message: { type: 'error', text: invalidEmail.message } }) + } + + const invalidPassword = AuthenticationManager.validatePassword( + password, + email + ) + if (invalidPassword) { + return res + .status(400) + .json({ message: { type: 'error', text: invalidPassword.message } }) + } + + const body = { email, password } + + const user = await UserRegistrationHandler.promises.registerNewUser(body) + + logger.debug({ userId: user._id }, 'making user an admin') + + try { + await User.updateOne( + { _id: user._id }, + { + $set: { + isAdmin: true, + emails: [{ email }], + }, + } + ).exec() + } catch (err) { + OError.tag(err, 'error setting user to admin', { + user_id: user._id, + }) + throw err + } + + logger.debug({ email, userId: user._id }, 'created first admin account') + res.json({ redir: '/launchpad' }) }, } + +const LaunchpadController = { + launchpadPage: expressify(_LaunchpadController.launchpadPage), + registerAdmin: expressify(_LaunchpadController.registerAdmin), + registerExternalAuthAdmin: _LaunchpadController.registerExternalAuthAdmin, + sendTestEmail: expressify(_LaunchpadController.sendTestEmail), + _atLeastOneAdminExists: _LaunchpadController._atLeastOneAdminExists, + _getAuthMethod: _LaunchpadController._getAuthMethod, +} + +module.exports = LaunchpadController diff --git a/services/web/modules/launchpad/test/unit/src/LaunchpadControllerTests.js b/services/web/modules/launchpad/test/unit/src/LaunchpadControllerTests.js index 88dadbbfe4..f03169d755 100644 --- a/services/web/modules/launchpad/test/unit/src/LaunchpadControllerTests.js +++ b/services/web/modules/launchpad/test/unit/src/LaunchpadControllerTests.js @@ -11,9 +11,9 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const SandboxedModule = require('sandboxed-module') -const assert = require('assert') const { expect } = require('chai') const sinon = require('sinon') +const MockResponse = require('../../../../../test/unit/src/helpers/MockResponse') const modulePath = require('path').join( __dirname, '../../../app/src/LaunchpadController.js' @@ -36,10 +36,14 @@ describe('LaunchpadController', function () { }), '@overleaf/metrics': (this.Metrics = {}), '../../../../app/src/Features/User/UserRegistrationHandler': - (this.UserRegistrationHandler = {}), + (this.UserRegistrationHandler = { + promises: {}, + }), '../../../../app/src/Features/Email/EmailHandler': (this.EmailHandler = - {}), - '../../../../app/src/Features/User/UserGetter': (this.UserGetter = {}), + { promises: {} }), + '../../../../app/src/Features/User/UserGetter': (this.UserGetter = { + promises: {}, + }), '../../../../app/src/models/User': { User: this.User }, '../../../../app/src/Features/Authentication/AuthenticationController': (this.AuthenticationController = {}), @@ -58,21 +62,12 @@ describe('LaunchpadController', function () { session: {}, } - this.res = { - render: sinon.stub(), - redirect: sinon.stub(), - json: sinon.stub(), - send: sinon.stub(), - sendStatus: sinon.stub(), - locals: { - translate(key) { - return key - }, + this.res = new MockResponse() + this.res.locals = { + translate(key) { + return key }, } - this.res.status = sinon.stub().returns(this.res) - - return (this.next = sinon.stub()) }) describe('launchpadPage', function () { @@ -91,12 +86,13 @@ describe('LaunchpadController', function () { describe('when the user is not logged in', function () { beforeEach(function () { this.SessionManager.getSessionUser = sinon.stub().returns(null) - return (this.res.render = sinon.stub()) }) describe('when there are no admins', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + beforeEach(function (done) { + this._atLeastOneAdminExists.resolves(false) + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.launchpadPage( this.req, this.res, @@ -120,8 +116,10 @@ describe('LaunchpadController', function () { }) describe('when there is at least one admin', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, true) + beforeEach(function (done) { + this._atLeastOneAdminExists.resolves(true) + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.launchpadPage( this.req, this.res, @@ -149,16 +147,16 @@ describe('LaunchpadController', function () { email: 'abcd@example.com', } this.SessionManager.getSessionUser = sinon.stub().returns(this.user) - this._atLeastOneAdminExists.callsArgWith(0, null, true) - this.res.render = sinon.stub() - return (this.res.redirect = sinon.stub()) + this._atLeastOneAdminExists.resolves(true) }) describe('when the user is an admin', function () { - beforeEach(function () { - this.UserGetter.getUser = sinon + beforeEach(function (done) { + this.UserGetter.promises.getUser = sinon .stub() - .callsArgWith(2, null, { isAdmin: true }) + .resolves({ isAdmin: true }) + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.launchpadPage( this.req, this.res, @@ -183,10 +181,12 @@ describe('LaunchpadController', function () { }) describe('when the user is not an admin', function () { - beforeEach(function () { - this.UserGetter.getUser = sinon + beforeEach(function (done) { + this.UserGetter.promises.getUser = sinon .stub() - .callsArgWith(2, null, { isAdmin: false }) + .resolves({ isAdmin: false }) + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.launchpadPage( this.req, this.res, @@ -205,75 +205,54 @@ describe('LaunchpadController', function () { describe('_atLeastOneAdminExists', function () { describe('when there are no admins', function () { beforeEach(function () { - return (this.UserGetter.getUser = sinon - .stub() - .callsArgWith(2, null, null)) + return (this.UserGetter.promises.getUser = sinon.stub().resolves(null)) }) - it('should callback with false', function (done) { - return this.LaunchpadController._atLeastOneAdminExists( - (err, exists) => { - expect(err).to.equal(null) - expect(exists).to.equal(false) - return done() - } - ) + it('should callback with false', async function () { + const exists = await this.LaunchpadController._atLeastOneAdminExists() + expect(exists).to.equal(false) }) }) describe('when there are some admins', function () { beforeEach(function () { - return (this.UserGetter.getUser = sinon + return (this.UserGetter.promises.getUser = sinon .stub() - .callsArgWith(2, null, { _id: 'abcd' })) + .resolves({ _id: 'abcd' })) }) - it('should callback with true', function (done) { - return this.LaunchpadController._atLeastOneAdminExists( - (err, exists) => { - expect(err).to.equal(null) - expect(exists).to.equal(true) - return done() - } - ) + it('should callback with true', async function () { + const exists = await this.LaunchpadController._atLeastOneAdminExists() + expect(exists).to.equal(true) }) }) describe('when getUser produces an error', function () { beforeEach(function () { - return (this.UserGetter.getUser = sinon + return (this.UserGetter.promises.getUser = sinon .stub() - .callsArgWith(2, new Error('woops'))) + .rejects(new Error('woops'))) }) - it('should produce an error', function (done) { - return this.LaunchpadController._atLeastOneAdminExists( - (err, exists) => { - expect(err).to.not.equal(null) - expect(err).to.be.instanceof(Error) - expect(exists).to.equal(undefined) - return done() - } - ) + it('should produce an error', async function () { + await expect(this.LaunchpadController._atLeastOneAdminExists()).rejected }) }) }) describe('sendTestEmail', function () { beforeEach(function () { - this.EmailHandler.sendEmail = sinon.stub().callsArgWith(2, null) + this.EmailHandler.promises.sendEmail = sinon.stub().resolves() this.req.body.email = 'someone@example.com' - this.res.sendStatus = sinon.stub() return (this.next = sinon.stub()) }) - it('should produce a 200 response', function () { + it('should produce a 200 response', function (done) { + this.res.callback = () => { + this.res.json.calledWith({ message: 'email_sent' }).should.equal(true) + done() + } this.LaunchpadController.sendTestEmail(this.req, this.res, this.next) - this.res.json - .calledWith({ - message: 'email_sent', - }) - .should.equal(true) }) it('should not call next with an error', function () { @@ -283,23 +262,26 @@ describe('LaunchpadController', function () { it('should have called sendEmail', function () { this.LaunchpadController.sendTestEmail(this.req, this.res, this.next) - this.EmailHandler.sendEmail.callCount.should.equal(1) - return this.EmailHandler.sendEmail + this.EmailHandler.promises.sendEmail.callCount.should.equal(1) + return this.EmailHandler.promises.sendEmail .calledWith('testEmail') .should.equal(true) }) describe('when sendEmail produces an error', function () { beforeEach(function () { - return (this.EmailHandler.sendEmail = sinon + return (this.EmailHandler.promises.sendEmail = sinon .stub() - .callsArgWith(2, new Error('woops'))) + .rejects(new Error('woops'))) }) - it('should call next with an error', function () { + it('should call next with an error', function (done) { + this.next = sinon.stub().callsFake(err => { + expect(err).to.be.instanceof(Error) + this.next.callCount.should.equal(1) + done() + }) this.LaunchpadController.sendTestEmail(this.req, this.res, this.next) - this.next.callCount.should.equal(1) - return expect(this.next.lastCall.args[0]).to.be.instanceof(Error) }) }) @@ -333,8 +315,8 @@ describe('LaunchpadController', function () { }) describe('when all goes well', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + beforeEach(function (done) { + this._atLeastOneAdminExists.resolves(false) this.email = 'someone@example.com' this.password = 'a_really_bad_password' this.req.body.email = this.email @@ -343,15 +325,17 @@ describe('LaunchpadController', function () { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon + this.UserRegistrationHandler.promises.registerNewUser = sinon .stub() - .callsArgWith(1, null, this.user) - this.User.updateOne = sinon.stub().callsArgWith(2, null) + .resolves(this.user) + this.User.updateOne = sinon + .stub() + .returns({ exec: sinon.stub().resolves() }) this.AuthenticationController.setRedirectInSession = sinon.stub() this.AuthenticationManager.validateEmail = sinon.stub().returns(null) this.AuthenticationManager.validatePassword = sinon.stub().returns(null) - this.res.json = sinon.stub() - this.next = sinon.stub() + this.next = sinon.stub().callsFake(() => done()) + this.res.callback = done return this.LaunchpadController.registerAdmin( this.req, this.res, @@ -369,8 +353,10 @@ describe('LaunchpadController', function () { }) it('should have called registerNewUser', function () { - this.UserRegistrationHandler.registerNewUser.callCount.should.equal(1) - return this.UserRegistrationHandler.registerNewUser + this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( + 1 + ) + return this.UserRegistrationHandler.promises.registerNewUser .calledWith({ email: this.email, password: this.password }) .should.equal(true) }) @@ -393,7 +379,7 @@ describe('LaunchpadController', function () { describe('when no email is supplied', function () { beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + this._atLeastOneAdminExists.resolves(false) this.email = undefined this.password = 'a_really_bad_password' this.req.body.email = this.email @@ -402,10 +388,9 @@ describe('LaunchpadController', function () { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon.stub() - this.User.updateOne = sinon.stub() + this.UserRegistrationHandler.promises.registerNewUser = sinon.stub() + this.User.updateOne = sinon.stub().returns({ exec: sinon.stub() }) this.AuthenticationController.setRedirectInSession = sinon.stub() - this.res.sendStatus = sinon.stub() this.next = sinon.stub() return this.LaunchpadController.registerAdmin( this.req, @@ -424,7 +409,7 @@ describe('LaunchpadController', function () { }) it('should not call registerNewUser', function () { - return this.UserRegistrationHandler.registerNewUser.callCount.should.equal( + return this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( 0 ) }) @@ -432,7 +417,7 @@ describe('LaunchpadController', function () { describe('when no password is supplied', function () { beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + this._atLeastOneAdminExists.resolves(false) this.email = 'someone@example.com' this.password = undefined this.req.body.email = this.email @@ -441,10 +426,9 @@ describe('LaunchpadController', function () { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon.stub() - this.User.updateOne = sinon.stub() + this.UserRegistrationHandler.promises.registerNewUser = sinon.stub() + this.User.updateOne = sinon.stub().returns({ exec: sinon.stub() }) this.AuthenticationController.setRedirectInSession = sinon.stub() - this.res.sendStatus = sinon.stub() this.next = sinon.stub() return this.LaunchpadController.registerAdmin( this.req, @@ -463,15 +447,15 @@ describe('LaunchpadController', function () { }) it('should not call registerNewUser', function () { - return this.UserRegistrationHandler.registerNewUser.callCount.should.equal( + return this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( 0 ) }) }) describe('when an invalid email is supplied', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + beforeEach(function (done) { + this._atLeastOneAdminExists.resolves(false) this.email = 'someone@example.com' this.password = 'invalid password' this.req.body.email = this.email @@ -480,15 +464,15 @@ describe('LaunchpadController', function () { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon.stub() - this.User.updateOne = sinon.stub() + this.UserRegistrationHandler.promises.registerNewUser = sinon.stub() + this.User.updateOne = sinon.stub().returns({ exec: sinon.stub() }) this.AuthenticationController.setRedirectInSession = sinon.stub() this.AuthenticationManager.validateEmail = sinon .stub() .returns(new Error('bad email')) this.AuthenticationManager.validatePassword = sinon.stub().returns(null) - this.res.sendStatus = sinon.stub() - this.next = sinon.stub() + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.registerAdmin( this.req, this.res, @@ -505,15 +489,15 @@ describe('LaunchpadController', function () { }) it('should not call registerNewUser', function () { - return this.UserRegistrationHandler.registerNewUser.callCount.should.equal( + return this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( 0 ) }) }) describe('when an invalid password is supplied', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + beforeEach(function (done) { + this._atLeastOneAdminExists.resolves(false) this.email = 'someone@example.com' this.password = 'invalid password' this.req.body.email = this.email @@ -522,15 +506,15 @@ describe('LaunchpadController', function () { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon.stub() - this.User.updateOne = sinon.stub() + this.UserRegistrationHandler.promises.registerNewUser = sinon.stub() + this.User.updateOne = sinon.stub().returns({ exec: sinon.stub() }) this.AuthenticationController.setRedirectInSession = sinon.stub() this.AuthenticationManager.validateEmail = sinon.stub().returns(null) this.AuthenticationManager.validatePassword = sinon .stub() .returns(new Error('bad password')) - this.res.sendStatus = sinon.stub() - this.next = sinon.stub() + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.registerAdmin( this.req, this.res, @@ -547,15 +531,15 @@ describe('LaunchpadController', function () { }) it('should not call registerNewUser', function () { - return this.UserRegistrationHandler.registerNewUser.callCount.should.equal( + return this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( 0 ) }) }) describe('when there are already existing admins', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, true) + beforeEach(function (done) { + this._atLeastOneAdminExists.resolves(true) this.email = 'someone@example.com' this.password = 'a_really_bad_password' this.req.body.email = this.email @@ -564,13 +548,13 @@ describe('LaunchpadController', function () { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon.stub() - this.User.updateOne = sinon.stub() + this.UserRegistrationHandler.promises.registerNewUser = sinon.stub() + this.User.updateOne = sinon.stub().returns({ exec: sinon.stub() }) this.AuthenticationController.setRedirectInSession = sinon.stub() this.AuthenticationManager.validateEmail = sinon.stub().returns(null) this.AuthenticationManager.validatePassword = sinon.stub().returns(null) - this.res.sendStatus = sinon.stub() - this.next = sinon.stub() + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.registerAdmin( this.req, this.res, @@ -584,15 +568,15 @@ describe('LaunchpadController', function () { }) it('should not call registerNewUser', function () { - return this.UserRegistrationHandler.registerNewUser.callCount.should.equal( + return this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( 0 ) }) }) describe('when checking admins produces an error', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, new Error('woops')) + beforeEach(function (done) { + this._atLeastOneAdminExists.rejects(new Error('woops')) this.email = 'someone@example.com' this.password = 'a_really_bad_password' this.req.body.email = this.email @@ -601,11 +585,11 @@ describe('LaunchpadController', function () { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon.stub() - this.User.updateOne = sinon.stub() + this.UserRegistrationHandler.promises.registerNewUser = sinon.stub() + this.User.updateOne = sinon.stub().returns({ exec: sinon.stub() }) this.AuthenticationController.setRedirectInSession = sinon.stub() - this.res.sendStatus = sinon.stub() - this.next = sinon.stub() + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.registerAdmin( this.req, this.res, @@ -623,15 +607,15 @@ describe('LaunchpadController', function () { }) it('should not call registerNewUser', function () { - return this.UserRegistrationHandler.registerNewUser.callCount.should.equal( + return this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( 0 ) }) }) describe('when registerNewUser produces an error', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + beforeEach(function (done) { + this._atLeastOneAdminExists.resolves(false) this.email = 'someone@example.com' this.password = 'a_really_bad_password' this.req.body.email = this.email @@ -640,15 +624,15 @@ describe('LaunchpadController', function () { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon + this.UserRegistrationHandler.promises.registerNewUser = sinon .stub() - .callsArgWith(1, new Error('woops')) - this.User.updateOne = sinon.stub() + .rejects(new Error('woops')) + this.User.updateOne = sinon.stub().returns({ exec: sinon.stub() }) this.AuthenticationController.setRedirectInSession = sinon.stub() this.AuthenticationManager.validateEmail = sinon.stub().returns(null) this.AuthenticationManager.validatePassword = sinon.stub().returns(null) - this.res.json = sinon.stub() - this.next = sinon.stub() + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.registerAdmin( this.req, this.res, @@ -666,8 +650,10 @@ describe('LaunchpadController', function () { }) it('should have called registerNewUser', function () { - this.UserRegistrationHandler.registerNewUser.callCount.should.equal(1) - return this.UserRegistrationHandler.registerNewUser + this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( + 1 + ) + return this.UserRegistrationHandler.promises.registerNewUser .calledWith({ email: this.email, password: this.password }) .should.equal(true) }) @@ -678,8 +664,8 @@ describe('LaunchpadController', function () { }) describe('when user update produces an error', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + beforeEach(function (done) { + this._atLeastOneAdminExists.resolves(false) this.email = 'someone@example.com' this.password = 'a_really_bad_password' this.req.body.email = this.email @@ -688,15 +674,17 @@ describe('LaunchpadController', function () { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon + this.UserRegistrationHandler.promises.registerNewUser = sinon .stub() - .callsArgWith(1, null, this.user) - this.User.updateOne = sinon.stub().callsArgWith(2, new Error('woops')) + .resolves(this.user) + this.User.updateOne = sinon.stub().returns({ + exec: sinon.stub().rejects(new Error('woops')), + }) this.AuthenticationController.setRedirectInSession = sinon.stub() this.AuthenticationManager.validateEmail = sinon.stub().returns(null) this.AuthenticationManager.validatePassword = sinon.stub().returns(null) - this.res.json = sinon.stub() - this.next = sinon.stub() + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.registerAdmin( this.req, this.res, @@ -714,18 +702,20 @@ describe('LaunchpadController', function () { }) it('should have called registerNewUser', function () { - this.UserRegistrationHandler.registerNewUser.callCount.should.equal(1) - return this.UserRegistrationHandler.registerNewUser + this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( + 1 + ) + return this.UserRegistrationHandler.promises.registerNewUser .calledWith({ email: this.email, password: this.password }) .should.equal(true) }) }) describe('when overleaf', function () { - beforeEach(function () { + beforeEach(function (done) { this.Settings.overleaf = { one: 1 } this.Settings.createV1AccountOnLogin = true - this._atLeastOneAdminExists.callsArgWith(0, null, false) + this._atLeastOneAdminExists.resolves(false) this.email = 'someone@example.com' this.password = 'a_really_bad_password' this.req.body.email = this.email @@ -734,18 +724,20 @@ describe('LaunchpadController', function () { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon + this.UserRegistrationHandler.promises.registerNewUser = sinon .stub() - .callsArgWith(1, null, this.user) - this.User.updateOne = sinon.stub().callsArgWith(2, null) + .resolves(this.user) + this.User.updateOne = sinon + .stub() + .returns({ exec: sinon.stub().resolves() }) this.AuthenticationController.setRedirectInSession = sinon.stub() this.AuthenticationManager.validateEmail = sinon.stub().returns(null) this.AuthenticationManager.validatePassword = sinon.stub().returns(null) - this.UserGetter.getUser = sinon + this.UserGetter.promises.getUser = sinon .stub() - .callsArgWith(1, null, { _id: '1234' }) - this.res.json = sinon.stub() - this.next = sinon.stub() + .resolves({ _id: '1234' }) + this.next = sinon.stub().callsFake(() => done()) + this.res.callback = done return this.LaunchpadController.registerAdmin( this.req, this.res, @@ -763,8 +755,10 @@ describe('LaunchpadController', function () { }) it('should have called registerNewUser', function () { - this.UserRegistrationHandler.registerNewUser.callCount.should.equal(1) - return this.UserRegistrationHandler.registerNewUser + this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( + 1 + ) + return this.UserRegistrationHandler.promises.registerNewUser .calledWith({ email: this.email, password: this.password }) .should.equal(true) }) @@ -799,21 +793,23 @@ describe('LaunchpadController', function () { }) describe('when all goes well', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + beforeEach(function (done) { + this._atLeastOneAdminExists.resolves(false) this.email = 'someone@example.com' this.req.body.email = this.email this.user = { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon + this.UserRegistrationHandler.promises.registerNewUser = sinon .stub() - .callsArgWith(1, null, this.user) - this.User.updateOne = sinon.stub().callsArgWith(2, null) + .resolves(this.user) + this.User.updateOne = sinon + .stub() + .returns({ exec: sinon.stub().resolves() }) this.AuthenticationController.setRedirectInSession = sinon.stub() - this.res.json = sinon.stub() - this.next = sinon.stub() + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.registerExternalAuthAdmin('ldap')( this.req, this.res, @@ -831,8 +827,10 @@ describe('LaunchpadController', function () { }) it('should have called registerNewUser', function () { - this.UserRegistrationHandler.registerNewUser.callCount.should.equal(1) - return this.UserRegistrationHandler.registerNewUser + this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( + 1 + ) + return this.UserRegistrationHandler.promises.registerNewUser .calledWith({ email: this.email, password: 'password_here', @@ -867,17 +865,16 @@ describe('LaunchpadController', function () { describe('when the authMethod is invalid', function () { beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + this._atLeastOneAdminExists.resolves(false) this.email = undefined this.req.body.email = this.email this.user = { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon.stub() - this.User.updateOne = sinon.stub() + this.UserRegistrationHandler.promises.registerNewUser = sinon.stub() + this.User.updateOne = sinon.stub().returns({ exec: sinon.stub() }) this.AuthenticationController.setRedirectInSession = sinon.stub() - this.res.sendStatus = sinon.stub() this.next = sinon.stub() return this.LaunchpadController.registerExternalAuthAdmin( 'NOTAVALIDAUTHMETHOD' @@ -894,7 +891,7 @@ describe('LaunchpadController', function () { }) it('should not call registerNewUser', function () { - return this.UserRegistrationHandler.registerNewUser.callCount.should.equal( + return this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( 0 ) }) @@ -902,17 +899,16 @@ describe('LaunchpadController', function () { describe('when no email is supplied', function () { beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + this._atLeastOneAdminExists.resolves(false) this.email = undefined this.req.body.email = this.email this.user = { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon.stub() - this.User.updateOne = sinon.stub() + this.UserRegistrationHandler.promises.registerNewUser = sinon.stub() + this.User.updateOne = sinon.stub().returns({ exec: sinon.stub() }) this.AuthenticationController.setRedirectInSession = sinon.stub() - this.res.sendStatus = sinon.stub() this.next = sinon.stub() return this.LaunchpadController.registerExternalAuthAdmin('ldap')( this.req, @@ -931,26 +927,26 @@ describe('LaunchpadController', function () { }) it('should not call registerNewUser', function () { - return this.UserRegistrationHandler.registerNewUser.callCount.should.equal( + return this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( 0 ) }) }) describe('when there are already existing admins', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, true) + beforeEach(function (done) { + this._atLeastOneAdminExists.resolves(true) this.email = 'someone@example.com' this.req.body.email = this.email this.user = { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon.stub() - this.User.updateOne = sinon.stub() + this.UserRegistrationHandler.promises.registerNewUser = sinon.stub() + this.User.updateOne = sinon.stub().returns({ exec: sinon.stub() }) this.AuthenticationController.setRedirectInSession = sinon.stub() - this.res.sendStatus = sinon.stub() - this.next = sinon.stub() + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.registerExternalAuthAdmin('ldap')( this.req, this.res, @@ -964,26 +960,26 @@ describe('LaunchpadController', function () { }) it('should not call registerNewUser', function () { - return this.UserRegistrationHandler.registerNewUser.callCount.should.equal( + return this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( 0 ) }) }) describe('when checking admins produces an error', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, new Error('woops')) + beforeEach(function (done) { + this._atLeastOneAdminExists.rejects(new Error('woops')) this.email = 'someone@example.com' this.req.body.email = this.email this.user = { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon.stub() - this.User.updateOne = sinon.stub() + this.UserRegistrationHandler.promises.registerNewUser = sinon.stub() + this.User.updateOne = sinon.stub().returns({ exec: sinon.stub() }) this.AuthenticationController.setRedirectInSession = sinon.stub() - this.res.sendStatus = sinon.stub() - this.next = sinon.stub() + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.registerExternalAuthAdmin('ldap')( this.req, this.res, @@ -1001,28 +997,28 @@ describe('LaunchpadController', function () { }) it('should not call registerNewUser', function () { - return this.UserRegistrationHandler.registerNewUser.callCount.should.equal( + return this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( 0 ) }) }) describe('when registerNewUser produces an error', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + beforeEach(function (done) { + this._atLeastOneAdminExists.resolves(false) this.email = 'someone@example.com' this.req.body.email = this.email this.user = { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon + this.UserRegistrationHandler.promises.registerNewUser = sinon .stub() - .callsArgWith(1, new Error('woops')) - this.User.updateOne = sinon.stub() + .rejects(new Error('woops')) + this.User.updateOne = sinon.stub().returns({ exec: sinon.stub() }) this.AuthenticationController.setRedirectInSession = sinon.stub() - this.res.json = sinon.stub() - this.next = sinon.stub() + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.registerExternalAuthAdmin('ldap')( this.req, this.res, @@ -1040,8 +1036,10 @@ describe('LaunchpadController', function () { }) it('should have called registerNewUser', function () { - this.UserRegistrationHandler.registerNewUser.callCount.should.equal(1) - return this.UserRegistrationHandler.registerNewUser + this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( + 1 + ) + return this.UserRegistrationHandler.promises.registerNewUser .calledWith({ email: this.email, password: 'password_here', @@ -1057,21 +1055,23 @@ describe('LaunchpadController', function () { }) describe('when user update produces an error', function () { - beforeEach(function () { - this._atLeastOneAdminExists.callsArgWith(0, null, false) + beforeEach(function (done) { + this._atLeastOneAdminExists.resolves(false) this.email = 'someone@example.com' this.req.body.email = this.email this.user = { _id: 'abcdef', email: this.email, } - this.UserRegistrationHandler.registerNewUser = sinon + this.UserRegistrationHandler.promises.registerNewUser = sinon .stub() - .callsArgWith(1, null, this.user) - this.User.updateOne = sinon.stub().callsArgWith(2, new Error('woops')) + .resolves(this.user) + this.User.updateOne = sinon.stub().returns({ + exec: sinon.stub().rejects(new Error('woops')), + }) this.AuthenticationController.setRedirectInSession = sinon.stub() - this.res.json = sinon.stub() - this.next = sinon.stub() + this.res.callback = done + this.next = sinon.stub().callsFake(() => done()) return this.LaunchpadController.registerExternalAuthAdmin('ldap')( this.req, this.res, @@ -1089,8 +1089,10 @@ describe('LaunchpadController', function () { }) it('should have called registerNewUser', function () { - this.UserRegistrationHandler.registerNewUser.callCount.should.equal(1) - return this.UserRegistrationHandler.registerNewUser + this.UserRegistrationHandler.promises.registerNewUser.callCount.should.equal( + 1 + ) + return this.UserRegistrationHandler.promises.registerNewUser .calledWith({ email: this.email, password: 'password_here',