/////////////////////////////////////////////////////////////// //Dependency Injection /////////////////////////////////////////////////////////////// /** * Provides Inversion of Control interface wrapping Bottlejs */ class ApplicationContext { /** * Create a new ApplicationContext with a given name * @param {String} name - The name of the application context */ constructor(name = 'main') { if (!name) throw new Error('ApplicationContext name must be defined'); if (!typeof (name) === 'string') throw new Error('ApplicationContext name must be a string.'); try { this._bottle = new Bottle(name); } catch (error) { console.error(error) throw new Error(`Unable to create new Application Context with name ${name}`); } this._name = name } /** * Register a new service into the Application Context. Will throw error if name|service is undefined * @param {String} name - The name of the service that is being registered into the ApplciationContext * @param {any} service - The service object to register to the given name. */ register(name, service) { if (!name) throw new Error('No name given to register service.'); if (!typeof (name) === 'string') throw new Error('Service name must be a string.'); if (!service) throw new Error('No service object given.'); this._bottle.service(name, service); } /** * Get the registered service with a given name. Throws error if name undefined or service not registered. * @param {String} name - The name of the service to retrieve. * @returns {any} - The Service object */ get(name) { if (!name) throw new Error('Serice name must be defined') if (!typeof (name) === 'string') throw new Error('Service name must be a string.'); const service = this._bottle.container[name]; if (!service) throw new Error(`Service ${name} not found. Please ensure all required services are registered into the Application Context`); return service; } /** * Clear the Application context of all services */ clear() { Bottle.clear(this._name) } } //Export Singleton Application Context to be used across the application. const appContext = new ApplicationContext() Object.freeze(appContext) export default appContext; /////////////////////////////////////////////////////////////// //User Database /////////////////////////////////////////////////////////////// /** * Data Access object for user profiles */ export default class UserDao extends Dao { /** * Returns a new UserDao object * @param {Object} user - the populated user object */ constructor(user) { super() this._user = new User(user); } /** * Returns a new UserDao for the user with the username specified * @param {String} usernameDb - The unique username for the user to fetch * @return {Promise} - The user with the given username */ static async fromUserName(usernameDb) { if (!await Dao._init()) new Error('Database cannot be initialized'); let foundUser = await User.findOne({ 'usernameDb': usernameDb }).exec(); if (!foundUser) return null return new UserDao(foundUser) } } /////////////////////////////////////////////////////////////// //Testing /////////////////////////////////////////////////////////////// /* Test Suite for Authentication Controller*/ describe("AuthController Class", function () { //Tests for login functionality describe("#login", function () { before(async function () { context.clear() //Need to mock the user retrieval from the DB, done via UserDao Class sinon.stub(UserDao.fromUserName, async () => { return { username: 'userperson', profile: {}, password: 'bcrypt_hash_goes_here' } }) //This apparently does not work. sinon.stub(UserDao.prototype.contructor, async () => { return { updateUser: async () => { return true } } // }) //This is an application context class that provides dependency injection for the application // Modules grab their dependencies with context.get('service_name') context.register('UserDao', UserDao) }) it("login: Should throw a 400 for a password less than 8 characters", async function () { const auth = new AuthController(process.env.SERVER_SECRET); const shortPassword = '@1234gH' try { const JWT = await auth.login(testUser, shortPassword) throw new Error('AuthController Should have thrown an 400 HttpError.') } catch (error) { if (!error instanceof HttpError) throw error; expect(error.code).to.equal(400) return true } }); }) })