diff --git a/bin/server.js b/bin/server.js index 4e1db9b..996bc9e 100644 --- a/bin/server.js +++ b/bin/server.js @@ -12,7 +12,10 @@ const cors = require('kcors') // Local libraries const config = require('../config') // this first. -const adminLib = require('../src/lib/admin') + +const AdminLib = require('../src/lib/admin') +const adminLib = new AdminLib() + const errorMiddleware = require('../src/middleware') const wlogger = require('../src/lib/wlogger') diff --git a/src/lib/admin.js b/src/lib/admin.js index 7c8f3e7..0b6578f 100644 --- a/src/lib/admin.js +++ b/src/lib/admin.js @@ -23,125 +23,137 @@ const JSON_PATH = `${__dirname}/../../config/${JSON_FILE}` const LOCALHOST = `http://localhost:${config.port}` const context = {} -// Create the first user in the system. A 'admin' level system user that is -// used by the Listing Manager and test scripts, in order access private API -// functions. -async function createSystemUser () { - // Create the system user. - try { - context.password = _randomString(20) +let _this +class Admin { + constructor () { + this.axios = axios + this.User = User + this.config = config + this.jsonFiles = jsonFiles + this.context = context - const options = { - method: 'POST', - url: `${LOCALHOST}/users`, - data: { - user: { - email: 'system@system.com', - password: context.password + _this = this + } + + // Create the first user in the system. A 'admin' level system user that is + // used by the Listing Manager and test scripts, in order access private API + // functions. + async createSystemUser () { + // Create the system user. + try { + context.password = _this._randomString(20) + + const options = { + method: 'POST', + url: `${LOCALHOST}/users`, + data: { + user: { + email: 'system@system.com', + password: context.password + } } } - } - const result = await axios(options) + const result = await _this.axios.request(options) + context.email = result.data.user.email + context.id = result.data.user._id + context.token = result.data.token - context.email = result.data.user.email - context.id = result.data.user._id - context.token = result.data.token + // Get the mongoDB entry + const user = await _this.User.findById(context.id) - // Get the mongoDB entry - const user = await User.findById(context.id) + // Change the user type to admin + user.type = 'admin' + // console.log(`user created: ${JSON.stringify(user, null, 2)}`) - // Change the user type to admin - user.type = 'admin' - // console.log(`user: ${JSON.stringify(user, null, 2)}`) + // Save the user model. + await user.save() - // Save the user model. - await user.save() + // console.log(`admin user created: ${JSON.stringify(result.body, null, 2)}`) + // console.log(`with password: ${context.password}`) - // console.log(`admin user created: ${JSON.stringify(result.body, null, 2)}`) - // console.log(`with password: ${context.password}`) + // Write out the system user information to a JSON file that external + // applications like the Task Manager and the test scripts can access. - // Write out the system user information to a JSON file that external - // applications like the Task Manager and the test scripts can access. - await jsonFiles.writeJSON(context, JSON_PATH) + await jsonFiles.writeJSON(context, JSON_PATH) - return context - } catch (err) { - // Handle existing system user. - if (err.response.status === 422) { - try { + return context + } catch (err) { + // Handle existing system user. + if (err.response.status === 422) { + try { // Delete the existing user - await deleteExistingSystemUser() + await _this.deleteExistingSystemUser() - // Call this function again. - return createSystemUser() - } catch (err2) { - console.error('Error in admin.js/createSystemUser() while trying generate new system user.') + // Call this function again. + return _this.createSystemUser() + } catch (err2) { + console.error('Error in admin.js/createSystemUser() while trying generate new system user.') + // process.end(1) + throw err2 + } + } else { + console.log('Error in admin.js/createSystemUser: ') // process.end(1) - throw err2 + throw err } - } else { - console.log('Error in admin.js/createSystemUser: ' + JSON.stringify(err, null, 2)) - // process.end(1) + } + } + + async deleteExistingSystemUser () { + try { + mongoose.Promise = global.Promise + mongoose.set('useCreateIndex', true) // Stop deprecation warning. + + await mongoose.connect(config.database, { useNewUrlParser: true, useUnifiedTopology: true }) + + await _this.User.deleteOne({ email: 'system@system.com' }) + } catch (err) { + console.log('Error in admin.js/deleteExistingSystemUser()') throw err } } -} -async function deleteExistingSystemUser () { - try { - mongoose.Promise = global.Promise - mongoose.set('useCreateIndex', true) // Stop deprecation warning. + async loginAdmin () { + // console.log(`loginAdmin() running.`) + let existingUser - await mongoose.connect(config.database, { useNewUrlParser: true, useUnifiedTopology: true }) + try { + // Read the exising file + existingUser = await _this.jsonFiles.readJSON(JSON_PATH) + // console.log(`existingUser: ${JSON.stringify(existingUser, null, 2)}`) - await User.deleteOne({ email: 'system@system.com' }) - } catch (err) { - console.log('Error in admin.js/deleteExistingSystemUser()') - throw err - } -} - -async function loginAdmin () { - // console.log(`loginAdmin() running.`) - let existingUser - - try { - // Read the exising file - existingUser = await jsonFiles.readJSON(JSON_PATH) - // console.log(`existingUser: ${JSON.stringify(existingUser, null, 2)}`) - - // Log in as the user. - const options = { - method: 'POST', - url: `${LOCALHOST}/auth`, - data: { - email: 'system@system.com', - password: existingUser.password + // Log in as the user. + const options = { + method: 'POST', + url: `${LOCALHOST}/auth`, + headers: { + Accept: 'application/json' + }, + data: { + email: 'system@system.com', + password: existingUser.password + } } + const result = await _this.axios.request(options) + // console.log(`result1: ${JSON.stringify(result, null, 2)}`) + return result + } catch (err) { + console.error('Error in admin.js/loginAdmin().') + + // console.error(`existingUser: ${JSON.stringify(existingUser, null, 2)}`) + + throw err } - const result = await axios(options) - // console.log(`result1: ${JSON.stringify(result, null, 2)}`) + } - return result - } catch (err) { - console.error('Error in admin.js/loginAdmin().') - - // console.error(`existingUser: ${JSON.stringify(existingUser, null, 2)}`) - - throw err + _randomString (length) { + var text = '' + var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + for (var i = 0; i < length; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)) + } + return text } } -function _randomString (length) { - var text = '' - var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' - for (var i = 0; i < length; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)) - } - return text -} - -module.exports = { - createSystemUser, - loginAdmin -} +module.exports = Admin diff --git a/test/a05-passport.spec.js b/test/a05-passport.spec.js index 095d63b..879d66c 100644 --- a/test/a05-passport.spec.js +++ b/test/a05-passport.spec.js @@ -1,17 +1,24 @@ const assert = require('chai').assert const PassportLib = require('../src/lib/passport') -describe('#passport.js', () => { - let passportLib +const sinon = require('sinon') - beforeEach(async () => { - passportLib = new PassportLib() +let uut +let sandbox + +describe('#passport.js', () => { + beforeEach(() => { + uut = new PassportLib() + + sandbox = sinon.createSandbox() }) + afterEach(() => sandbox.restore()) + describe('authUser()', () => { it('should throw error if ctx is not provided', async () => { try { - await passportLib.authUser() + await uut.authUser() assert(false, 'Unexpected result') } catch (err) { assert.include(err.message, 'ctx is required') @@ -20,17 +27,16 @@ describe('#passport.js', () => { it('Should throw error if the passport library fails', async () => { try { - // This is a mock to handle the callback error - // when the passport library fails or throws error const error = new Error('cant auth user') const user = null - const authMock = (value, callback) => { - callback(error, user) - } - passportLib.passport.authenticate = authMock + + // Mock calls + // https://sinonjs.org/releases/latest/stubs/ + // About yields + sandbox.stub(uut.passport, 'authenticate').yields(error, user) const ctx = {} - await passportLib.authUser(ctx) + await uut.authUser(ctx) assert(false, 'Unexpected result') } catch (err) { assert.include(err.message, 'cant auth user') diff --git a/test/a09-admin.spec.js b/test/a09-admin.spec.js new file mode 100644 index 0000000..ceb1c96 --- /dev/null +++ b/test/a09-admin.spec.js @@ -0,0 +1,116 @@ +const assert = require('chai').assert + +const Admin = require('../src/lib/admin') + +const sinon = require('sinon') + +const util = require('util') +util.inspect.defaultOptions = { depth: 1 } + +let sandbox +let uut +describe('Admin', () => { + beforeEach(() => { + uut = new Admin() + + sandbox = sinon.createSandbox() + }) + + afterEach(() => sandbox.restore()) + describe('loginAdmin()', () => { + it('should logind admin', async () => { + try { + const error = new Error('test error') + error.response = { + status: 422 + } + // sandbox.stub(uut.axios, 'request').onFirstCall().throws(error) + + const result = await uut.loginAdmin() + const user = result.data.user + + assert.property(user, '_id') + assert.property(user, 'email') + assert.property(user, 'type') + + assert.isString(user._id) + assert.isString(user.email) + assert.isString(user.type) + + assert.equal(user.type, 'admin') + } catch (err) { + assert(false, 'Unexpected result') + } + }) + it('should handle axios error', async () => { + try { + // Returns an erroneous password to force + // an auth error + sandbox + .stub(uut.jsonFiles, 'readJSON').resolves({ password: 'wrong' }) + + await uut.loginAdmin() + assert(false, 'Unexpected result') + } catch (err) { + assert.equal(err.response.status, 401) + assert.include(err.response.data, 'Unauthorized') + } + }) + }) + describe('createSystemUser()', () => { + it('should create admin', async () => { + try { + const result = await uut.createSystemUser() + + assert.property(result, 'email') + assert.property(result, 'password') + assert.property(result, 'id') + assert.property(result, 'token') + } catch (err) { + assert(false, 'Unexpected result') + } + }) + it('should handle axios error', async () => { + try { + const error1 = new Error('test error') + error1.response = { + status: 422 + } + const error2 = new Error('test error') + error1.response = { + status: 500 + } + // The loginAdmin() function in some use cases is recursive + // after handling the 422 error, it gets called again + sandbox + .stub(uut.axios, 'request') + .onFirstCall() + .throws(error1) + .onSecondCall() + .throws(error2) + + await uut.createSystemUser() + assert(false, 'Unexpected result') + } catch (err) { + assert.include(err.message, 'test error') + } + }) + it('should handle errors when remove user', async () => { + try { + const error1 = new Error('test error') + error1.response = { + status: 422 + } + sandbox + .stub(uut.axios, 'request').throws(error1) + sandbox + .stub(uut.User, 'deleteOne').throws(new Error('test error')) + + await uut.createSystemUser() + assert(false, 'Unexpected result') + } catch (err) { + assert.include(err.message, 'test error') + } + }) + }) +})