From 0646645686a65959231cfcabbab7f6e8ab715a1c Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 9 Nov 2016 12:51:32 -0700 Subject: [PATCH 1/3] Added new cli interface + users model --- bin/cli | 29 ++++ bin/cli-settings | 42 +++++ bin/cli-users | 408 +++++++++++++++++++++++++++++++++++++++++++++++ bin/init.js | 12 -- bin/www | 28 ++-- models/user.js | 277 +++++++++++++++++++++++++++++--- mongoose.js | 10 +- package.json | 5 +- 8 files changed, 758 insertions(+), 53 deletions(-) create mode 100755 bin/cli create mode 100755 bin/cli-settings create mode 100755 bin/cli-users delete mode 100644 bin/init.js diff --git a/bin/cli b/bin/cli new file mode 100755 index 000000000..bec5ea84f --- /dev/null +++ b/bin/cli @@ -0,0 +1,29 @@ +#!/usr/bin/env node + +/** + * Setup the debug paramater. + */ + +process.env.DEBUG = process.env.TALK_DEBUG; + +/** + * Module dependencies. + */ + +const program = require('commander'); +const pkg = require('../package.json'); + +//============================================================================== +// Setting up the program command line arguments. +//============================================================================== + +program + .version(pkg.version) + .command('settings', 'work with the application settings') + .command('users', 'work with the application auth') + .parse(process.argv); + +// If there is no command listed, output help. +if (!process.argv.slice(2).length) { + program.outputHelp(); +} diff --git a/bin/cli-settings b/bin/cli-settings new file mode 100755 index 000000000..0920473da --- /dev/null +++ b/bin/cli-settings @@ -0,0 +1,42 @@ +#!/usr/bin/env node + +/** + * Setup the debug paramater. + */ + +process.env.DEBUG = process.env.TALK_DEBUG; + +/** + * Module dependencies. + */ + +const program = require('commander'); + +//============================================================================== +// Setting up the program command line arguments. +//============================================================================== + +program + .command('init') + .description('initilizes the talk settings') + .action(() => { + const mongoose = require('../mongoose'); + const Setting = require('../models/setting'); + const defaults = {id: '1', moderation: 'pre'}; + + Setting.update({id: '1'}, {$setOnInsert: defaults}, {upsert: true}) + .then(() => { + console.log('Created settings object.'); + mongoose.disconnect(); + }).catch((err) => { + console.error(`failed to create the settings object ${JSON.stringify(err)}`); + throw new Error(err); // just to be safe + }); + }); + +program.parse(process.argv); + +// If there is no command listed, output help. +if (!process.argv.slice(2).length) { + program.outputHelp(); +} diff --git a/bin/cli-users b/bin/cli-users new file mode 100755 index 000000000..74aa902eb --- /dev/null +++ b/bin/cli-users @@ -0,0 +1,408 @@ +#!/usr/bin/env node + +/** + * Setup the debug paramater. + */ + +process.env.DEBUG = process.env.TALK_DEBUG; + +/** + * Module dependencies. + */ + +const program = require('commander'); +const pkg = require('../package.json'); +const prompt = require('prompt'); + +/** + * Prompts for input and registers a user based on those. + */ +function createUser(options) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + return new Promise((resolve, reject) => { + + if (options.flag_mode) { + return resolve({ + email: options.email, + password: options.password, + displayName: options.name, + }); + } + + prompt.start(); + + prompt.get([ + { + name: 'email', + description: 'Email', + format: 'email', + required: true + }, + { + name: 'password', + description: 'Password', + hidden: true, + required: true + }, + { + name: 'confirmPassword', + description: 'Confirm Password', + hidden: true, + required: true + }, + { + name: 'displayName', + description: 'Display Name', + required: true + } + ], (err, result) => { + if (err) { + return reject(err); + } + + if (result.password !== result.confirmPassword) { + return reject(new Error('Passwords do not match')); + } + + resolve(result); + }); + }) + .then((result) => { + return User.createLocalUser(result.email.trim(), result.password.trim(), result.displayName.trim()); + }).then((user) => { + console.log(`Created user ${user.id}.`); + mongoose.disconnect(); + }).catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Deletes a user. + */ +function deleteUser(userID) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .findOneAndRemove({ + id: userID + }) + .then(() => { + console.log('Deleted user'); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Changes the password for a user. + */ +function passwd(userID) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + prompt.start(); + + prompt.get([ + { + name: 'password', + description: 'Password', + hidden: true, + required: true + }, + { + name: 'confirmPassword', + description: 'Confirm Password', + hidden: true, + required: true + } + ], (err, result) => { + if (err) { + console.error(err); + mongoose.disconnect(); + return; + } + + if (result.password !== result.confirmPassword) { + console.error(new Error('Password mismatch')); + mongoose.disconnect(); + return; + } + + User + .changePassword(userID, result.password) + .then(() => { + console.log('Password changed.'); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); + }); +} + +/** + * Updates the user from the options array. + */ +function updateUser(userID, options) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + const updates = []; + + if (options.email && typeof options.email === 'string' && options.email.length > 0) { + let q = User.update({ + 'id': userID, + 'profiles.provider': 'local' + }, { + $set: { + 'profiles.$.id': options.email + } + }); + + updates.push(q); + } + + if (options.name && typeof options.name === 'string' && options.name.length > 0) { + let q = User.update({ + 'id': userID + }, { + $set: { + displayName: options.name + } + }); + + updates.push(q); + } + + Promise + .all(updates.map((q) => q.exec())) + .then(() => { + console.log(`User ${userID} updated.`); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Lists all the users registered in the database. + */ +function listUsers() { + const Table = require('cli-table'); + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .find() + .then((users) => { + let table = new Table({ + head: [ + 'ID', + 'Display Name', + 'Profiles', + 'Roles', + 'State' + ] + }); + + users.forEach((user) => { + table.push([ + user.id, + user.displayName, + user.profiles.map((p) => p.provider).join(', '), + user.roles.join(', '), + user.disabled ? 'Disabled' : 'Enabled' + ]); + }); + + console.log(table.toString()); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Merges two users using the specified ID's. + * @param {String} dstUserID id of the user to which is the target of the merge + * @param {String} srcUserID id of the user to which is the source of the merge + */ +function mergeUsers(dstUserID, srcUserID) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .mergeUsers(dstUserID, srcUserID) + .then(() => { + console.log(`User ${srcUserID} was merged into user ${dstUserID}.`); + mongoose.disconnect(); + }).catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Adds a role to a user + * @param {String} userUD id of the user to add the role to + * @param {String} role the role to add + */ +function addRole(userID, role) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .addRoleToUser(userID, role) + .then(() => { + console.log(`Added the ${role} role to User ${userID}.`); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Removes a role from a user + * @param {String} userUD id of the user to remove the role from + * @param {String} role the role to remove + */ +function removeRole(userID, role) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .removeRoleFromUser(userID, role) + .then(() => { + console.log(`Removed the ${role} role from User ${userID}.`); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Disable a given user. + * @param {String} userID the ID of a user to disable + */ +function disableUser(userID) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .disableUser(userID) + .then(() => { + console.log(`User ${userID} was disabled.`); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +/** + * Enabled a given user. + * @param {String} userID the ID of a user to enable + */ +function enableUser(userID) { + const User = require('../models/user'); + const mongoose = require('../mongoose'); + + User + .enableUser(userID) + .then(() => { + console.log(`User ${userID} was enabled.`); + mongoose.disconnect(); + }) + .catch((err) => { + console.error(err); + mongoose.disconnect(); + }); +} + +//============================================================================== +// Setting up the program command line arguments. +//============================================================================== + +program + .version(pkg.version); + +program + .command('create') + .option('--email [email]', 'Email to use') + .option('--password [password]', 'Password to use') + .option('--name [name]', 'Name to use') + .option('-f, --flag_mode', 'Source from flags instead of prompting') + .description('create a new user') + .action(createUser); + +program + .command('delete ') + .description('delete a user') + .action(deleteUser); + +program + .command('passwd ') + .description('change a password for a user') + .action(passwd); + +program + .command('update ') + .option('--email [email]', 'Email to use') + .option('--name [name]', 'Name to use') + .description('update a user') + .action(updateUser); + +program + .command('list') + .description('list all the users in the database') + .action(listUsers); + +program + .command('merge ') + .description('merge srcUser into the dstUser') + .action(mergeUsers); + +program + .command('addrole ') + .description('adds a role to a given user') + .action(addRole); + +program + .command('removerole ') + .description('removes a role from a given user') + .action(removeRole); + +program + .command('disable ') + .description('disable a given user from logging in') + .action(disableUser); + +program + .command('enable ') + .description('enable a given user from logging in') + .action(enableUser); + +program.parse(process.argv); + +// If there is no command listed, output help. +if (!process.argv.slice(2).length) { + program.outputHelp(); +} diff --git a/bin/init.js b/bin/init.js deleted file mode 100644 index d8be5a6fa..000000000 --- a/bin/init.js +++ /dev/null @@ -1,12 +0,0 @@ -const mongoose = require('../mongoose'); -const Setting = require('../models/setting'); -const defaults = {id: '1', moderation: 'pre'}; - -Setting.update({id: '1'}, {$setOnInsert: defaults}, {upsert: true}) - .then(() => { - console.log('Created settings object.'); - mongoose.disconnect(); - }).catch((err) => { - console.error(`failed to create the settings object ${JSON.stringify(err)}`); - throw new Error(err); // just to be safe - }); diff --git a/bin/www b/bin/www index 94408f5e8..af91c2c40 100755 --- a/bin/www +++ b/bin/www @@ -40,7 +40,7 @@ server.on('listening', onListening); */ function normalizePort(val) { - var port = parseInt(val, 10); + let port = parseInt(val, 10); if (isNaN(port)) { // named pipe @@ -64,23 +64,21 @@ function onError(error) { throw error; } - var bind = typeof port === 'string' - ? 'Pipe ' + port - : 'Port ' + port; + let bind = typeof port === 'string' + ? `Pipe ${ port}` + : `Port ${ port}`; // handle specific listen errors with friendly messages switch (error.code) { case 'EACCES': - console.error(bind + ' requires elevated privileges'); - process.exit(1); + console.error(`${bind} requires elevated privileges`); break; case 'EADDRINUSE': - console.error(bind + ' is already in use'); - process.exit(1); + console.error(`${bind} is already in use`); break; - default: - throw error; } + + throw error; } /** @@ -88,9 +86,9 @@ function onError(error) { */ function onListening() { - var addr = server.address(); - var bind = typeof addr === 'string' - ? 'pipe ' + addr - : 'port ' + addr.port; - debug('Listening on ' + bind); + let addr = server.address(); + let bind = typeof addr === 'string' + ? `pipe ${ addr}` + : `port ${ addr.port}`; + debug(`Listening on ${ bind}`); } diff --git a/models/user.js b/models/user.js index 4592ec847..fe06b9ab2 100644 --- a/models/user.js +++ b/models/user.js @@ -1,45 +1,278 @@ - const mongoose = require('../mongoose'); const uuid = require('uuid'); -const Schema = mongoose.Schema; +const bcrypt = require('bcrypt'); -const UserProfileSchema = new Schema({ +const SALT_ROUNDS = 10; + +const UserSchema = new mongoose.Schema({ id: { type: String, default: uuid.v4, unique: true }, - display_name: String, - auth_user_id: String + displayName: String, + disabled: Boolean, + password: String, + profiles: [{ + id: String, + provider: String + }], + roles: [String] +}); + +// Add the indixies on the user profile data. +UserSchema.index({ + 'profiles.id': 1, + 'profiles.provider': 1 }, { - timestamps: { - createdAt: 'created_at', - updatedAt: 'updated_at' - } + unique: true, + background: false }); /** - * Finds a user by the id. - * @param {String} id identifier of the user (uuid) -*/ -UserProfileSchema.statics.findById = function(id) { - return UserProfile.findOne({id}); + * toJSON overrides to remove the password field from the json + * output. + */ +UserSchema.options.toJSON = {}; +UserSchema.options.toJSON.hide = 'password profiles roles disabled'; +UserSchema.options.toJSON.transform = (doc, ret, options) => { + if (options.hide) { + options.hide.split(' ').forEach((prop) => { + delete ret[prop]; + }); + } + + return ret; +}; + +/** + * Finds a user given their email address that we have for them in the system + * and ensures that the retuned user matches the password passed in as well. + * @param {string} email - email to look up the user by + * @param {string} password - password to match against the found user + * @param {Function} done [description] + */ +UserSchema.statics.findLocalUser = function(email, password) { + return User.findOne({ + profiles: { + $elemMatch: { + id: email, + provider: 'local' + } + } + }) + .then((user) => { + if (!user) { + return false; + } + + return new Promise((resolve, reject) => { + bcrypt.compare(password, user.password, (err, res) => { + if (err) { + return reject(err); + } + + if (!res) { + return resolve(false); + } + + return resolve(user); + }); + }); + }); +}; + +/** + * Merges two users together by taking all the profiles on a given user and + * pushing them into the source user followed by deleting the destination user's + * user account. This will not merge the roles associated with the source user. + * @param {String} dstUserID id of the user to which is the target of the merge + * @param {String} srcUserID id of the user to which is the source of the merge + * @return {Promise} resolves when the users are merged + */ +UserSchema.statics.mergeUsers = function(dstUserID, srcUserID) { + let srcUser, dstUser; + + return Promise.all([ + User.findOne({id: dstUserID}).exec(), + User.findOne({id: srcUserID}).exec() + ]).then((users) => { + dstUser = users[0]; + srcUser = users[1]; + + srcUser.profiles.forEach((profile) => { + dstUser.profiles.push(profile); + }); + + return srcUser.remove(); + }).then(() => dstUser.save()); +}; + +/** + * Finds a user given a social profile and if the user does not exist, creates + * them. + * @param {Object} profile - User social/external profile + * @param {Function} done [description] + */ +UserSchema.statics.findOrCreateExternalUser = function(profile) { + return User.findOne({ + profiles: { + $elemMatch: { + id: profile.id, + provider: profile.provider + } + } + }) + .then((user) => { + if (user) { + return user; + } + + // The user was not found, lets create them! + user = new User({ + displayName: profile.displayName, + roles: [], + profiles: [ + { + id: profile.id, + provider: profile.provider + } + ] + }); + + return user.save(); + }); +}; + +UserSchema.statics.changePassword = function(id, password) { + return new Promise((resolve, reject) => { + bcrypt.hash(password, SALT_ROUNDS, (err, hashedPassword) => { + if (err) { + return reject(err); + } + + resolve(hashedPassword); + }); + }) + .then((hashedPassword) => { + return User.update({id}, { + $set: { + password: hashedPassword + } + }); + }); +}; + +/** + * Creates the local user with a given email, password, and name. + * @param {String} email email of the new user + * @param {String} password plaintext password of the new user + * @param {String} displayName name of the display user + * @param {Function} done callback + */ +UserSchema.statics.createLocalUser = function(email, password, displayName) { + return new Promise((resolve, reject) => { + bcrypt.hash(password, SALT_ROUNDS, (err, hashedPassword) => { + if (err) { + return reject(err); + } + + let user = new User({ + displayName: displayName, + password: hashedPassword, + roles: [], + profiles: [ + { + id: email, + provider: 'local' + } + ] + }); + + user.save((err) => { + if (err) { + return reject(err); + } + + return resolve(user); + }); + }); + }); +}; + +/** + * Disables a given user account. + * @param {String} id id of a user + * @param {Function} done callback after the operation is complete + */ +UserSchema.statics.disableUser = function(id) { + return User.update({ + id: id + }, { + $set: { + disabled: true + } + }); +}; + +/** + * Enables a given user account. + * @param {String} id id of a user + * @param {Function} done callback after the operation is complete + */ +UserSchema.statics.enableUser = function(id) { + return User.update({ + id: id + }, { + $set: { + disabled: false + } + }); +}; + +/** + * Adds a role to a user. + * @param {String} id id of a user + * @param {String} role role to add + * @param {Function} done callback after the operation is complete + */ +UserSchema.statics.addRoleToUser = function(id, role) { + return User.update({ + id: id + }, { + $addToSet: { + roles: role + } + }); +}; + +/** + * Removes a role from a user. + * @param {String} id id of a user + * @param {String} role role to remove + * @param {Function} done callback after the operation is complete + */ +UserSchema.statics.removeRoleFromUser = function(id, role) { + return User.update({ + id: id + }, { + $pull: { + roles: role + } + }); }; /** * Finds users in an array of idd. * @param {String} idd array of user identifiers (uuid) */ -UserProfileSchema.statics.findByIdArray = function(ids) { - return UserProfile.find({ +UserSchema.statics.findByIdArray = function(ids) { + return User.find({ 'id': {$in: ids} }); }; -// TO DO: methods -// modifications to user as statics -// find by auth user id +const User = mongoose.model('User', UserSchema); -const UserProfile = mongoose.model('UserProfile', UserProfileSchema); - -module.exports = UserProfile; +module.exports = User; +module.exports.Schema = UserSchema; diff --git a/mongoose.js b/mongoose.js index 8d9bb3bfb..9062816e6 100644 --- a/mongoose.js +++ b/mongoose.js @@ -1,4 +1,5 @@ const mongoose = require('mongoose'); +const debug = require('debug')('talk:db'); const enabled = require('debug').enabled; const url = process.env.TALK_MONGO_URL || 'mongodb://localhost'; @@ -11,11 +12,14 @@ if (enabled('talk:db')) { try { mongoose.connect(url, (err) => { - if (err) {throw err;} - console.log('Connected to MongoDB!'); + if (err) { + throw err; + } + + debug('Connected to MongoDB!'); }); } catch (err) { - console.log('Cannot stablish a connection with MongoDB'); + console.error('Cannot stablish a connection with MongoDB', err); } module.exports = mongoose; diff --git a/package.json b/package.json index 979475b5b..b2e7b938b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "start": "./bin/www", "build": "webpack --config webpack.config.js --bail", "build-watch": "webpack --config webpack.config.dev.js --watch", - "lint": "eslint .", + "lint": "eslint bin/* .", "pretest": "npm install", "test": "mocha tests --recursive", "test-watch": "mocha tests --recursive -w", @@ -43,12 +43,15 @@ }, "homepage": "https://github.com/coralproject/talk#readme", "dependencies": { + "bcrypt": "^0.8.7", "body-parser": "^1.15.2", + "commander": "^2.9.0", "debug": "^2.2.0", "ejs": "^2.5.2", "express": "^4.14.0", "mongoose": "^4.6.5", "morgan": "^1.7.0", + "prompt": "^1.0.0", "uuid": "^2.0.3" }, "devDependencies": { From c203bf16eb91a6f3f3201c4e0f0f4f16fff97918 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 9 Nov 2016 16:26:23 -0700 Subject: [PATCH 2/3] Improved tests and fixed them for new users model --- app.js | 7 +- models/user.js | 47 ++++++- package.json | 3 +- tests/models/user.js | 45 +++++-- tests/routes/api/comments/index.js | 196 ++++++++++++++++------------- tests/routes/api/stream/index.js | 20 +-- 6 files changed, 206 insertions(+), 112 deletions(-) diff --git a/app.js b/app.js index 29817c970..04e4222cd 100644 --- a/app.js +++ b/app.js @@ -6,7 +6,12 @@ const path = require('path'); const app = express(); // Middleware declarations. -app.use(morgan('dev')); + +// Add the logging middleware only if we aren't testing. +if (app.get('env') !== 'test') { + app.use(morgan('dev')); +} + app.use(bodyParser.json()); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); diff --git a/models/user.js b/models/user.js index fe06b9ab2..6800b59ef 100644 --- a/models/user.js +++ b/models/user.js @@ -8,14 +8,21 @@ const UserSchema = new mongoose.Schema({ id: { type: String, default: uuid.v4, - unique: true + unique: true, + required: true }, displayName: String, disabled: Boolean, password: String, profiles: [{ - id: String, - provider: String + id: { + type: String, + required: true + }, + provider: { + type: String, + required: true + } }], roles: [String] }); @@ -163,6 +170,18 @@ UserSchema.statics.changePassword = function(id, password) { }); }; +/** + * Creates local users. + * @param {Array} users Users to create + * @return {Promise} Resolves with the users that were created + */ +UserSchema.statics.createLocalUsers = function(users) { + return Promise.all(users.map((user) => { + return User + .createLocalUser(user.email, user.password, user.displayName); + })); +}; + /** * Creates the local user with a given email, password, and name. * @param {String} email email of the new user @@ -171,6 +190,18 @@ UserSchema.statics.changePassword = function(id, password) { * @param {Function} done callback */ UserSchema.statics.createLocalUser = function(email, password, displayName) { + if (!email) { + return Promise.reject('email is required'); + } + + if (!password) { + return Promise.reject('password is required'); + } + + if (!displayName) { + return Promise.reject('displayName is required'); + } + return new Promise((resolve, reject) => { bcrypt.hash(password, SALT_ROUNDS, (err, hashedPassword) => { if (err) { @@ -262,9 +293,17 @@ UserSchema.statics.removeRoleFromUser = function(id, role) { }); }; +/** + * Finds a user with the id. + * @param {String} id user id (uuid) +*/ +UserSchema.statics.findById = function(id) { + return User.findOne({id}); +}; + /** * Finds users in an array of idd. - * @param {String} idd array of user identifiers (uuid) + * @param {Array} ids array of user identifiers (uuid) */ UserSchema.statics.findByIdArray = function(ids) { return User.find({ diff --git a/package.json b/package.json index d24ac8a0a..754742516 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "pre-git": { "commit-msg": [], "pre-commit": [ - "npm run lint" + "npm run lint", + "npm test" ], "pre-push": [ "npm test" diff --git a/tests/models/user.js b/tests/models/user.js index 46fc7703c..056aafeed 100644 --- a/tests/models/user.js +++ b/tests/models/user.js @@ -6,12 +6,18 @@ const expect = require('chai').expect; describe('User: models', () => { let mockUsers; beforeEach(() => { - return User.create([{ - display_name: 'Stampi', + return User.createLocalUsers([{ + email: 'stampi@gmail.com', + displayName: 'Stampi', + password: '1Coral!' }, { - display_name: 'Sockmonster', + email: 'sockmonster@gmail.com', + displayName: 'Sockmonster', + password: '2Coral!' }, { - display_name: 'Marvel', + email: 'marvel@gmail.com', + displayName: 'Marvel', + password: '3Coral!' }]).then((users) => { mockUsers = users; }); @@ -19,10 +25,12 @@ describe('User: models', () => { describe('#findById()', () => { it('should find a user by id', () => { - return User.findById(mockUsers[0].id).then((result) => { - expect(result).to.have.property('display_name') - .and.to.equal('Stampi'); - }); + return User + .findById(mockUsers[0].id) + .then((user) => { + expect(user).to.have.property('displayName') + .and.to.equal('Stampi'); + }); }); }); @@ -34,4 +42,25 @@ describe('User: models', () => { }); }); }); + + describe('#findLocalUser', () => { + + it('should find a user when we give the right credentials', () => { + return User + .findLocalUser(mockUsers[0].profiles[0].id, '1Coral!') + .then((user) => { + expect(user).to.have.property('displayName') + .and.to.equal(mockUsers[0].displayName); + }); + }); + + it('should not find the user when we give the wrong credentials', () => { + return User + .findLocalUser(mockUsers[0].profiles[0].id, '1Coral!') + .then((user) => { + expect(user).to.equal(false); + }); + }); + + }); }); diff --git a/tests/routes/api/comments/index.js b/tests/routes/api/comments/index.js index 378860bcb..492f7c238 100644 --- a/tests/routes/api/comments/index.js +++ b/tests/routes/api/comments/index.js @@ -32,11 +32,13 @@ describe('Get /comments', () => { }]; const users = [{ - id: '123', - display_name: 'Ana', + displayName: 'Ana', + email: 'ana@gmail.com', + password: '123' }, { - id: '456', - display_name: 'Maria', + displayName: 'Maria', + email: 'maria@gmail.com', + password: '123' }]; const actions = [{ @@ -48,11 +50,11 @@ describe('Get /comments', () => { }]; beforeEach(() => { - return Comment.create(comments).then(() => { - return User.create(users); - }).then(() => { - return Action.create(actions); - }); + return Promise.all([ + Comment.create(comments), + User.createLocalUsers(users), + Action.create(actions) + ]); }); it('should return all the comments', function(done){ @@ -86,11 +88,13 @@ describe('Get moderation queues rejected, pending, flags', () => { }]; const users = [{ - id: '123', - display_name: 'Ana', + displayName: 'Ana', + email: 'ana@gmail.com', + password: '123' }, { - id: '456', - display_name: 'Maria', + displayName: 'Maria', + email: 'maria@gmail.com', + password: '123' }]; const actions = [{ @@ -104,11 +108,11 @@ describe('Get moderation queues rejected, pending, flags', () => { }]; beforeEach(() => { - return Comment.create(comments).then(() => { - return User.create(users); - }).then(() => { - return Action.create(actions); - }); + return Promise.all([ + Comment.create(comments), + User.createLocalUsers(users), + Action.create(actions) + ]); }); it('should return all the rejected comments', function(done){ @@ -148,11 +152,13 @@ describe('Get moderation queues rejected, pending, flags', () => { describe('Post /comments', () => { const users = [{ - id: '123', - display_name: 'Ana', + displayName: 'Ana', + email: 'ana@gmail.com', + password: '123' }, { - id: '456', - display_name: 'Maria', + displayName: 'Maria', + email: 'maria@gmail.com', + password: '123' }]; const actions = [{ @@ -164,9 +170,10 @@ describe('Post /comments', () => { }]; beforeEach(() => { - return User.create(users).then(() => { - return Action.create(actions); - }); + return Promise.all([ + User.createLocalUsers(users), + Action.create(actions) + ]); }); it('it should create a comment', function(done) { @@ -199,11 +206,13 @@ describe('Get /:comment_id', () => { }]; const users = [{ - id: '123', - display_name: 'Ana', + displayName: 'Ana', + email: 'ana@gmail.com', + password: '123' }, { - id: '456', - display_name: 'Maria', + displayName: 'Maria', + email: 'maria@gmail.com', + password: '123' }]; const actions = [{ @@ -217,11 +226,11 @@ describe('Get /:comment_id', () => { }]; beforeEach(() => { - return Comment.create(comments).then(() => { - return User.create(users); - }).then(() => { - return Action.create(actions); - }); + return Promise.all([ + Comment.create(comments), + User.createLocalUsers(users), + Action.create(actions) + ]); }); it('should return the right comment for the comment_id', function(done){ @@ -256,11 +265,13 @@ describe('Put /:comment_id', () => { }]; const users = [{ - id: '123', - display_name: 'Ana', + displayName: 'Ana', + email: 'ana@gmail.com', + password: '123' }, { - id: '456', - display_name: 'Maria', + displayName: 'Maria', + email: 'maria@gmail.com', + password: '123' }]; const actions = [{ @@ -272,11 +283,11 @@ describe('Put /:comment_id', () => { }]; beforeEach(() => { - return Comment.create(comments).then(() => { - return User.create(users); - }).then(() => { - return Action.create(actions); - }); + return Promise.all([ + Comment.create(comments), + User.createLocalUsers(users), + Action.create(actions) + ]); }); it('it should update comment', function(done) { @@ -311,11 +322,13 @@ describe('Remove /:comment_id', () => { }]; const users = [{ - id: '123', - display_name: 'Ana', + displayName: 'Ana', + email: 'ana@gmail.com', + password: '123' }, { - id: '456', - display_name: 'Maria', + displayName: 'Maria', + email: 'maria@gmail.com', + password: '123' }]; const actions = [{ @@ -327,27 +340,32 @@ describe('Remove /:comment_id', () => { }]; beforeEach(() => { - return Comment.create(comments).then(() => { - return User.create(users); - }).then(() => { - return Action.create(actions); - }); + return Promise.all([ + Comment.create(comments), + User.createLocalUsers(users), + Action.create(actions) + ]); }); - it('it should remove comment', function(done) { - chai.request(app) + it('it should remove comment', () => { + return chai.request(app) .delete('/api/v1/comments/abc') - .end(function(err, res){ - expect(err).to.be.null; + .then((res) => { expect(res).to.have.status(201); - Comment.findById('abc').then((comment) => { - expect(comment).to.be.empty; - }); - done(); + + return Comment.findById('abc'); + }) + .then((comment) => { + expect(comment).to.be.null; }); }); }); +process.on('unhandledRejection', (reason) => { + console.error('Reason: '); + console.error(reason); +}); + describe('Post /:comment_id/status', () => { const comments = [{ @@ -370,11 +388,13 @@ describe('Post /:comment_id/status', () => { }]; const users = [{ - id: '123', - display_name: 'Ana', + displayName: 'Ana', + email: 'ana@gmail.com', + password: '123' }, { - id: '456', - display_name: 'Maria', + displayName: 'Maria', + email: 'maria@gmail.com', + password: '123' }]; const actions = [{ @@ -386,23 +406,21 @@ describe('Post /:comment_id/status', () => { }]; beforeEach(() => { - return Comment.create(comments).then(() => { - return User.create(users); - }).then(() => { - return Action.create(actions); - }); + return Promise.all([ + Comment.create(comments), + User.createLocalUsers(users), + Action.create(actions) + ]); }); - it('it should update status', function(done) { - chai.request(app) + it('it should update status', function() { + return chai.request(app) .post('/api/v1/comments/abc/status') - .send({'status': 'accepted'}) - .end(function(err, res){ - expect(err).to.be.null; + .send({status: 'accepted'}) + .then((res) => { expect(res).to.have.status(200); expect(res).to.have.body; expect(res.body).to.have.property('status', 'accepted'); - done(); }); }); }); @@ -429,11 +447,13 @@ describe('Post /:comment_id/actions', () => { }]; const users = [{ - id: '123', - display_name: 'Ana', + displayName: 'Ana', + email: 'ana@gmail.com', + password: '123' }, { - id: '456', - display_name: 'Maria', + displayName: 'Maria', + email: 'maria@gmail.com', + password: '123' }]; const actions = [{ @@ -445,26 +465,24 @@ describe('Post /:comment_id/actions', () => { }]; beforeEach(() => { - return Comment.create(comments).then(() => { - return User.create(users); - }).then(() => { - return Action.create(actions); - }); + return Promise.all([ + Comment.create(comments), + User.createLocalUsers(users), + Action.create(actions) + ]); }); - it('it should update actions', function(done) { - chai.request(app) + it('it should update actions', () => { + return chai.request(app) .post('/api/v1/comments/abc/actions') .send({'user_id': '456', 'action_type': 'flag'}) - .end(function(err, res){ - expect(err).to.be.null; + .then((res) => { expect(res).to.have.status(200); expect(res).to.have.body; expect(res.body).to.have.property('item_type', 'comment'); expect(res.body).to.have.property('action_type', 'flag'); expect(res.body).to.have.property('item_id', 'abc'); expect(res.body).to.have.property('user_id', '456'); - done(); }); }); }); diff --git a/tests/routes/api/stream/index.js b/tests/routes/api/stream/index.js index b1348e88a..6859d1dc0 100644 --- a/tests/routes/api/stream/index.js +++ b/tests/routes/api/stream/index.js @@ -30,11 +30,13 @@ describe('api/stream: routes', () => { }]; const users = [{ - id: '123', - display_name: 'John', + displayName: 'Ana', + email: 'ana@gmail.com', + password: '123' }, { - id: '456', - display_name: 'Paul', + displayName: 'Maria', + email: 'maria@gmail.com', + password: '123' }]; const actions = [{ @@ -46,11 +48,11 @@ describe('api/stream: routes', () => { }]; beforeEach(() => { - return Comment.create(comments).then(() => { - return User.create(users); - }).then(() => { - return Action.create(actions); - }); + return Promise.all([ + Comment.create(comments), + User.createLocalUsers(users), + Action.create(actions) + ]); }); it('should return a stream with comments, users and actions', function(done){ From 27896b8e428acc1c5b4d8a24c9f449b387cb4158 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 9 Nov 2016 16:33:19 -0700 Subject: [PATCH 3/3] Fixed tests for new users model --- .../coral-admin/src/services/talk-adapter.js | 2 +- tests/models/comment.js | 41 ++++++++++--------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/client/coral-admin/src/services/talk-adapter.js b/client/coral-admin/src/services/talk-adapter.js index 8122bc709..b187c6791 100644 --- a/client/coral-admin/src/services/talk-adapter.js +++ b/client/coral-admin/src/services/talk-adapter.js @@ -42,7 +42,7 @@ Promise.all([fetch('/api/v1/comments/status/pending'), fetch('/api/v1/comments/s // Update a comment. Now to update a comment we need to send back the whole object const updateComment = (store, comment) => -fetch('/api/v1/comments/${comment._id}/status', { +fetch(`/api/v1/comments/${comment._id}/status`, { method: 'POST', body: JSON.stringify({status: comment.status}) }) diff --git a/tests/models/comment.js b/tests/models/comment.js index b4b8a51ba..27817ccb2 100644 --- a/tests/models/comment.js +++ b/tests/models/comment.js @@ -34,11 +34,13 @@ describe('Comment: models', () => { }]; const users = [{ - id: '123', - display_name: 'Ana', + email: 'stampi@gmail.com', + displayName: 'Stampi', + password: '1Coral!' }, { - id: '456', - display_name: 'Maria', + email: 'sockmonster@gmail.com', + displayName: 'Sockmonster', + password: '2Coral!' }]; const actions = [{ @@ -54,13 +56,12 @@ describe('Comment: models', () => { }]; beforeEach(() => { - return Setting.create(settings).then(() => { - return Comment.create(comments); - }).then(() => { - return User.create(users); - }).then(() => { - return Action.create(actions); - }); + return Promise.all([ + Setting.create(settings), + Comment.create(comments), + User.createLocalUsers(users), + Action.create(actions) + ]); }); describe('#findById()', () => { @@ -74,15 +75,17 @@ describe('Comment: models', () => { describe('#findByAssetId()', () => { it('should find an array of comments by asset id', () => { - return Comment.findByAssetId('123').then((result) => { - expect(result).to.have.length(2); - result.sort((a, b) => { - if (a.body < b.body) {return -1;} - else {return 1;} + return Comment + .findByAssetId('123') + .then((result) => { + expect(result).to.have.length(2); + result.sort((a, b) => { + if (a.body < b.body) {return -1;} + else {return 1;} + }); + expect(result[0]).to.have.property('body', 'comment 10'); + expect(result[1]).to.have.property('body', 'comment 20'); }); - expect(result[0]).to.have.property('body', 'comment 10'); - expect(result[1]).to.have.property('body', 'comment 20'); - }); }); }); describe('#moderationQueue()', () => {