Chai test a mongoose Promise - express

I am really struggling with Chai testing especially now that I have started using mongoose Promises and Async/Await.
Apologies in advance for the long post, but each part is necessary to form a bigger picture.
This is a Message Board app (FCC for anyone familiar).
3 models for Board, Thread, Reply with Ref.
My Thread Schema needs a Board _id when created.
In my Chai test I am hardcoding a message Board _id. This Thread Object does get created in the database but the board field is not added and I can't understand why.
The second issue is that the CHAI test does not return a body in the response - it returns {}. So I have nothing to run asserts against. The newThread function does return a data object from the new_thread.save(function (err, data), as I had the new _id to the Board/threads sub-document array
Thread Schema
const ThreadSchema = new Schema({
"text": {'type': String},
"delete_password": { 'type': String,'select': false},
"created_on": {'type': Date, 'default': new Date()},
"bumped_on": {'type': Date, 'default': new Date()},
"reported": {'type': Boolean,'select': false,'default': false},
"replycount":{'type': Number,'default': 0},
"board": {'type': mongoose.Schema.Types.ObjectId,'ref': 'Board'},
"replies": [{'type': mongoose.Schema.Types.ObjectId,'ref': 'Reply'}]
});
Create a Thread function
this.newThread = function(req, res) {
let threadText = req.body.text;
let passwordText = req.body.delete_password;
let boardTitle = req.params.board;
const new_thread = new models.Thread({
text: threadText,
delete_password: passwordText,
board:currentBoardId //currentBoardId defined & set earlier
});
new_thread.save(function (err, data) {
models.Board
.findOne({ board_title: boardTitle})
.then(board => {
if (board == null) {
res.json({ message: "Cannot find board named, " + boardTitle });
} else {
board.threads.push(data._id);
board.save(function (err){
if (err) {console.log(err);}
res.end();
});
}
})
.catch(err => {
return res.status(500).json({ message: err.message })
})
})
};
CHAI test
test('Every field filled in', function(done) {
chai.request(server)
.post('/api/threads/test')
.send({
board: ObjectId('5d8f748a1d788a3be2b9a7b7'), // board test _id Never gets added to database
text: 'POST - new thread - test thread text',
delete_password: 'password'
})
.end(function(err, res){
expect(err).to.be.null;
assert.equal(res.status, 200);
console.log(res.body); // returns {}
done();
});
});

Related

mongoose static methods not showing model document in console

I have a database of jobs, I am trying to create a mongoose static method to toggle a field on specific jobs to true or false. I have been trying to figure out mongoose methods and have hit a road block just trying to console.log a document to screen. I am just trying to use this.find to get a document from my mongo db whenever I run the static method but it seems to not work.
Model
const mongoose = require('mongoose');
const proxyModel = require('./proxyModel');
const User = require('./userModel');
const pendingJobModel = new mongoose.Schema({
date:{type:String, required: true, unique:true},
startTime:{type:String, required: true},
endTime:{type:String, required: true},
courseList:{type:[String], required: true},
member:{type:String},
clubUsername:{type:String, required: true},
clubPassword:{type:String, required: true},
proxy:{type:Boolean, required: true, default:false},
active:{type:Boolean, required: true, default:false},
},{collection:'pendingjobs'})
// pendingJobModel.statics.findByIdAndToggleActive = function(id, callback){
// this.find({_id:id}, callback)
// console.log('tried')
// }
pendingJobModel.static('findByID', function(id){
this.find({_id:id}, function(err, resp){
if(err){
console.log(err)
}else{
console.log(resp)
}
})
})
module.exports = mongoose.model('PendingJob', pendingJobModel)
Calling static method
async function startBot(){
console.log("[+] Bot Starting...")
const callback = function(err, resp){
if(err){
console.log(err)
}else{
console.log(resp)
}
}
PendingJob.findByID('63169a53a8944fd098f3d88b')
Why cant I see my document in console when I run the model static method??

Why does the 1st middleware of the array needs to call next() but the 2nd does not?

I am new in web development. While following the Node Express Mozilla Tutorial, I came accross this array of controller middlewares that made me confused regarding the use of next().
This array has 3 middlewares:
One for making sure a certain form parameter is in array format
One for doing validation and sintizaiton of the for parameters
One for processing and giving a response to the request
My doubt is: why does the 1st one calls next() but the 2nd does not?
Below the block of code with the array of controller middlewares:
// Handle book create on POST.
exports.book_create_post = [
// Convert the genre to an array.
(req, res, next) => {
if(!(req.body.genre instanceof Array)){
if(typeof req.body.genre ==='undefined')
req.body.genre = [];
else
req.body.genre = new Array(req.body.genre);
}
next();
},
// Validate and sanitise fields.
body('title', 'Title must not be empty.').trim().isLength({ min: 1 }).escape(),
body('author', 'Author must not be empty.').trim().isLength({ min: 1 }).escape(),
body('summary', 'Summary must not be empty.').trim().isLength({ min: 1 }).escape(),
body('isbn', 'ISBN must not be empty').trim().isLength({ min: 1 }).escape(),
body('genre.*').escape(),
// Process request after validation and sanitization.
(req, res, next) => {
// Extract the validation errors from a request.
const errors = validationResult(req);
// Create a Book object with escaped and trimmed data.
var book = new Book(
{ title: req.body.title,
author: req.body.author,
summary: req.body.summary,
isbn: req.body.isbn,
genre: req.body.genre
});
if (!errors.isEmpty()) {
// There are errors. Render form again with sanitized values/error messages.
// Get all authors and genres for form.
async.parallel({
authors: function(callback) {
Author.find(callback);
},
genres: function(callback) {
Genre.find(callback);
},
}, function(err, results) {
if (err) { return next(err); }
// Mark our selected genres as checked.
for (let i = 0; i < results.genres.length; i++) {
if (book.genre.indexOf(results.genres[i]._id) > -1) {
results.genres[i].checked='true';
}
}
res.render('book_form', { title: 'Create Book',authors:results.authors, genres:results.genres, book: book, errors: errors.array() });
});
return;
}
else {
// Data from form is valid. Save book.
book.save(function (err) {
if (err) { return next(err); }
//successful - redirect to new book record.
res.redirect(book.url);
});
}
}
];

Can't find mongoose validation error within express error object

I'm using mongoose js and trying to insert a new document into a mongodb. I'm passing the data object to express using Axios. When I get a validation error, the express server identifies the problem very easily. It returns an error productCode: ValidatorError: Path 'productCode' is required.
The error that is returned to express is a huge object and I can't find the validation error in it. Can someone tell me what I'm missing in my catch error?
UPDATED
Path: Express axios Post catch error
const product = {
productCode: '1a',
productName: 'product x'
};
async function createProduct(product) {
axios
.post(
`http://localhost:3000/myroute`,
product
)
.then((res) => {
console.log('res', res.data.productCode);
return res.data.productCode;
})
.catch((error) => {
// console.log('err', err.response);
if (error.response) {
/*
* The request was made and the server responded with a
* status code that falls out of the range of 2xx
*/
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
/*
* The request was made but no response was received, `error.request`
* is an instance of XMLHttpRequest in the browser and an instance
* of http.ClientRequest in Node.js
*/
console.log(error.request);
} else {
// Something happened in setting up the request and triggered an Error
console.log('Error', error.message);
}
// console.log(error);
});
}
UPDATE
Path: Server
const product = new Product(req.body);
product
.save()
.then((result) => {
res.status(200).json(result);
})
.catch((err) => {
console.log('err', err);
res.status(500).json({ error: err.reason });
});
Path: Model
var productSchema = new Schema(
{
productCode: {
type: String,
required: true
},
productName: { type: String, default: '' }
},
{ collection: 'products', timestamps: true, versionKey: false }
);

How to construct mongoose Find from url query string?

I have an object schema as
var book = new Schema ({
name: {type: String},
code: {type: String}
});
Sample content is
{
name: "Jungle Book",
code: "Jungle"
}
{
name: "Java Book",
code: "Java"
}
I have a http GET request to my Express server as
http://localhost:port/api/book?name=Jung
This API invocation is expected to return all books that have the name starting with "Jung"
In my experess implementation, I have
exports.getBooks = function(req, res, next) {
var query = require('url').parse(req.url, true).query;
Book.find({"name": /^query.name/}).exec(function(err, books){
if(err){ res.status(400).json(
{ success: false,
message: 'Error processing request '+ err
});
}
res.status(201).send(books);
});
};
I receive an empty array, the search is not successful. However when I do an exact match like below its successful.
Book.find({"name": query.name})....
Pleases suggest how to perform a 'find' with 'starts with' operation from a query parameter.
Mongoose supports regexp, so an other option is:
let regexp = new RegExp("^" + query.name);
Book.find({ name: regexp });
Or try:
let data = {"name": /^query.name/};
Book.find(data, function(err, books) {
if(err) {
res.status(400).json({
success: false,
message: 'Error processing request ' + err
});
};
res.status(201).send(books);
});

How to get socket.io to recognize when model has been updated

I'm relatively new to MEAN/Mongoose/socket.io and am missing something that seems like it'd be basic, but regardless, it's keeping me from moving forward.
I'm keeping track of a group's meetings and have the following Schema:
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ScheduleSchema = new Schema({
description: String,
meetingPlace: String,
office: String,
start: { type: Date, default: Date.now },
end: { type: Date, default: Date.now },
participants: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
author: {
type: Schema.Types.ObjectId,
ref: 'User'
},
timestamp: { type: Date, default: Date.now },
active: Boolean
});
ScheduleSchema.statics = {
load: function(cb) {
this.find({})
.populate({path:'participants'})
.exec(cb);
}
};
module.exports = mongoose.model('Schedule', ScheduleSchema);
When I create a new item in the Schedule model, this code sets it in motion (from a client-side controller):
$http.post('/api/schedules', { description: info.description, participants: attendees, meetingPlace: info.meetingPlace, office: info.office, start: info.start, end: info.end, timestamp: new Date(), active: true });
And because of this code:
'use strict';
var express = require('express');
var controller = require('./schedule.controller');
var router = express.Router();
router.get('/', controller.index);
router.get('/:id', controller.show);
router.post('/', controller.create);
router.put('/:id', controller.update);
router.patch('/:id', controller.update);
router.delete('/:id', controller.destroy);
module.exports = router;
I think the request is being routed to controller.create, which is the following:
'use strict';
var _ = require('lodash');
var Schedule = require('./schedule.model');
// Get list of schedules
exports.index = function(req, res) {
Schedule.load(function (err, schedules) { /*.find*/
if(err) { return handleError(res, err); }
return res.json(200, schedules);
});
};
// Get a single schedule
exports.show = function(req, res) {
Schedule.findById(req.params.id, function (err, schedule) {
if(err) { return handleError(res, err); }
if(!schedule) { return res.send(404); }
return res.json(schedule);
});
};
// Creates a new schedule in the DB.
exports.create = function(req, res) {
var promise = Schedule.create(req.body, function(err, schedule) {
if(err) { return handleError(res, err); }
return res.json(201, schedule);
});
};
// Updates an existing schedule in the DB.
exports.update = function(req, res){
var updatedMeeting = req.body;
var id = updatedMeeting._id;
delete updatedMeeting._id;
Schedule.update({_id : id}, updatedMeeting, { }, function (err, numberAffected, raw) {
if (err) return res.json(500, err);
updatedMeeting._id = id;
return res.json(201, updatedMeeting);
});
};
// Deletes a schedule from the DB.
exports.destroy = function(req, res) {
Schedule.findById(req.params.id, function (err, schedule) {
if(err) { return handleError(res, err); }
if(!schedule) { return res.send(404); }
schedule.remove(function(err) {
if(err) { return handleError(res, err); }
return res.send(204);
});
});
};
function handleError(res, err) {
return res.send(500, err);
}
After exports.create is run, and I don't know how, but something sends it over to here:
'use strict';
var Schedule = require('./schedule.model');
exports.register = function(socket) {
Schedule.schema.post('save', function (doc) {
onSave(socket, doc);
});
Schedule.schema.post('remove', function (doc) {
onRemove(socket, doc);
});
Schedule.schema.post('update', function (doc) {
onUpdate(socket, doc);
});
}
function onSave(socket, doc, cb) {
console.log('**********onSave**********');
Schedule
.findOne({ _id : doc._id })
.populate('participants')
.exec(function (err, event) {
if (err) return handleError(err);
socket.emit('schedule:save', event);
});
}
function onRemove(socket, doc, cb) {
socket.emit('schedule:remove', doc);
}
function onUpdate(socket, doc, cb) {
console.log('**********onUpdate**********');
socket.emit('schedule:update', doc);
}
and the line socket.emit('schedule:save', event); is called.
So that's all great, but when I update the Schedule model, I can't get socket to emit 'schedule:update' because it never gets to the "onUpdate" function in the code snippet just above this.
Starting from the client-side call:
$http.patch('/api/schedules/' + info._id, { _id: info._id, description: info.description, participants: attendees, meetingPlace: info.meetingPlace, office: info.office, start: info.start, end: info.end, timestamp: new Date(), active: true });
and the router sends that to exports.update, the model is updated, but socket doesn't emit anything, so all the clients connected see the old schedule information.
How and where can I relay to socket.io that the model has been updated? I think what I'm missing is how Schedule.schema.post('some action... gets called within the exports.register function, but I've been looking at this for a while and could use a hand - thanks.