My application code to test:
create(params) {
let result = {};
try {
result = await this.db.query('/* some query */');
} catch (e) {
throw new Error('Error creating User', e);
}
return this._getById(result.insertId);
}
I have the _getById method in the same class which does exactly what it says...
And my current test (running through Ava):
test('it should create a user', async t => {
const db = mock({
query: () => {
},
});
const userObj = {
// some params
};
const user = new Users({
db: db.object,
});
const call = {
request: userObj,
};
const result = await user.create(call);
// test?
});
If I try and test anything based off of the result variable, ie. the newly created User, I receive the error "Cannot read property 'insertId' of undefined". What is my best option with Sinon to test that this create method will return a newly created "user"?
I think that you have the "Cannot read property 'insertId' of undefined" because the following mock doesn't return something
const db = mock({
query: () => {
},
});
If you return something like that
const db = mock({
query: () => {
return {
username: "username"
}
},
});
and in the test the result will have the value and you should be able to use expect to check if you have the expected result:
{
username: "username"
}
The problem in this specific test starts when the mock does not return something and also calling mock overrode the value that you assigned here
let result = {};
Related
I have the following API to get the user's data based on a [pid]:
import prisma from "../../../../lib/prisma";
// Master read function - API route includes profile, subnodes and contents
async function getProfile(req, res) {
const profilePID = await prisma.profileNode.findUnique({
where: {
userName: req.query.pid
},
include: {
subnode: {
include: {
content: true,
}
},
},
})
// Integer for how many accounts the current user is following
const followingCount = await prisma.follower.count({
where: {
followerId: profilePID.userId
},
select: {
profileId: true
}
})
// integer for how many accounts the current user is being followed
const followerCount = await prisma.follower.count({
where: {
profileId: profilePID.userId
},
select: {
profileId: true
}
})
// detailed profile info of the people you are following
const following = await prisma.follower.findMany({
where: {
followerId: profilePID.userId,
NOT: {
profileId: null,
}
},
include: {
followees: true
}
})
// aggregate all data queries into one
const aggregatedData = {
profilesYouAreFollowing: followingCount.profileId,
yourProfileFollowers: followerCount.profileId,
followingData: following,
profileData: profilePID
}
if (aggregatedData) {
res.status(200).json(aggregatedData)
} else {
return res.status(500).json({ error: 'Something went wrong' })
}
}
export default async function handler(req, res) {
// commit to the database
if (req.method === 'GET') {
return getProfile(req, res)
}
}
As you would observe, the first request is to find the profileNode using a [pid] - which is a string like localhost:3000/user/ABC. Then I would get the userId (an integer) within the profileNode. The userId is then used in the rest of the prisma query to the database for followers and followers' details since all the ids are stored as integer.
I used SWR for client-side fetch, which is all fine but I noticed that while fetching, it will cause an error 500 before the data is fully fetched.
Now, while this does not hinder data fetching for presenting data to the client since SWR takes care of error handling and continue fetching until all the data is acquired, however, it does throw an error on other code like JSON.parse, as the error 500 has passed an undefined value to it - thus throwing an error.
Any tips or tricks as to how to get rid of the error 500?
Added client side code below:
const { data, error } = useSWR(`/api/profiles/read/${slug}`, fetcher)
const [subnodes, setSubnodes] = useState();
// authentication using next-auth session and fetched client-side userId
// compare equality - if equal, set Auth to true and show edit components
useEffect(() => {
async function fetchingData() {
setLoading(true);
// session
const session = await getSession();
let sessionUserId;
if (!session) {
sessionUserId = null;
} else {
sessionUserId = session.user.id;
}
// client
const clientId = await data?.profileData.userId;
// authentication check
if (sessionUserId !== clientId) {
setAuth(false);
} else {
setAuth(true);
}
async function asyncStringify(str) {
return JSON.parse(JSON.stringify(str));
}
const awaitJson = await asyncStringify(data?.profileData.subnode)
setSubnodes(awaitJson);
setLoading(false)
}
fetchingData();
}, []);
I got my MySQL data printed in console.log() as console.log(result) and it works fine.
Now I need to use this result to define variable MyPlan in a different function.
This is how I got the results from MySQL db:
function load() {
var MySQL = SQL.createConnection(credentials)
MySQL.connect((err) => {
if(err) {
console.log(err);
process.exit(0);
}
for(var i = 0; i < 250; i++) console.log('\n');
console.log('Successfuly logged in!');
});
MySQL.query("SELECT plan FROM users WHERE username = 'asd123'", (err, rows) => {
if(err) { console.log(`[!] SQL Error: ${err}`); return; }
rows.forEach((results) => {
console.log(results); //output: RowDataPacket { plan: 300 }
});
});
}
load();
I need MyPlan: to be declared with a number 300 as stated above.
startMyPlan() {
//var theoric = 10;
this.botCountInt = setInterval(() => {
let json = {
connected: 0,
MyPlan: //300
};
this.send(json);
}, 100);
}
I tried to define results in first function like this:
rows.forEach((results) => {
myresult = results //first option
// OR LIKE THIS:
this.myresult = results; //second option
console.log(results);
});
and write it in startMyPlan() as
let json = {
connected: 0,
MyPlan: this.myresult
};
But first option gave me error myresult is not defined and second option gave me different error: Cannot set properties of undefined(setting 'myresult').
EDIT: I guess my problem is that results won't give me correct output OUTSIDE that function where it's created. It returns undentified when I try to run console.log(results); once it's run outside load() function
So, as per my understanding, you have not defined the myresult variable in any scope.
The first option will work if you do var myresult=undefined just before rows.forEach((results) => call.
One example:
var rows = [{ "test": "foo1" }, { "test": "foo2" }, { "test": "foo3" }, { "test": "foo4" }];
var myresult;
rows.forEach((results) => {
myresult = results //first option
console.log(myresult);
});
console.log(myresult);
I am not sure what exactly you want to do with myresult. But I think above example will be enough to understand things.
I am completely at a loss here. I have been struggling with this for several hours now trying multiple different approaches and none are getting me anywhere. My problem is I cannot seem to figure out how it the new Password is meant to be retrieved from the user within the newPasswordRequired callback after an authentication request to Cognito. Here is my code in its current state. Please don't hesitate to tell me what I can do better, as I am fairly new to Angular and completely new to using Cognito authentication.
public login(email: string, password: string): Observable<UserModel> {
const cognitoUser = new CognitoUser(this.getUserData(email));
cognitoUser.setAuthenticationFlowType('USER_PASSWORD_AUTH');
const authenticationDetails = new AuthenticationDetails(CognitoUtils.getAuthDetails(email, password));
const self = this;
return Observable.create((obs: Observer<UserModel>) => {
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: result => {
this.session = result;
const token = result.getIdToken();
const accessToken = result.getAccessToken();
this.localStorage.setToken(token);
this.localStorage.setAccessToken(accessToken);
obs.complete();
},
onFailure: err => {
obs.error(err);
},
newPasswordRequired: (userAttributes, requiredAttributes) => {
let dialogRef: MatDialogRef<NewPasswordComponent>;
const config = new MatDialogConfig();;
config.role = 'dialog';
config.width = '40%';
config.data = { newPass: self.newPass };
dialogRef = self.dialog.open(NewPasswordComponent, config);
dialogRef.afterClosed().subscribe(result => {
self.newPass = result;
cognitoUser.completeNewPasswordChallenge(self.newPass, userAttributes, {
onSuccess: result => {
obs.complete();
},
onFailure: err => {
obs.error(err);
}
});
});
}
});
});
}
Based on what you have provided, it looks like the issue is when you respond with completeNewPasswordChallenge you're passing in userAttributes which is returned from the newPasswordRequired callback and won't work.
Instead, you need to see what attributes are required (i.e. requiredAttributes) and pass them in as an object. For example, if "name" is the required attribute, then pass in the following way:
dialogRef.afterClosed().subscribe(result => {
self.newPass = result;
cognitoUser.completeNewPasswordChallenge(self.newPass, {"name":"John Doe"}, {
onSuccess: result => {
obs.complete();
},
onFailure: err => {
obs.error(err);
}
});
Hope this helps!
I'm trying to insert a simple user object into a mysql database using sequelize orm.
User model image
body payload & error image
Insert code:
try {
const { body } = req
const user = await User.create(body) // It's breaking here :(
const userJson = user.toJSON()
res.send({
user: userJson,
token: jwtSignUser(userJson)
})
} catch (err) {
res.status(400).send({
error: 'Something went wrong!'
})
I believe that you have not defined the hooks correctly. They are supposed to be functions and I do not see hashPassword being defined in the code that has been shared.
From the docs
hooks: {
beforeValidate: (user, options) => {
user.mood = 'happy';
},
afterValidate: (user, options) => {
user.username = 'Toni';
}
}
Make sure the reference to hashPassword function declaration is correct if it's in a different file :)
I'm quite new to NodeJS and JS globally and I'm in trouble while setting and Object Property through a MySQL query.
I'm using Promise to avoid bad asynchronous effect but apparently I'm doing it wrong, the property of my Agent Obejct is never updated.
Here is the code :
class Agent {
constructor(agentId, agentName, agentCountry) {
this.agentId = agentId;
this.agentName = agentName;
this.agentCountry = agentCountry;
}
setAgentCountry () {
var promise = function(agentID) {
return new Promise(function(resolve, reject) {
var query = "SELECT c.CountryID, c.CountryName FROM AgentCountry ac, Country c WHERE ac.AgentID = '" + agentID + "' AND ac.CountryID = c.CountryID";
connection.query(query, function(err, results) {
if (!err) {
resolve(results);
} else {
console.log('Error while performing Query.');
}
});
});
}
promise(this.agentID).then(function(data) {
var string = JSON.stringify(data);
var json = JSON.parse(string);
//the agent property is never updated !!
this.agentCountry = json;
}.bind(this), function(err) {
console.log(err);
});
}
}
I call the method this way :
var agent = new Agent(1,"John Doe", "France");
console.log(agent.agentCountry); //Displays "France"
agent.setAgentCountry();
console.log(agent.agentCountry); //Did not display the table of countries it should
Could you help me with this ?
Thanks
The main problem is that console.log is being executed before the promise being resolved. Writing a console.log inside the "then" clause will show you the timing.
The promise will be resolved or rejected eventually but nobody is waiting for setAgentCountry.
There are several points of order here:
A promise must always be either (1) resolved or (2) rejected. Your error case logs it to the console without calling reject(), so it's stuck in promise limbo for forever when it errors.
Why do you name a variable, promise, the same as the library, Promise?
I think you will find it more modular to just wrap the mysql_conn.query() callback into a promise():
const mysql_conn = mysql.createConnection({
host: mysql_conf.host,
user: mysql_conf.user,
password: mysql_conf.password
});
mysql_conn.queryPromiser = function(sql, args) {
return new Promise(function(resolve, reject) {
mysql_conn.query(
sql,
args,
function(err, results, fields) {
if (err) {
reject(err);
} else {
resolve( {"results": results, "fields": fields} );
}
}
);
});
};
then you can use it like so:
class Agent {
constructor(agentId, agentName) {
this.agentId = agentId;
this.agentName = agentName;
this.agentCountry = null;
}
configureCountryPromiser() {
var sql = "SELECT country FROM agent_countries WHERE agent_id = ?";
var args = [ this.agentId ];
var that = this;
return mysql_conn.queryPromiser(sql, args)
.then(function(data) {
if (data.results.length) {
that.agentCountry = data.results[0].country;
} else {
// handle case where agent_id is not found in agent_countries
}
});
}
};
agent_instance = new Agent(1, "Benoit Duprat");
agent_instance.configureCountryPromiser()
.then(function() {
console.log("agent country configured to ", agent_instance.agentCountry);
}).catch(console.error);
Please note that I have not tested the class code, but the general idea should be enough to answer your question.