diff --git a/package.json b/package.json index cb9dee1..123b04a 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,11 @@ "type": "module", "scripts": { "start": "node index.js", - "test": "npm run test:all", + "test": "npm run test:unit", "test:all": "export SVC_ENV=test && c8 --reporter=text mocha --exit --timeout 15000 --recursive test/unit test/e2e/automated/", "test:unit": "export SVC_ENV=test && c8 --reporter=text mocha --exit --timeout 15000 --recursive test/unit/", "test:e2e:auto": "export SVC_ENV=test && mocha --exit --timeout 15000 test/e2e/automated/", + "start:e2e:server": "export SVC_ENV=test && node index.js", "test:temp": "export SVC_ENV=test && mocha --exit --timeout 15000 -g '#rate-limit' test/unit/json-rpc/", "lint": "standard --env mocha --fix", "docs": "./node_modules/.bin/apidoc -i src/ -o docs", diff --git a/test/e2e/automated/a00-liveness.rest-e2e.js b/test/e2e/automated/a00-liveness.rest-e2e.js new file mode 100644 index 0000000..6401503 --- /dev/null +++ b/test/e2e/automated/a00-liveness.rest-e2e.js @@ -0,0 +1,45 @@ +/* + Liveness test runs before all other tests to ensure the server is running. +*/ + +// Public npm libraries +import { assert } from 'chai' +import axios from 'axios' +import testUtils from '../../utils/test-utils.js' + +// const sinon = require('sinon') + +// Local support libraries +import config from '../../../config/index.js' +// import Server from '../../../bin/server.js' +// import testUtils from '../../utils/test-utils.js' +// const adminLib = new AdminLib() + +const LOCALHOST = `http://localhost:${config.port}` + +describe('#Check Server Liveness', () => { + // before(async () => { + it('should confirm the server is running', async () => { + try { + const response = await axios.get(`${LOCALHOST}/`) + assert(response.status === 200, 'Server is running, continuing with E2E tests.') + } catch (err) { + console.log('\nServer is not running, exiting tests.') + console.log('Start the server with `npm run start:e2e:server` before running E2E tests.\n') + console.log('Ensure running npm run docs before running the test server') + process.exit(1) + } + }) + it('should confirm the server is running over test enviroment', async () => { + try { + const res = await testUtils.loginAdminUser() + assert.property(res, 'user') + assert.property(res, 'token') + assert.property(res, 'id') + } catch (err) { + console.log('\nServer is not running over test enviroment, exiting tests.') + console.log('Start the server with `npm run start:e2e:server` before running E2E tests.\n') + process.exit(1) + } + }) +}) diff --git a/test/e2e/automated/a01-auth.rest-e2e.js b/test/e2e/automated/a01-auth.rest-e2e.js index d6d21d0..88f3f22 100644 --- a/test/e2e/automated/a01-auth.rest-e2e.js +++ b/test/e2e/automated/a01-auth.rest-e2e.js @@ -13,10 +13,8 @@ import axios from 'axios' // Local support libraries import config from '../../../config/index.js' -import Server from '../../../bin/server.js' +// import Server from '../../../bin/server.js' import testUtils from '../../utils/test-utils.js' -import AdminLib from '../../../src/adapters/admin.js' -const adminLib = new AdminLib() // const request = supertest.agent(app.listen()) const context = {} @@ -26,10 +24,12 @@ const LOCALHOST = `http://localhost:${config.port}` if (!config.noMongo) { describe('Auth', () => { before(async () => { - const app = new Server() + // const app = new Server() // This should be the first instruction. It starts the REST API server. - await app.startServer() + // await app.startServer() + + // TODO: // Stop the IPFS node for the rest of the e2e tests. // await app.controllers.adapters.ipfs.stop() @@ -37,9 +37,6 @@ if (!config.noMongo) { // Delete all previous users in the database. await testUtils.deleteAllUsers() - // Create a new admin user. - await adminLib.createSystemUser() - const userObj = { email: 'test@test.com', password: 'pass', diff --git a/test/e2e/automated/a02-users.rest-e2e.js b/test/e2e/automated/a02-users.rest-e2e.js index 2af1eb1..7ce8ac5 100644 --- a/test/e2e/automated/a02-users.rest-e2e.js +++ b/test/e2e/automated/a02-users.rest-e2e.js @@ -5,16 +5,11 @@ import axios from 'axios' import sinon from 'sinon' import util from 'util' -import UserController from '../../../src/controllers/rest-api/users/controller.js' -import Adapters from '../../../src/adapters/index.js' -import UseCases from '../../../src/use-cases/index.js' util.inspect.defaultOptions = { depth: 1 } const LOCALHOST = `http://localhost:${config.port}` const context = {} -const adapters = new Adapters() -let uut let sandbox // const mockContext = require('../../unit/mocks/ctx-mock').context @@ -50,9 +45,6 @@ if (!config.noMongo) { }) beforeEach(() => { - const useCases = new UseCases({ adapters }) - uut = new UserController({ adapters, useCases }) - sandbox = sinon.createSandbox() }) @@ -174,59 +166,6 @@ if (!config.noMongo) { assert.property(result.data, 'token', 'Token property exists.') assert.equal(result.data.user.type, 'user') }) - it('should reject signup when DISABLE_NEW_ACCOUNTS is true', async () => { - try { - process.env.DISABLE_NEW_ACCOUNTS = true - const options = { - method: 'POST', - url: `${LOCALHOST}/users`, - data: { - email: 'test2@test.com', - password: 'supersecretpassword', - name: 'test3' - } - } - - await axios(options) - - assert(false, 'Unexpected result') - } catch (err) { - assert(err.response.status === 401, 'Error code 401 expected.') - } - }) - it('admin can create a user when DISABLE_NEW_ACCOUNTS is true', async () => { - process.env.DISABLE_NEW_ACCOUNTS = true - const options = { - method: 'post', - url: `${LOCALHOST}/users`, - headers: { - Authorization: `Bearer ${context.adminJWT}` - }, - data: { - user: { - email: 'fromAdmin@test.com', - password: 'supersecretpassword', - name: 'test3' - } - } - } - const result = await axios(options) - - context.user = result.data.user - context.token = result.data.token - - assert(result.status === 200, 'Status Code 200 expected.') - assert( - result.data.user.email === 'fromAdmin@test.com', - 'Email of test expected' - ) - assert( - result.data.user.password === undefined, - 'Password expected to be omited' - ) - assert.property(result.data, 'token', 'Token property exists.') - assert.equal(result.data.user.type, 'user') - }) }) describe('GET /users', () => { @@ -321,33 +260,6 @@ if (!config.noMongo) { assert.hasAnyKeys(users[0], ['type', '_id', 'email']) assert.isNumber(users.length) }) - - it('should return a 422 http status if biz-logic throws an error', async () => { - try { - const { token } = context - - // Force an error - sandbox - .stub(uut.useCases.user, 'getAllUsers') - .rejects(new Error('test error')) - - const options = { - method: 'GET', - url: `${LOCALHOST}/users`, - headers: { - Accept: 'application/json', - Authorization: `Bearer ${token}` - } - } - await axios(options) - - assert.fail('Unexpected code path!') - } catch (err) { - // console.log(err) - assert.equal(err.response.status, 422) - assert.equal(err.response.data, 'test error') - } - }) }) describe('GET /users/:id', () => { diff --git a/test/e2e/automated/a09-admin.rest-e2e.js b/test/unit/adapters/admin.adapter.unit.js similarity index 79% rename from test/e2e/automated/a09-admin.rest-e2e.js rename to test/unit/adapters/admin.adapter.unit.js index 6c43def..796b2e3 100644 --- a/test/e2e/automated/a09-admin.rest-e2e.js +++ b/test/unit/adapters/admin.adapter.unit.js @@ -19,42 +19,28 @@ describe('Admin', () => { if (!config.noMongo) { 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) + // it('should login admin', async () => { + // try { + // sandbox.stub(uut.axios, 'request').resolves(true) - 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') - } - }) + // const result = await uut.loginAdmin() + // assert.isTrue(result) + // } 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.axios, 'request').throws(new Error('test 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') + assert.include(err.message, 'test error') } }) }) @@ -84,6 +70,7 @@ describe('Admin', () => { } sandbox.stub(uut.User, 'findOne').resolves(fakeUser) + sandbox.stub(uut.jsonFiles, 'writeJSON').resolves(true) const result = await uut.createSystemUser() assert.property(result, 'email') diff --git a/test/unit/controllers/rest-api/users/users.rest.router.unit.js b/test/unit/controllers/rest-api/users/users.rest.router.unit.js index 798264f..ef5fcb3 100644 --- a/test/unit/controllers/rest-api/users/users.rest.router.unit.js +++ b/test/unit/controllers/rest-api/users/users.rest.router.unit.js @@ -79,4 +79,32 @@ describe('#Users-REST-Router', () => { } }) }) + + describe('#createUser', () => { + it('should ignore admin validator when DISABLE_NEW_ACCOUNTS is not defined', async () => { + // Stub functions + const validationSpy = sandbox.stub(uut.validators, 'ensureAdmin').resolves(true) + sandbox.stub(uut.userRESTController, 'createUser').resolves(true) + + // Call function + await uut.createUser() + + // Assertions + assert.isTrue(validationSpy.notCalled, 'Admin validator should not be called') + }) + it('should ensure admin when DISABLE_NEW_ACCOUNTS is defined', async () => { + // Set environment variable + process.env.DISABLE_NEW_ACCOUNTS = true + + // Stub functions + const validationSpy = sandbox.stub(uut.validators, 'ensureAdmin').resolves(true) + sandbox.stub(uut.userRESTController, 'createUser').resolves(true) + + // Call function + await uut.createUser() + + // Assertions + assert.isTrue(validationSpy.calledOnce, 'Admin validator should be called') + }) + }) }) diff --git a/test/utils/test-utils.js b/test/utils/test-utils.js index 5758dbc..6ee0e45 100644 --- a/test/utils/test-utils.js +++ b/test/utils/test-utils.js @@ -8,7 +8,6 @@ import axios from 'axios' // Local libraries import config from '../../config/index.js' -import User from '../../src/adapters/localdb/models/users.js' import JsonFiles from '../../src/adapters/json-files.js' // Hack to get __dirname back. @@ -33,24 +32,6 @@ async function cleanDb () { } } -// Delete all users in the database. This ensures there is no previous state -// to confuse tests. -async function deleteAllUsers () { - try { - // Get all the users in the DB. - const users = await User.find({}, '-password') - // console.log(`users: ${JSON.stringify(users, null, 2)}`) - - // Delete each user. - for (let i = 0; i < users.length; i++) { - const thisUser = users[i] - await thisUser.remove() - } - } catch (err) { - console.error('Error in test-utils.js/deleteAllUsers()') - } -} - // This function is used to create new users. // userObj = { // username, @@ -170,12 +151,56 @@ async function getAdminJWT () { throw err } } +// Fetches all users from the database. +async function getAllUsers () { + try { + const adminJWT = await getAdminJWT() + const options = { + method: 'GET', + url: `${LOCALHOST}/users`, + headers: { + Authorization: `Bearer ${adminJWT}` + } + } + const result = await axios(options) + return result.data.users + } catch (err) { + console.error('Error in test/utils.js/getAllUsers()', err) + throw err + } +} +// Deletes all users from the database. +async function deleteAllUsers () { + try { + const allUsers = await getAllUsers() + const adminJWT = await getAdminJWT() + for (let i = 0; i < allUsers.length; i++) { + const user = allUsers[i] + // Skip the admin user. + if (user.type === 'admin') { + continue + } + const options = { + method: 'DELETE', + url: `${LOCALHOST}/users/${user._id}`, + headers: { + Authorization: `Bearer ${adminJWT}` + } + } + await axios(options) + } + } catch (err) { + console.error('Error in test/utils.js/deleteAllUsers()', err) + throw err + } +} export default { cleanDb, createUser, loginTestUser, loginAdminUser, getAdminJWT, - deleteAllUsers + deleteAllUsers, + getAllUsers }