Jest toMatchObject with MongoDB confusing - mongodb

I have a test on my mongoose model and while one models' tests are running completly fine, another one which is basically a copy - does not work. And honestly I don't understand the problem. I tried to remove some of the properties or add hardcoded values to really match 100% exactly - but somehow i always get a similar error.
The error. What drives me crazy is that I tried to remove/add the "_id" and the "createdOn" field but at least the "_id" always appear in the error. As said above in another model the test does not complain about the "_id" because I do not validate it there...I just don't get it.
insert › Should save a channel
expect(received).toMatchObject(expected)
- Expected - 1
+ Received + 2
## -1,8 +1,9 ##
Object {
+ "_id": "5e962f1dc133d8b92891ddaf",
"createdBy": "5e962f1dc133d8b92891ddae",
- "createdOn": Anything,
+ "createdOn": 2020-04-14T21:46:05.907Z,
"hasAcceptedLegal": false,
"isActive": true,
"isPublic": false,
"members": Array [
"5e962f1dc133d8b92891ddae",
48 | expect(spy).toHaveBeenCalled();
49 |
> 50 | expect(channel).toMatchObject({
| ^
51 | name: mockName,
52 | createdBy: mockCreatedById,
53 | createdOn: expect.anything(),
at Object.it (test/model/channel.model.test.ts:50:21)
The respective model
import mongoose, { Schema, Document } from "mongoose";
import { IUser } from "./user.model";
export interface IChannel extends Document {
name: string;
createdBy: IUser["id"];
createdOn: Date;
isActive: boolean;
isPublic: boolean;
hasAcceptedLegal: boolean;
members: [IUser["id"]];
}
const ChannelSchema: Schema = new Schema({
name: { type: String, required: true },
createdBy: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
createdOn: { type: Date },
isActive: { type: Boolean, default: true },
isPublic: { type: Boolean, default: false },
hasAcceptedLegal: { type: Boolean, default: false },
members: [
{
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
],
});
export default mongoose.model<IChannel>("Channel", ChannelSchema);
The test:
it("Should save a channel", async () => {
expect.assertions(2);
let mockName = Faker.company.companyName();
let mockCreatedById = Types.ObjectId();
let mockCreatedOn = Date.now();
const channel: IChannel = new Channel({
name: mockName,
createdBy: mockCreatedById,
createdOn: mockCreatedOn,
members: [mockCreatedById],
});
const spy = jest.spyOn(channel, "save");
channel.save();
expect(spy).toHaveBeenCalled();
expect(channel).toMatchObject({
name: mockName,
createdBy: mockCreatedById,
createdOn: expect.anything(),
isActive: true,
isPublic: false,
hasAcceptedLegal: false,
members: [mockCreatedById],
});
});

just for reference, I found sth. that the returned mongoose object should be converted with ".toJSON()" and then it works, but even then it was having a problem with the "createdOn" field as it seems to be a formatted Date or sth. like this (no string, parenthesis missing).
What I finally did now was the following and now it works:
it("Should save a channel (required+defaults)", async () => {
expect.assertions(2);
let mockName = Faker.company.companyName();
let mockCreatedById = Types.ObjectId();
let mockCreatedOn = new Date();
const channel: IChannel = new Channel({
name: mockName,
createdBy: mockCreatedById,
createdOn: mockCreatedOn,
members: [mockCreatedById],
});
const spy = jest.spyOn(channel, "save");
channel.save();
expect(spy).toHaveBeenCalled();
expect(channel.toJSON()).toMatchObject({
_id: expect.anything(),
name: mockName,
createdBy: mockCreatedById,
createdOn: expect.anything(),
isActive: true,
isPublic: false,
hasAcceptedLegal: false,
members: expect.arrayContaining([mockCreatedById]),
});
});

Related

How to develop nested condition query in mongoDB

I am pretty new to mongoDb and want to apply nested query.
I have a business schema like this:
const businessSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
businessType: {
type: Schema.Types.ObjectId,
ref: "businessCategory",
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
select: false,
},
review: {
type: [reviewSchema],
},
isDeleted: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
);
Business has a review where user can do the review and reviewSchema is
const reviewSchema = new mongoose.Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: "users",
required: true,
},
rating: {
type: Number,
enum: [1, 2, 3, 4, 5],
},
reviewArray: {
type: [singleReviewSchema],
},
},
{ timestamps: true }
);
One user can do many reviews, and it has reviewArray.
ReviewArray schema is
const singleReviewSchema = new mongoose.Schema(
{
title: {
type: String,
},
description: {
type: String,
},
isDeleted: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
);
How to fetch the business with a condition business: isDeleted:false and its reviews with singleReviewSchema: isDeleted:false
I dont know your model names, so please replace path with correct names
but it might look like:
businnesModel.find({isDeleted: false})
.populate({
path: 'reviewModelName',
model: 'review',
populate: {
path: 'reviewArray',
model: 'singleReviewModelName',
match: {
isDeleted : false
}
}
})
It should provide you array of businessModel documents - even when their singleReviews array will be empty (because all of reviews are deleted, or there was zero reviews). So you have to filter it out in JS.
To avoid filtering in JS, and to do it a bit more efficient way for mongodb, you can go with aggregate instead.

Create ref to sub document's array for each property of subdocument

Model A looks like
{ a : {
step1: [{
type: { type: String },
category: { type: String },
}],
step2: [{
type: { type: String },
category: { type: String },
}]
} }
Model B which I wanted to create should contain a prop which will ref to Model A.step1 or A.step2 , trying to acheive this by following
{ progress : [
{
step: {
type: Schema.Types.ObjectId,
ref: "A.a.{What should be here}",
required: true,
},
details: { type: Schema.Types.Mixed },
}
]
}
I think you need to create Schemas for all of them :)
I would just separate the three completely - not sure if this is viable to you as the whole idea behind this is a bit mysterious, but would something like this work for you?
const stepSchema = new Schema({
type: String,
category: String,
});
const Step = mongoose.model("steps", stepSchema);
const aSchema = new Schema({
step1: [stepSchema],
step2: [stepSchema],
});
const A = mongoose.model("as", aSchema);
const progressSchema = new Schema({
a: { type: aSchema, required: true, ref: "as"},
progress: [{ type: stepSchema, required: true, ref: "steps" }],
details: Schema.Types.Mixed,
});
const Progress = mongoose.model("progresses", aSchema);

Docs not deleted although expireAt passed

I have a database of documents in production that are not getting deleted although their expireAt suggest they should be. I can't reproduce the behavior locally - when I try the document does get deleted when they should.
Example of a document not getting deleted:
_id: "ca67081..."
accessToken: "de160..."
client: "WEB"
expireAt: 2021-09-28T07:59:10.459+00:00
keepSignedIn: false
createdAt: 2021-09-28T07:14:45.460+00:00
updatedAt: 2021-09-28T07:14:45.460+00:00
__v: 0
In my API I save the session as such:
const sessionId = await saveSession({
...(keepSignedIn ? { refreshToken } : {}),
accessToken,
client: client || constants.CLIENTS.WEB,
expireAt, //taken from a jwt-token
keepSignedIn,
});
And the model:
const mongoose = require('mongoose');
const { v4: uuid } = require('uuid');
const sessionSchema = mongoose.Schema(
{
_id: {
default: uuid,
type: String,
},
accessToken: {
required: true,
type: String,
},
client: {
required: true,
type: String,
},
expireAt: {
required: true,
type: Date,
},
keepSignedIn: {
required: true,
type: Boolean,
},
refreshToken: {
required() {
return this.keepSignedIn;
},
type: String,
},
},
{ timestamps: true },
);
sessionSchema.index({ expireAt: 1 }, { expireAfterSeconds: 0 });
module.exports = mongoose.model('Session', sessionSchema);
Versions:
db.version() = '5.0.3'
"mongoose": "6.0.7",
"connect-mongo": "4.5.0",
Any ideas on what could be the cause?

Mongoose - Sub Document Array Issue

I have a "Company" Model which has a number of "Rooms".
I'm able to create the Rooms no problem, and they have a link back to the Company.
The problem is that when I get the Company, the Rooms array has no items.
I can see that the Room exists and has a link back to the Company:
{
"RoomType": 0,
"AllowedRoomRoles": [
"5f999f4f542eed1b8fce5fa9"
],
"Enabled": true,
"_id": "5fb677b453658c7070bf9433",
"RoomName": "Martins Room 2",
"RoomPin": "0001",
"Company": "5fad553db19ca100161a0d8f",
"__v": 0
}
However when I do:
var company = await CompanyModel.findById('5fad553db19ca100161a0d8f');
I get the Company back but the Rooms array has no items in it.
Here are my models:
//Room Schema:
const roomSchema = new Schema({
RoomPin: String,
RoomName: String,
RoomType: { type: RoomType, default: RoomType.MeetingRoom } ,
Company: {type: Schema.Types.ObjectId, ref: 'CompanySchema'},
AllowedRoomRoles: [{type: Schema.Types.ObjectId, ref: 'RoomRoleSchema'}],
Enabled: { type: Boolean, default: true },
}, {
collection: "room"
})
//Company Schema:
const companySchema = new Schema({
CompanyName: { type: String, required: true },
CompanyLogo: { type: String, required: true },
Enabled: { type: Boolean, default: true },
CompanyIdentifier: { type: String, required: true },
CompanyWebsite: { type: String, required: true },
Rooms: [{type: Schema.Types.ObjectId, ref: 'RoomSchema'}],
}, {
collection: "company"
})
Minor update:
Seems doing it this way around is fine?
```var rooms = await RoomModel.find({'Company': company._id.toString() });

Data is not inserted as per schema

I had defined mongoose schema and i tried to insert data into mongodb.But it is not inserted as per defined schema
export const EmpSchema: mongoose.Schema = new Schema({
name: {
type: String,
required: true
},
empNo: {
type: String,
required: true
},
skill: {
type: [String],
required: true
},
address: {
type: String,
required: true
}
}, {
_id: false,
versionKey: false,
retainKeyOrder: true
});
It is getting stored like Array elements as last field.like
name
empno
address
skill
export const EmpSchema: mongoose.Schema = new Schema({
name: {
type: String,
required: true
},
empNo: {
type: String,
required: true
},
skill: {
type: [String],
required: true
},
address: {
type: String,
required: true
}
}, {
_id: false,
versionKey: false,
retainKeyOrder: true
});
YOUR SCHEMA DEFINATION IS CORRECT PLEASE CHECK THE CONTROLLER PART WHERE YOU PUT THE INSERT QUERY . THERE MATTERS A INSERTION ORDER