Can't save to MongoDB when using beforeEach() in jestjs - mongodb

I was writing a couple of tests to my test database but I can't seem to save a document('add user to database' test) . When I run the test I get the correct length of the number of users but when I actually go to the database I only see one user document(firstUser) and cannot see the newUser document. I think it is an issue with the beforeEach function as when I remove it, everything works with the only issue being the same user being repeatedly added to the database when the test is run.
Test Code
const supertest = require('supertest')
const mongoose = require('mongoose')
const app = require('../app')
const User = require('../models/user')
const bcrypt = require('bcrypt')
const api = supertest(app)
beforeEach(async () => {
await User.deleteMany({})
const passwordHash = await bcrypt.hash('12345',10)
const firstUser = new User(
{
username: "Big Mark",
password: passwordHash,
name:"Mark"
}
)
await firstUser.save()
})
describe('user tests', () => {
// Cannot see this document in mongoDB
test('add user to database', async () => {
const newUser = {
username: 'smart',
password: 'dvsgfd',
name: 'Kevin'
}
const result = await api.post('/api/users').send(newUser).expect(201)
const length = await api.get('/api/users')
expect(length._body).toHaveLength(2)
console.log("length is", length)
})
test('see if fetching works', async () => {
const fetchedUsers = await api.get('/api/users').expect(201)
})
// test('returns 404 error if username already exists', async () => {
// })
})
afterAll(() => {
mongoose.connection.close()
},100000)
Router Code
const userRouter = require('express').Router()
const User = require('../models/user')
const bcrypt = require('bcrypt')
userRouter.post('/', async (request,response) => {
console.log("request body is", request.body)
const {username,password,name} = request.body
const alreadyThere = await User.findOne({username})
if(alreadyThere == null && username.length > 2 && password.length > 2) {
const saltRounds = 10 //How many times password it gonna get hashed (Ex: 2^n times)
const passwordHash = await bcrypt.hash(password,saltRounds)
const user = new User(
{
username,
password: passwordHash,
name
}
)
const savedUser = await user.save()
console.log("SavedUser is", savedUser)
response.status(201).json({savedUser})
}
else {
response.status(404).json({error: "Username must be unique"})
}
})
userRouter.get('/', async(request,response) => {
const users = await User.find({})
response.status(201).json(users)
})
module.exports = userRouter

Related

Mongo DB Runtime Error because of connection is closed too early

I had the same issue with the below question, and the answer(adding setTimeout()) worked for me.
MongoRuntimeError: Connection pool closed
But I can't find more information about this issue on any other documents, Youtube video, or MongoDB Guide. All of them close the connection without setTimeout function. Am I missing something? or if there is a better way to close the connection. Please advise.
const { MongoClient } = require("mongodb");
const url =
"mongodb+srv://USERNAME:PASSWORD#cluster0.feify.mongodb.net/products_test?retryWrites=true&w=majority";
const createProduct = async (req, res, next) => {
const newProduct = {
name: req.body.name,
price: req.body.price,
};
const client = new MongoClient(url);
try {
await client.connect();
const db = client.db();
const result = db.collection("products").insertOne(newProduct);
} catch (error) {
return res.json({ message: "Could not store data." });
}
setTimeout(() => {
client.close();
}, 1500);
res.json(newProduct);
};
const getProducts = async (req, res, next) => {};
exports.createProduct = createProduct;
exports.getProducts = getProducts;
Thank you
You should await the insertOne function:
const { MongoClient } = require("mongodb");
const url =
"mongodb+srv://USERNAME:PASSWORD#cluster0.feify.mongodb.net/products_test?retryWrites=true&w=majority";
const createProduct = async (req, res, next) => {
const newProduct = {
name: req.body.name,
price: req.body.price,
};
const client = new MongoClient(url);
try {
await client.connect();
const db = client.db();
const collection = db.collection("products");
const result = await collection.insertOne(newProduct);
} catch (error) {
return res.json({ message: "Could not store data." });
}
client.close();
res.json(newProduct);
};
const getProducts = async (req, res, next) => {};
exports.createProduct = createProduct;
exports.getProducts = getProducts;

How to solve client fetch error for next-auth authentication

I have created an app that connects to a mongodb cluster and stores user info. The user is then able to log in with Next-Auth functionality. The app was working just fine before deploying to Vercel. On the live site I ran into some Server Config Errors. I refractored my code yet I am still running into a few errors.
I am successfully able to connect to the database for a new user sign up.
import {
connectToDatabase,
hashedPassword,
} from "../../helper/HelperFunctions";
const isEmpty = (value) => value.trim() === "";
const isTenChars = (value) => value.trim().length >= 10;
const emailValidation = (value) => {
const pattern = /^[^ ]+#[^ ]+\.[a-z]{2,3}$/;
if (value.match(pattern)) {
return true;
} else {
return false;
}
};
export default async function handler(req, res) {
if (req.method == "POST") {
let data = req.body;
const { firstName, lastName, email, password, userName } = data;
const firstNameIsValid = !isEmpty(firstName);
const lastNameisValid = !isEmpty(lastName);
const emailIsValid = emailValidation(email);
const passwordisValid = isTenChars(password);
const userNameIsValid = !isEmpty(userName);
let userDataIsValid =
firstNameIsValid &&
lastNameisValid &&
emailIsValid &&
passwordisValid &&
userNameIsValid;
if (!userDataIsValid) {
return;
}
const client = await connectToDatabase();
const db = client.db();
const existingUser = await db.collection("users").findOne({ email: email });
if (existingUser) {
res.status(422).json({ message: "User already exists, please log in!" });
console.log("User already exists, please log in!");
client.close();
return;
}
const protectedPassword = await hashedPassword(password);
await db.collection("users").insertOne({
firstName: firstName,
lastName: lastName,
email: email,
password: protectedPassword,
userName: userName,
});
client.close();
res.status(201).json({ message: "Signed up!" });
} else {
res.status(200).json({ data: req.body });
}
}
Here is my nextauth api route
import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
// Helper Functions
import {
connectToDatabase,
comparePasswords,
} from "../../../helper/HelperFunctions";
export default NextAuth({
session: { strategy: "jwt" },
providers: [
CredentialsProvider({
async authorize(credentials) {
const client = await connectToDatabase();
const userCollection = client.db().collection("users");
const user = await userCollection.findOne({
email: credentials.email,
});
if (!user) {
client.close();
throw new Error("No user found!");
}
const isValid = await comparePasswords(
credentials.password,
user.password
);
if (!isValid) {
client.close();
throw new Error("Invalid password");
}
client.close();
if (user) {
return {
email: user.email,
};
} else {
return null;
}
},
}),
],
});
Before I deployed my site on Vercel, this was working just fine on localhost. The user should then proceed to a new page if the result of logging in has no errors.
const result = await signIn("credentials", {
redirect: false,
email: form.email,
password: form.password,
});
if (!result.error) {
console.log(true);
router.replace("/suggestions");
} else {
console.log(result.error);
setLoginResult(result.error);
}
If you see CLIENT_FETCH_ERROR make sure you have configured the NEXTAUTH_URL environment variable.
when developing you set it to localhost:3000, now you need to set that to your deployed url.

Why my firebase update function is not always working?

Im trying to figuring out why my firebase funtion for cheat is always creating but when like open the chat where it call create function immediately send a message the message will not be saved, because my function is not ready so how can I sole this?.
Heres my function .
export const onConversationCreated = functions.firestore.
document("Conversations/{conversationID}").onCreate((snapshot, context) => {
const data = snapshot.data();
const conversationID = context.params.conversationID;
if (data) {
const members = data.members;
for (let index = 0; index < members.length; index++) {
const uid = members[index];
const remainingUserIDs = members.filter((u: string) => u !== uid);
remainingUserIDs.forEach((m: string) => {
return admin.firestore().
collection("profile").doc(m).get().then((_doc) => {
const userData = _doc.data();
if (userData) {
return admin.firestore().collection("profile")
.doc(uid).collection("Conversations").doc(m).create({
"conversationID": conversationID,
"url": userData.url,
"name": userData.username,
"unseenCount": 0,
});
}
return null;
}).catch(() => {
return null;
});
});
}
}
return null;
});
export const onConversationUpdated = functions.firestore
.document("Conversations/{conversationID}").onUpdate((change, context) => {
const data = change?.after.data();
if (data) {
const members = data.members;
const lastMessage = data.messages[data.messages.length - 1];
for (let index = 0; index < members.length; index++) {
const uid = members[index];
const remainingUserIDs = members.filter((u: string) => u !== uid);
remainingUserIDs.forEach((u: string) => {
return admin.firestore().collection("meinprofilsettings")
.doc(uid).collection("Conversation").doc(u).update({
"lastMessage": lastMessage.message,
"timestamp": lastMessage.timestamp,
"type": lastMessage.type,
"lastmessageuid": lastMessage.senderID,
"unseenCount": admin.firestore.FieldValue.increment(1),
});
});
}
}
return null;
});
So again creating is correct working. its just need some time . And when I immediately when calling create function write a message and send it this message will not be saved until the create function is finished then I have to send again the message
enter image description here
The reason for your bug is that you don't await the execution of your async task correctly. Unfortunately the forEach doesn't support async so we need to revrite your code to something like this:
export const onConversationCreated = functions.firestore
.document("Conversations/{conversationID}")
.onCreate((snapshot, context) => {
const data = snapshot.data();
const promises: Promise<any>[] = [];
const conversationID = context.params.conversationID;
if (data) {
const members = data.members;
for (let index = 0; index < members.length; index++) {
const uid = members[index];
const remainingUserIDs = members.filter((u: string) => u !== uid);
remainingUserIDs.forEach((m: string) => {
promises.push(
admin
.firestore()
.collection("profile")
.doc(m)
.get()
.then((_doc) => {
const userData = _doc.data();
if (userData) {
return admin
.firestore()
.collection("profile")
.doc(uid)
.collection("Conversations")
.doc(m)
.create({
conversationID: conversationID,
url: userData.url,
name: userData.username,
unseenCount: 0,
});
}
return null;
})
);
});
}
}
return Promise.all(promises);
});
export const onConversationUpdated = functions.firestore
.document("Conversations/{conversationID}")
.onUpdate((change, context) => {
const data = change?.after.data();
const promises: Promise<any>[] = [];
if (data) {
const members = data.members;
const lastMessage = data.messages[data.messages.length - 1];
for (let index = 0; index < members.length; index++) {
const uid = members[index];
const remainingUserIDs = members.filter((u: string) => u !== uid);
remainingUserIDs.forEach((u: string) => {
promises.push(
admin
.firestore()
.collection("meinprofilsettings")
.doc(uid)
.collection("Conversation")
.doc(u)
.update({
lastMessage: lastMessage.message,
timestamp: lastMessage.timestamp,
type: lastMessage.type,
lastmessageuid: lastMessage.senderID,
unseenCount: admin.firestore.FieldValue.increment(1),
})
);
});
}
}
return Promise.all(promises);
});
We use Promise.all() to even run all your async tasks in parallel to finish the function faster and save on execution time.

FetchError: invalid json response body at http://localhost:3000/api/products/6092ca3460fc67315178f2fa reason: Unexpected token < in JSON at position 0

I am trying to fetch data from MongoDB, but apparently, it gives an error
FetchError: invalid json response body at
http://localhost:3000/api/products/6092ca3460fc67315178f2fa reason: Unexpected token < in JSON at position 0
const defaultEndpoint = 'http://localhost:3000/api/products/';
export const getStaticPaths = async () => {
const res = await fetch(defaultEndpoint);
const data = await res.json();
const paths = data.map (product => {
return {
params: { id: product._id.toString() }
}
})
return {
paths,
fallback: false
}
}
export const getStaticProps = async (context) => {
const id = context.params.id;
const res = await fetch ('http://localhost:3000/api/products/' + id);
const data = await res.json ();
return {
props: {product: data}
}
}
const Details = ({product}) => {
return (
<div>
<h1>{product.title}</h1>
</div>
)
}
export default Details
API Endpoint which is perfectly working on http://localhost:3000/api/products
import { connectToDatabase } from "../../util/mongodb";
export default async (req, res) => {
const { db } = await connectToDatabase();
const products = await db.collection("products").find({}).toArray();
res.json(products);
};

Cannot Make POST or GET Request with Express API

I am trying to test my API and I am not sure what I am doing wrong. The only error I am getting is cannot post. After some research, I am suspecting something wrong with my controller class.
This is my first real large project and I have created an API similar to this just with mongo but this is my first time using seqealize, and MySQL (with node).
The api/student/register router just won't post, but when I remove the controller and but a function that returns just plain JSON it works, that is why I really do believe it has something to do with my controller class.
contoller/student.controller.js
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
require('dotenv').config();
const secret = process.env.SERCRET_KEY;
const db = require('../models/index.js');
const Student = db.Student;
class StudentContoller {
static registerStudent(req, res) {
try {
let {
first_name,
last_name,
email,
password,
phone,
shirt_size,
grade,
teacher,
} = req.body;
Student.findAll({
where: {
email : email,
is_Archived: false,
}
})
.then(result => {
if (result.length > 0){
res.status(400).json({ message: 'Email is already registerd'});
} else {
let hashPassword = bcrypt.hashSync(password, 10);
let newStudent = {
first_name,
last_name,
email,
password: hashPassword,
phone,
shirt_size,
grade,
teacher,
school_id,
}
Student.Create(newStudent)
.then(data => {
if(data){
res.status(201).json({message: 'Student Created', student: data })
}
})
.catch(err => res.json({error : err.message}));
}
})
} catch (e) {
res.status(500).json({error: 'error caused in regiserted controller'})
}
}
static async login(req, res) {
let { email, password, } = req.body;
await Student.findAll({
where: {
email: email,
is_Archived: false,
}
})
.then(student => {
if(student.length === 0){
res.status(400).json({ message: 'Sorry, account does not exsist'})
} else {
let passwordIsValid = bcrypt.compareSync(req.body.password, student[0].password.trim());
if (passwordIsValid) {
let studentDetails = {
id: student[0].dataValues.id,
first_name: student[0].first_name,
last_name: student[0].last_name,
}
let token = jwt.sign({
student: studentDetails,
}, secret, {
expiresIn: '1d'
});
res.status(200).json({
success: true,
student: studentDetails,
message: 'Login successfull',
token: token,
});
} else {
res.status(401).json({
success: false,
message: 'Login failed',
});
}
}
})
.catch(err => res.status(500).json({error: err.message}));
}
static async updateStudent(req, res) {
try {
const {
first_name,
last_name,
email,
password,
phone,
shirt_size,
grade,
teacher,
} = req.body;
let hashpassword = bcrypt.hashSync(password, 10);
let updateStudent = {
first_name,
last_name,
email,
password : hashpassword,
phone,
shirt_size,
grade,
teacher,
}
Student.update(updateStudent, {
where: {
id: req.params.id
}
})
.then(response => {
res.status(200).json({success: true, message: "Student updated successfully"})
})
.then(err => res.json({err: err.message}));
} catch (e) {
res.status(500).json({ error: e});
}
}
static async archiveStudent(req, res) {
try{
let id = req.params.id
await Student.findAll({
where: {id: id}
})
.then(result => {
if (result.length == 1){
Student.update(
{is_Archived: true},
{ where: {id: id}}
)
}
})
} catch (e) {
res.status(500).json({ error: e});
}
}
}
module.exports = StudentContoller;
student.routs.js
const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');
const checkJWT = require('../middlewares/check-token');
const StudentController = require('../controllers/student.controller');
//
//
router.get('student/login', StudentController.login);
router.post('student/register', checkJWT,StudentController.registerStudent);
module.exports = router;
Sever js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const morgan = require('morgan');
const helmet = require('helmet');
const app = express();
// Middlewars
app.use(cors()); // Enable Cors
app.use(morgan('dev')); // Enable Logging
app.use(helmet()); // Enables Security Headers
app.use(bodyParser.json()); // Parses requrests of type application/json
app.use(bodyParser.urlencoded({ extended: true})); // Parses request of application/x-www-form-urlencode
//Add database connection
const db = require('./models');
//db.sequelize.sync().then(()=> initial())
// Routing
app.get('/', (req, res)=> res.json({message: "Base API URL"}));
const studnetRoute = require('./routes/student.routes');
app.use('/', studnetRoute);
const PORT = process.env.PORT || 3300;
app.listen(PORT, () => console.log(`Server Running on port ${PORT}`));
you can do like this in your router file .
router.route('student/login')
.get(StudentController.login);
router.route('student/register')
.post(checkJWT, StudentController.registerStudent);