TypeScript, Mongoose, Jasmine - requiring schemas - node.js

I've been struggling for days with how to build a proper test workflow. I realise that tests should mock database dependencies but the case is that I need to test the whole process, together with real db queries.
In the following code I need to require my model so that I could perform operations on db such as deleting test data and pushing test data.
The questions are:
What is the proper way of referencing the schema/model?
Or is there a way of writing Jasmine tests using typescript?
The code does not work, as it says that BucketConfigS.remove is not a function:
'use strict';
let BucketConfigS = require('../dist/app/BucketConfig/BucketConfigSchema');
describe('Bucket config collection:', () => {
describe('GetAll service -', () => {
describe('Given that there are no configs', function () {
beforeEach(done => {
done();
});
afterEach(done => {
BucketConfigS.remove({}, done);
done();
});
it('should return an empty array', function () {
// test case
});
});
});
});
I also tried the following require line:
let BucketConfigS = require('../dist/app/BucketConfig/BucketConfigSchema').default;
However it brokes the whole test suite (no test results are written out).
The schema file looks like this:
"use strict";
var DataAccess_1 = require("./../common/DataAccess");
var mongoose = DataAccess_1.DataAccess.mongooseInstance;
var mongooseConnection = DataAccess_1.DataAccess.mongooseConnection;
var BucketConfigSchema = (function () {
function BucketConfigSchema() {
}
Object.defineProperty(BucketConfigSchema, "schema", {
get: function () {
var schema = mongoose.Schema({
AppName: {
type: String,
required: true
},
Platform: {
type: String,
required: true
},
Segment: {
type: String,
required: true
},
UpdateTime: {
type: Date,
default: Date.now
}
});
return schema;
},
enumerable: true,
configurable: true
});
return BucketConfigSchema;
}());
var BucketConfig = mongooseConnection.model("BucketConfig", BucketConfigSchema.schema);
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = BucketConfig;
This is a compilation result of:
import { DataAccess } from "./../common/DataAccess";
import { IBucketConfig } from "./IBucketConfig";
let mongoose = DataAccess.mongooseInstance;
let mongooseConnection = DataAccess.mongooseConnection;
class BucketConfigSchema {
static get schema() {
let schema = mongoose.Schema({
AppName: {
type: String,
required: true
},
Platform: {
type: String,
required: true
},
Segment: {
type: String,
required: true
},
UpdateTime: {
type: Date,
default: Date.now
}
});
return schema;
}
}
let BucketConfig = mongooseConnection.model<IBucketConfig>("BucketConfig", BucketConfigSchema.schema);
export default BucketConfig;

Not sure why, but
let BucketConfigS = require('../dist/app/BucketConfig/BucketConfigSchema').default;
started working...

Related

How to stub query helper method Mongoose?

I'm using Sinon to test my Express/Typescript application. In Mongoose I have models with query helper methods to make query chains.
I have this schema
export const articuloSchema = new Schema({
_id: { type: String, default: v4 },
numeroInventario: { type: String, required: true, unique: true },
descripcion: { type: String, trim: true },
}, {
versionKey: false,
query: {
paginate(page: number, limit: number) {
return this.skip(page - 1).limit(limit);
}
}
});
export type ArticuloType = InferSchemaType<typeof articuloSchema>;
export const Articulo = mongoose.model('Articulo', articuloSchema);
and I want to fake my query helper method with Sinon like this
const getMultipleArticulos (): ArticuloType[] => {
const arr:ArticuloType = []
for (let i = 0; i < 5; i++) {
arr.push({
numeroInventario: i,
descripcion: 'string'
})
}
return arr;
}
it('Should return a list of items that belong to a user', function (done) {
const paginateFake = sinon.fake.resolves(getMultipleArticulos());
sinon.replace(Articulo, 'paginate', paginateFake);
chai.request(app)
.get(`/api/v1/users/${userId}/articulos`)
.end((err, res) => {
expect(err).to.be.null;
expect(res).to.have.status(200);
// more expects
done();
});
});
The problem is that I cannot stub those methods, it says that it can't stub non existent property of 'paginate' (which is the query helper method I added to my model).

MongoDB UpdateMany Method

how can i use the update Many Method inside my code?. Right now this code will insert the data over and over inside the table, i need to make it so it will update the old data and add new data if new data is available in the third party api. I'm new to MongoDB any help will be much appreciate it! thanks.
cron.schedule('0 0 * * *', async () => {
const postSchema = new mongoose.Schema({
id: {
type: Number,
required: true
},
name: {
type: String,
required: true
},
status: {
type: String,
required: false
},
});
const Post = mongoose.model('players', postSchema);
async function getPosts() {
const getPlayers = await fetch("http://localhost:3008/api/players");
const response = await getPlayers.json();
for( let i = 0;i < response.players.length; i++){
const post = new Post({
id: response.players[i]['id'],
name: response.players[i]['name'],
status: response.players[i]['status'],
});
post.save();
}
}
console.log("Task submitted successfully")
await getPosts();
});
what i was thinking
const post = await Post.updateMany({
id: response.players[i]['id'],
name: response.players[i]['name'],
status: response.players[i]['status'],
});
I believe you can use bulkWrite and inside bulkWrite you can write updateOne operation with the upsert flag set to true
You can construct an array of operations as follows
let operations = [];
for (let player of players) {
operations.push({
updateOne: {
filter: { id: player.id },
update: { $set: { name: player.name } },
upsert: true,
}
})
}
And finally you can make a call to bulkWrite. Please read the documentation for the operations above

Mongoose post save hook, modifiedPaths() is always empty

Our aim is to have a post hook in place where we can track changed fields.
Model file:
const VariationSchema = new Schema({
_id: {
type: String,
},
title: {
type: String,
},
desc: {
type: String,
},
});
VariationSchema.post('save', async function (doc) {
console.log(doc.modifiedPaths());
});
const VariationModel = mongoose.model('variation', VariationSchema);
module.exports = {
VariationModel,
VariationSchema,
};
Service file:
const variationDocument = await VariationModel.findById(variationId).select({});
variationDocument.desc = (Math.random() + 1).toString(36).substring(7);
await variationDocument.save();
return variationDocument.toJSON();
No matter what we do, doc.modifiedPaths() is always empty. Please help

Mongoose set empty array when update model

I have a problem with mongoose. I use MEAN stack.
I have an House Schema with some fields. When I update the house (with save method) mongoose update all fields but set an array in nested object empty. Why?
HOUSE SCHEMA
const mongoose = require('mongoose');
const posSchema = require('./pos');
const reviewSchema = require('./reviews');
const roomSchema = require('./rooms');
const contactSchema = require('./contacts');
const nearSchema = require('./nears');
const houseSchema = new mongoose.Schema({
title: { type: String, required: true },
description: { type: String, required: true },
shared: {
description: { type: String, required: true },
photos: { type: [String], required: true }
},
// OTHER FIELDS
}, { timestamps: true });
mongoose.model('House', houseSchema);
UPDATE FUNCTION
House.findById(body.house._id, "-__v", async (err, house) => {
if (err) { return res.status(400).json(err); }
else if (!house) { return res.status(400); }
house.title = body.house.title;
house.totalSize = parseInt(body.house.totalSize.toString());
house.bathrooms = parseInt(body.house.bathrooms.toString());
house.totalBeds = parseInt(body.house.totalBeds.toString());
house.description = body.house.description;
house.totalFreeBeds = parseInt(body.house.totalFreeBeds.toString());
house.minPrice = parseFloat(body.house.minPrice.toString()).toFixed(2);
house.type = body.house.type;
house.level = body.house.level;
house.top_floor = body.house.top_floor;
house.rooms = body.house.rooms;
house.checkboxes = body.house.checkboxes;
house.contacts = body.house.contacts;
house.pos = body.house.pos;
house.save(err => {
if (err) { console.log(err); return res.status(400).json(err); }
return res.status(200).json({ status: 200, remove: remove });
});
});
Before updating house.shared.photos is ["foo", "bar"]
After is [].
Why?
You are not setting the house.shared anywhere.
Try adding house.shared = body.house.shared before save call.

Refactoring several mongoose models to similar collections

I have several collections that have the same documents type, except for the language.
Let's say imagesES, imagesEN, imagesFR, and so on....
I just thought about definig just one schema, but also one model that get the proper collection with a parameter:
var mongoose = require('mongoose')
var Schema = mongoose.Schema
let authorSchema = require('./Authors').authorSchema
const imageSchema = new Schema({
authors: [authorSchema],
status: Number, // published (1), unpublished (0)
created: { type: Date, default: Date.now },
lastUpdated: { type: Date, default: Date.now },
license: {
type: String,
enum: ['Creative Commons BY-NC-SA'], //just one license right now
default: 'Creative Commons BY-NC-SA'
},
downloads: {
type: Number,
default: 0
},
tags: [String]
})
module.exports = locale => {
return mongoose.model('Image', imageSchema, `image${locale}`)
}
However in the controller I should require the model inside the controller (when I know the locale):
getImageById: (req, res) => {
const id = req.swagger.params.id.value
const locale = req.swagger.params.locale.value
const Images = require('../models/Images')(locale)
Images.findOne({_id: id}).lean().exec( (err, image) => {
I'm not sure if this is the proper way as each request I get I have to require the model module (syncronously) or should I require all the different models previous to the use in the function.
const locales = ['es', 'en', 'fr']
const Images = []
locales.map(locale=>Images[locale] = require('../models/Images')(locale))
getImageById: (req, res) => {
const id = req.swagger.params.id.value
const locale = req.swagger.params.locale.value
Images[locale].findOne({_id: id}).lean().exec( (err, image) => {
Finally this is how I resolved it. Where it says Pictograms, could be Images as in the question
const setPictogramModel = require('../models/Pictograms')
const languages = [
'ar',
'bg',
'en',
'es',
'pl',
'ro',
'ru',
'zh'
]
const Pictograms = languages.reduce((dict, language)=> {
dict[language]= setPictogramModel(language)
return dict
}, {})
module.exports = {
getPictogramById: (req, res) => {
const id = req.swagger.params.idPictogram.value
const locale = req.swagger.params.locale.value
// Use lean to get a plain JS object to modify it, instead of a full model instance
Pictograms[locale].findOne({id_image: id}).exec( (err, pictogram) => {
if(err) {
return res.status(500).json({
message: 'Error getting pictograms. See error field for detail',
error: err
})
}
if(!pictogram) {
return res.status(404).json( {
message: `Error getting pictogram with id ${id}`,
err
})
}
return res.json(pictogram)
})
},

Resources