diff --git a/src/controllers/rest-api/index.js b/src/controllers/rest-api/index.js index 505d091..90255d8 100644 --- a/src/controllers/rest-api/index.js +++ b/src/controllers/rest-api/index.js @@ -8,23 +8,23 @@ // Load the REST API Controllers. const AuthRESTController = require('./auth') -const UserRESTController = require('./users') +const UserRouter = require('./users') const ContactRESTController = require('./contact') const LogsRESTController = require('./logs') class RESTControllers { - constructor (localConfig) { + constructor (localConfig = {}) { // Dependency Injection. this.adapters = localConfig.adapters if (!this.adapters) { throw new Error( - 'Instance of Adapters library required when instantiating PostEntry REST Controller.' + 'Instance of Adapters library required when instantiating REST Controller libraries.' ) } this.useCases = localConfig.useCases if (!this.useCases) { throw new Error( - 'Instance of Use Cases library required when instantiating PostEntry REST Controller.' + 'Instance of Use Cases library required when instantiating REST Controller libraries.' ) } @@ -42,8 +42,8 @@ class RESTControllers { authRESTController.attach(app) // Attach the REST API Controllers associated with the /user route - const userRESTController = new UserRESTController(dependencies) - userRESTController.attach(app) + const userRouter = new UserRouter(dependencies) + userRouter.attach(app) // Attach the REST API Controllers associated with the /contact route const contactRESTController = new ContactRESTController(dependencies) diff --git a/src/controllers/rest-api/users/controller.js b/src/controllers/rest-api/users/controller.js index 7fd9e54..01ab82b 100644 --- a/src/controllers/rest-api/users/controller.js +++ b/src/controllers/rest-api/users/controller.js @@ -2,12 +2,6 @@ REST API Controller library for the /user route */ -// User database model. -// const UserModel = require('../../../adapters/localdb/models/users') - -// User library for business logic. -// const UserLib = require('../../../use-cases/user') - const { wlogger } = require('../../../adapters/wlogger') let _this diff --git a/src/controllers/rest-api/users/index.js b/src/controllers/rest-api/users/index.js index 302b701..c829615 100644 --- a/src/controllers/rest-api/users/index.js +++ b/src/controllers/rest-api/users/index.js @@ -2,9 +2,16 @@ REST API library for /user route. */ -const UserRESTRouter = require('./router') +// Public npm libraries. +const Router = require('koa-router') -class UserRESTController { +// Local libraries. +const UserRESTControllerLib = require('./controller') +const Validators = require('../middleware/validators') + +let _this + +class UserRouter { constructor (localConfig = {}) { // Dependency Injection. this.adapters = localConfig.adapters @@ -20,12 +27,62 @@ class UserRESTController { ) } - this.userRESTRouter = new UserRESTRouter(localConfig) + const dependencies = { + adapters: this.adapters, + useCases: this.useCases + } + + // Encapsulate dependencies. + this.userRESTController = new UserRESTControllerLib(dependencies) + this.validators = new Validators() + + // Instantiate the router and set the base route. + const baseUrl = '/users' + this.router = new Router({ prefix: baseUrl }) + + _this = this } attach (app) { - this.userRESTRouter.attachControllers(app) + if (!app) { + throw new Error( + 'Must pass app object when attaching REST API controllers.' + ) + } + + // Define the routes and attach the controller. + this.router.post('/', this.userRESTController.createUser) + this.router.get('/', this.getAll) + this.router.get('/:id', this.getById) + this.router.put('/:id', this.updateUser) + this.router.delete('/:id', this.deleteUser) + + // Attach the Controller routes to the Koa app. + app.use(this.router.routes()) + app.use(this.router.allowedMethods()) + } + + async getAll (ctx, next) { + await _this.validators.ensureUser(ctx, next) + await _this.userRESTController.getUsers(ctx, next) + } + + async getById (ctx, next) { + await _this.validators.ensureUser(ctx, next) + await _this.userRESTController.getUser(ctx, next) + } + + async updateUser (ctx, next) { + await _this.validators.ensureTargetUserOrAdmin(ctx, next) + await _this.userRESTController.getUser(ctx, next) + await _this.userRESTController.updateUser(ctx, next) + } + + async deleteUser (ctx, next) { + await _this.validators.ensureTargetUserOrAdmin(ctx, next) + await _this.userRESTController.getUser(ctx, next) + await _this.userRESTController.deleteUser(ctx, next) } } -module.exports = UserRESTController +module.exports = UserRouter diff --git a/src/controllers/rest-api/users/router.js b/src/controllers/rest-api/users/router.js deleted file mode 100644 index a57086a..0000000 --- a/src/controllers/rest-api/users/router.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - REST Router for the /user route. -*/ - -// Public npm libraries. -const Router = require('koa-router') - -// Local libraries. -const UserRESTControllerLib = require('./controller') -const Validators = require('../middleware/validators') - -let _this - -class UserRESTRouter { - constructor (localConfig = {}) { - // Dependency Injection. - this.adapters = localConfig.adapters - if (!this.adapters) { - throw new Error( - 'Instance of Adapters library required when instantiating /users REST Controller.' - ) - } - this.useCases = localConfig.useCases - if (!this.useCases) { - throw new Error( - 'Instance of Use Cases library required when instantiating /users REST Controller.' - ) - } - const dependencies = { - adapters: this.adapters, - useCases: this.useCases - } - - // Encapsulate dependencies. - this.userRESTController = new UserRESTControllerLib(dependencies) - this.validators = new Validators() - - // Instantiate the router and set the base route. - const baseUrl = '/users' - this.router = new Router({ prefix: baseUrl }) - - _this = this - } - - attachControllers (app) { - if (!app) { - throw new Error( - 'Must pass app object when attaching REST API controllers.' - ) - } - - // Define the routes and attach the controller. - this.router.post('/', this.userRESTController.createUser) - this.router.get('/', this.getAll) - this.router.get('/:id', this.getById) - this.router.put('/:id', this.updateUser) - this.router.delete('/:id', this.deleteUser) - - // Attach the Controller routes to the Koa app. - app.use(this.router.routes()) - app.use(this.router.allowedMethods()) - } - - async getAll (ctx, next) { - await _this.validators.ensureUser(ctx, next) - await _this.userRESTController.getUsers(ctx, next) - } - - async getById (ctx, next) { - await _this.validators.ensureUser(ctx, next) - await _this.userRESTController.getUser(ctx, next) - } - - async updateUser (ctx, next) { - await _this.validators.ensureTargetUserOrAdmin(ctx, next) - await _this.userRESTController.getUser(ctx, next) - await _this.userRESTController.updateUser(ctx, next) - } - - async deleteUser (ctx, next) { - await _this.validators.ensureTargetUserOrAdmin(ctx, next) - await _this.userRESTController.getUser(ctx, next) - await _this.userRESTController.deleteUser(ctx, next) - } -} - -module.exports = UserRESTRouter diff --git a/test/e2e/automated/a02-users.rest-e2e.js b/test/e2e/automated/a02-users.rest-e2e.js index 1ccdd46..439c6e1 100644 --- a/test/e2e/automated/a02-users.rest-e2e.js +++ b/test/e2e/automated/a02-users.rest-e2e.js @@ -292,7 +292,7 @@ describe('Users', () => { assert.fail('Unexpected code path!') } catch (err) { - console.log(err) + // console.log(err) assert.equal(err.response.status, 422) assert.equal(err.response.data, 'test error') } diff --git a/test/unit/controllers/json-rpc/users.json-rpc-controller.unit.js b/test/unit/controllers/json-rpc/users.json-rpc-controller.unit.js index 2d95c96..61aa0d3 100644 --- a/test/unit/controllers/json-rpc/users.json-rpc-controller.unit.js +++ b/test/unit/controllers/json-rpc/users.json-rpc-controller.unit.js @@ -13,8 +13,6 @@ process.env.SVC_ENV = 'test' // Local libraries const UserRPC = require('../../../../src/controllers/json-rpc/users') -const RateLimit = require('../../../../src/controllers/json-rpc/rate-limit') -// const UserModel = require('../../../src/adapters/localdb/models/users') const adapters = require('../../mocks/adapters') const UseCasesMock = require('../../mocks/use-cases') @@ -29,7 +27,6 @@ describe('#UserRPC', () => { const useCases = new UseCasesMock() uut = new UserRPC({ adapters, useCases }) - uut.rateLimit = new RateLimit({ max: 100 }) }) afterEach(() => sandbox.restore()) diff --git a/test/unit/controllers/rest-api/rest.controller.unit.js b/test/unit/controllers/rest-api/rest.controller.unit.js new file mode 100644 index 0000000..f798edb --- /dev/null +++ b/test/unit/controllers/rest-api/rest.controller.unit.js @@ -0,0 +1,64 @@ +/* + Unit tests for the REST API controllers/rest-api/index.js library. +*/ + +// Public npm libraries +const assert = require('chai').assert +const sinon = require('sinon') + +// Local libraries +const RESTControllers = require('../../../../src/controllers/rest-api/') +// const mockContext = require('../../../unit/mocks/ctx-mock').context +const adapters = require('../../mocks/adapters') +const UseCasesMock = require('../../mocks/use-cases') + +describe('#RESTControllers', () => { + let uut + let sandbox + // let ctx + + before(async () => {}) + + beforeEach(() => { + const useCases = new UseCasesMock() + uut = new RESTControllers({ adapters, useCases }) + + sandbox = sinon.createSandbox() + + // Mock the context object. + // ctx = mockContext() + }) + + afterEach(() => sandbox.restore()) + + describe('#constructor', () => { + it('should throw an error if adapters are not passed in', () => { + try { + uut = new RESTControllers() + + assert.fail('Unexpected code path') + } catch (err) { + assert.include( + err.message, + 'Instance of Adapters library required when instantiating REST Controller libraries.' + ) + } + }) + + it('should throw an error if useCases are not passed in', () => { + try { + uut = new RESTControllers({ adapters }) + + assert.fail('Unexpected code path') + + // use to prevent complaints from linter. + console.log('uut: ', uut) + } catch (err) { + assert.include( + err.message, + 'Instance of Use Cases library required when instantiating REST Controller libraries.' + ) + } + }) + }) +}) diff --git a/test/unit/controllers/rest-api/a02-users.rest-unit.js b/test/unit/controllers/rest-api/users/users.rest.controller.unit.js similarity index 76% rename from test/unit/controllers/rest-api/a02-users.rest-unit.js rename to test/unit/controllers/rest-api/users/users.rest.controller.unit.js index ba6e3e8..737d86b 100644 --- a/test/unit/controllers/rest-api/a02-users.rest-unit.js +++ b/test/unit/controllers/rest-api/users/users.rest.controller.unit.js @@ -5,62 +5,21 @@ // Public npm libraries const assert = require('chai').assert const sinon = require('sinon') -const mongoose = require('mongoose') // Local support libraries -const config = require('../../../../config') -const testUtils = require('../../../utils/test-utils') -const adapters = require('../../mocks/adapters') -const UseCasesMock = require('../../mocks/use-cases') +const adapters = require('../../../mocks/adapters') +const UseCasesMock = require('../../../mocks/use-cases') -const UserController = require('../../../../src/controllers/rest-api/users/controller') +const UserController = require('../../../../../src/controllers/rest-api/users/controller') let uut let sandbox let ctx -const mockContext = require('../../../unit/mocks/ctx-mock').context +const mockContext = require('../../../../unit/mocks/ctx-mock').context -describe('Users', () => { +describe('#Users-REST-Controller', () => { // const testUser = {} - before(async () => { - // Connect to the Mongo Database. - mongoose.Promise = global.Promise - mongoose.set('useCreateIndex', true) // Stop deprecation warning. - await mongoose.connect(config.database, { - useUnifiedTopology: true, - useNewUrlParser: true - }) - - // Delete all previous users in the database. - await testUtils.deleteAllUsers() - - // console.log(`config: ${JSON.stringify(config, null, 2)}`) - - // Create a second test user. - // const userObj = { - // email: 'test2@test.com', - // password: 'pass2' - // } - // const testUser = await testUtils.createUser(userObj) - // console.log(`testUser2: ${JSON.stringify(testUser, null, 2)}`) - - // context.user2 = testUser.user - // context.token2 = testUser.token - // context.id2 = testUser.user._id - - // Get the JWT used to log in as the admin 'system' user. - // const adminJWT = await testUtils.getAdminJWT() - // // console.log(`adminJWT: ${adminJWT}`) - // context.adminJWT = adminJWT - - // const admin = await testUtils.loginAdminUser() - // context.adminJWT = admin.token - - // const admin = await adminLib.loginAdmin() - // console.log(`admin: ${JSON.stringify(admin, null, 2)}`) - }) - beforeEach(() => { const useCases = new UseCasesMock() uut = new UserController({ adapters, useCases }) @@ -73,8 +32,32 @@ describe('Users', () => { afterEach(() => sandbox.restore()) - after(() => { - mongoose.connection.close() + describe('#constructor', () => { + it('should throw an error if adapters are not passed in', () => { + try { + uut = new UserController() + + assert.fail('Unexpected code path') + } catch (err) { + assert.include( + err.message, + 'Instance of Adapters library required when instantiating /users REST Controller.' + ) + } + }) + + it('should throw an error if useCases are not passed in', () => { + try { + uut = new UserController({ adapters }) + + assert.fail('Unexpected code path') + } catch (err) { + assert.include( + err.message, + 'Instance of Use Cases library required when instantiating /users REST Controller.' + ) + } + }) }) describe('#POST /users', () => { @@ -258,4 +241,18 @@ describe('Users', () => { assert.equal(ctx.status, 200) }) }) + + describe('#handleError', () => { + it('should still throw error if there is no message', () => { + try { + const err = { + status: 404 + } + + uut.handleError(ctx, err) + } catch (err) { + assert.include(err.message, 'Not Found') + } + }) + }) }) 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 new file mode 100644 index 0000000..f225562 --- /dev/null +++ b/test/unit/controllers/rest-api/users/users.rest.router.unit.js @@ -0,0 +1,78 @@ +/* + Unit tests for the REST API handler for the /users endpoints. +*/ + +// Public npm libraries +const assert = require('chai').assert +const sinon = require('sinon') + +// Local support libraries +const adapters = require('../../../mocks/adapters') +const UseCasesMock = require('../../../mocks/use-cases') +// const app = require('../../../mocks/app-mock') + +const UserRouter = require('../../../../../src/controllers/rest-api/users') +let uut +let sandbox +// let ctx + +// const mockContext = require('../../../../unit/mocks/ctx-mock').context + +describe('#Users-REST-Router', () => { + // const testUser = {} + + beforeEach(() => { + const useCases = new UseCasesMock() + uut = new UserRouter({ adapters, useCases }) + + sandbox = sinon.createSandbox() + + // Mock the context object. + // ctx = mockContext() + }) + + afterEach(() => sandbox.restore()) + + describe('#constructor', () => { + it('should throw an error if adapters are not passed in', () => { + try { + uut = new UserRouter() + + assert.fail('Unexpected code path') + } catch (err) { + assert.include( + err.message, + 'Instance of Adapters library required when instantiating PostEntry REST Controller.' + ) + } + }) + + it('should throw an error if useCases are not passed in', () => { + try { + uut = new UserRouter({ adapters }) + + assert.fail('Unexpected code path') + } catch (err) { + assert.include( + err.message, + 'Instance of Use Cases library required when instantiating PostEntry REST Controller.' + ) + } + }) + }) + + describe('#attach', () => { + it('should throw an error if app is not passed in.', () => { + try { + uut.attach() + + assert.fail('Unexpected code path') + } catch (err) { + assert.include( + err.message, + 'Must pass app object when attaching REST API controllers.' + ) + } + }) + }) +}) diff --git a/test/unit/mocks/app-mock.js b/test/unit/mocks/app-mock.js new file mode 100644 index 0000000..e341212 --- /dev/null +++ b/test/unit/mocks/app-mock.js @@ -0,0 +1,9 @@ +/* + Mocks for Koa 'app' object. +*/ + +const app = { + use: () => {} +} + +module.exports = app