Mocha/Node.js/PostgreSQL integration testing - javascript

I have been trying to get this to work for days. I've looked around the internets and on StackOverflow. There are examples of how to test APIs using MongoDB and how to write Mocha tests that execute PSQL commands. That's not what I want.
I created a wrapper for pg, called db.js from the instructions in this SO question (note my comments in the calls to console.log():
pg = require("pg");
config = require("./../config.js");
module.exports = {
query: function(text, values, cb) {
console.log("I get to this in Mocha");
pg.connect(config.connectionString, function(err, client, done) {
console.log("I never get here");
if (err) return console.error("error connecting to postgres: ", err);
client.query(text, values, function(err, result) {
console.log("I most certainly never get here");
done();
cb(err, result);
})
});
}
}
With that, I can do the following:
$ node
$ var db = require ("./path/to/db.js");
$ db.query("insert into sometable(id, value) values(1, \"blah\")", {}, function (err, result) {
if (err) { console.error ("db errored out man"); }
console.log("no error...");
console.log(result);
});
Believe it or not, that works without a hitch!
What I can't do is the same thing in a mocha test (i.e., db.spec.js):
var db = require("./../../../Data/db.js");
// These tests assume you have run the scripts in the -SQL repo
describe("module: db", function() {
it("provides a wrapper for the execution of queries", function () {
db.query("insert into employer.profile \
(id, returncustomer, receiveupdates, name, email, password, active) \
values (4, true, true, 'someNameLol', 'ce#spam.org', 'change_me', true)", {},
function (err, stdout, stderr) {
console.log(err || "");
console.log(stdout || "");
console.log(stderr || "");
}
);
});
});
Help! I want to be able to write integration tests using my database connection. Are there components I'm missing? Required libraries?
This is all hand-rolled, I'm not using an IDE, because I want to understand how it's supposed to work by myself.
Thanks in advance.

You need to include the done parameter, and call it at the end of your test.
describe("module: db", function() {
it("provides a wrapper for the execution of queries", function (done) {
db.query("insert into employer.profile \
(id, returncustomer, receiveupdates, name, email, password, active) \
values (4, true, true, 'someNameLol', 'ce#spam.org', 'change_me', true)", {},
function (err, stdout, stderr) {
console.log(err || "");
console.log(stdout || "");
console.log(stderr || "");
done();
}
);
});
});

Related

Search in ldapjs

I am trying to use the search method of Ldap.js in my node.js code. Here is my code for the client side. It adds successfully a user, but searching for the newly added user does not yield any results. (The ldap server is running in a docker container from https://github.com/osixia/docker-openldap)
var ldap = require("ldapjs");
var assert = require("assert");
var client = ldap.createClient({
url: "ldap://localhost:389",
});
client.bind("cn=admin,dc=example,dc=org", "admin", function (err) {
assert.ifError(err);
let newUser = {
cn: "userId7",
userPassword: "password",
objectClass: "person",
sn: "efub",
};
// Here i successfully add this user "userId7"
client.add(
"cn=userId7,dc=example,dc=org",
newUser,
(err, response) => {
if (err) return console.log(err);
return response;
}
);
var options = {
filter: "(objectClass=*)",
scope: "sub",
};
// Now the search, it runs without error, but does never receive a searchEntry
client.search(
"cn=userId7,dc=example,dc=org",
options,
function (error, search) {
console.log("Searching.....");
client.on("searchEntry", function (entry) {
console.log("I found a result in searchEntry");
});
client.on("error", function (error) {
console.error("error: " + error.message);
});
client.unbind(function (error) {
if (error) {
console.log(error.message);
} else {
console.log("client disconnected");
}
});
}
);
});
client.on('error', function (err) {
if (err.syscall == "connect") {
console.log(err);
}
});
Also, if it helps, this is how the newly added user looks like when i display all users from ldap by running docker exec my-openldap-container ldapsearch -x -H ldap://localhost:389 -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w admin
# userId7, example.org
dn: cn=userId7,dc=example,dc=org
cn: userId7
userPassword:: cGFzc3dvcmQ=
objectClass: person
sn: efub
Update: I can successfully search for the user "userId7" with the shell command: docker exec ldap-service ldapsearch -LLL -x -D "cn=admin,dc=example,dc=org" -w "admin" -b "cn=userId7,dc=example,dc=org" "(objectclass=*)". How can i make ldapJS also run this search successfully?
Update 2: I can also successfully search by using the frontend "phpLDAPadmin" as seen in the screenshots below:
So i solved it. The correct client.search code is:
client.search(
"cn=userId7,dc=example,dc=org",
options,
function (error, res) {
console.log("Searching.....");
res.on("searchEntry", function (entry) {
console.log("I found a result in searchEntry", JSON.stringify(entry.object));
});
res.on("error", function (error) {
console.error("error: " + error.message);
});
client.unbind(function (error) {
if (error) {
console.log(error.message);
} else {
console.log("client disconnected");
}
});
}
);
Inside function (error, res) { I listened for the events via client.on("searchEntry", instead of res.on("searchEntry", therefore missing the events from the search results. The root cause was a classic copy and paste error and changing the variable while misunderstanding the origin of the event.

MongoClient not returning data in cucumberjs test

I've taken this apart several different ways. The find happens after the remove, and the find never finds anything. If I comment out the this.accounts.remove... the find works. If I leave the remove line in there it doesn't. My understanding of cucumberjs, mongo client and node indicates that the find should work.
I've even tried moving the remove/find sequence into its own file, and it works there. It seems to be only when I'm running it in cucumber that the sequence fails. I suspect because of the way of cucumber loads the files, but I'm not sure.
Can someone help me figure out how to get this working?
World.js:
var db = new Db('FlashCards', new Server('localhost', 27017));
db.open(function(err, opened) {
if (err) {
console.log("error opening: ", err);
done(err);
}
db = opened;
});
var {
defineSupportCode
} = require('cucumber');
function CustomWorld() {
this.db = db;
this.accounts = db.collection('accounts');
hooks.js:
Before(function(result, done) {
//comment this out, and leave a done(), it works!!!!
this.accounts.remove(function(error, result){
if( error) {
console.log("Error cleaning the database: ", error);
done(error);
}
done();
})
});
user_steps.js:
Then('I will be registered', function(done) {
let world = this;
this.accounts.find({
username: world.user.username
}).toArray(
function(err, accounts) {
if (err) {
console.log("Error retrieveing data: ", err);
done(err);
}
console.log("Accounts found: ", accounts);
expect(accounts).to.be.ok;
expect(accounts.length).to.be.equal(1);
done();
});
});
Inovcation:
cucumber-js --compiler es6:babel-core/register
You are missing the item to be removed in the remove method. I am assuming the item to be removed is
this.accounts.remove(function(error, result){
You are missing one parameter to remove method. The parameter is query to remove. I am assuming, the remove query is {username: world.user.username}
var qry={username: world.user.username};
Please try with the following:
Before(function(result, done) { //comment this out, and leave a done(), it works!!!!
var qry={username: world.user.username};
this.accounts.remove(qry, function(error, result){
if( error) {
console.log("Error cleaning the database: ", error);
done(error);
}
done();
}) });

Unit Test passes without check Node.js

I am using Node.js (noob in BDD). In my controller I have a function like so:
var getUser = function(username, done) {
console.log('prints');
User.findOne({
'local.username': username
}, function (err, user) {
console.log('doesn"t print');
if (err) {
return done('Oops, server error!', null);
} else {
return done(null, user);
}
});
};
I am using Mocha and described a test block like so:
describe('can be created only using a web interface', function () {
describe('The user name should:', function () {
it("be a valid email", function () {
assert(common.isValidEmail(fakeuser.local.username));
});
it("not already exist in the database", function () {
userController.getUser(fakeuser.local.username, function (err, user) {
log.info('The user name is', user);
//I would like to assert here. But user is always undefined.
}());
});
});
});
It seems like User.findOne is not working the way I expect it to since the test passes no matter what and the code inside doesn't even get executed. What am I missing?
NB: Calling the same function in other parts of the code works just fine.
EDIT
The project is accessible here: https://github.com/attosol/nodeseed
The documentation (and project) is far from complete. Just do an npm install, and npm start followed by starting up your MongoDB (it will use the DB - nodeseed). Just signup using any valid email and find the activation URL in your log files.
You'll have to write something to compare against
it("not already exist in the database", function(done){
var wasCalled = false;
userController.getUser('my#email.com', function(err, user){
console.log('doesn"t print');
wasCalled = true;
});
expect(wasCalled).toBe(true);
});

async watefall doesn't call the functions

So i am actually woking on a simple program with node.Js and i have an issue using async.waterfall :
I created a function in my user model that connect the user by accessing the database, here is the code :
exports.connection = function (login,password) {
async.waterfall([
function getLogin(callback){
usersModel.findOne({ login: login }, function (err, res) {
if (err){
callback(err,null);
return;
}
if(res != null ){
// test a matching password if the user is found we compare both passwords
var userReceived = res.items[0].login;
callback(null,userReceived);
}
});
},
function getPassword(userReceived, callback){
console.log(userReceived);
callback(null,'done')
}
], function(err){
if (err) {
console.error(err);
}
console.log('success');
});
}
Using node-inspector i figured out that the main issue(I think) is that when it enters the waterfall function it doesn't execute the callback function of findOne it literally skips this and directly jump to the getPassword function (which isn't executed too).
so if someone could help me figuring out what's the problem that would be nice since i'm on it for around two days now.
Thank you
EDIT:
After adding the different missing cases of tests(which was why the callback didn't worked) I have this connection function:
exports.connection = function (login,password) {
async.waterfall([
function getLogin(callback){
usersModel.findOne({ login: login }, function (err, res) {
console.log('login: ',res.login);
console.log('erreur: ',err);
if (err){
callback(err,null);
return;
}
if(!res)
{
console.log('getLogin - returned empty res');
callback('empty res');
}
if(res != null ){
// test a matching password if the user is found we compare both passwords
var userReceived = res;
callback(null,userReceived);
}
});
},
function getPassword(userReceived, callback){
console.log('login received :',userReceived.login);
var Ulogin = userReceived.login;
var Upassword = userReceived.password;
// function that compare the received password with the encrypted
//one
bcrypt.compare(password, Upassword, function(err, isMatch) {
if (err) {
console.log(err);
callback(err,null);
return;
}
else if (isMatch) {
console.log('Match', isMatch);
callback(null,isMatch);
}
else {
console.log('the password dont match', isMatch);
callback('pwd error',null);
}
});
},
], function(err){
if (err) {
console.error('unexpected error while connecting', err);
return false;
}
console.log('connected successfully');
return true;
});
}
And in my main file server.js i'm doing currently doing :
var connect = users.connection(login,password);
//the goal is to use the connect variable to know if the connection
//failed or not but it's 'undefined'
if(connect){
res.send('youyou connecté');
}
else {
res.send('youyou problem');
}
this absolutely don't work so i tried to use Q library but I have an error saying
"TypeError: Cannot read property 'apply' of undefined at Promise.apply"
here is the code using Q:
app.post('/signup', function (req, res) {
var login = req.body.login;
var password = req.body.password;
Q.fcall(users.connection(login,password))
.then(function (connect) {
if(connect){
res.send('connected');
}
else {
res.send('problem');
}
})
.catch(function (error) {
throw error;
})
.done();
});
but i am a little bit astonished i thought that by using async.waterfall() i told the function to wait until it received all the callbacks return so i don't understand why the connect variable is 'undefined'?
What I don't understand is - what was the flow exactly? did 'usersModel.findOne' get called?
What I see that is missing here in the getLogin function is a callback in the case that both the 'if' statement return false. in this case you'll get stuck in the first function and you won't advance to 'getPassword' function.
If this still doesn't work, please try executing the following code and report what was printed:
exports.connection = function (login,password) {
async.waterfall([
function getLogin(callback){
usersModel.findOne({ login: login }, function (err, res) {
if (err){
console.log('getLogin - error has occured');
callback(err,null);
return;
}
if(!res)
{
console.log('getLogin - returned empty res');
callback('empty res');
}
console.log('getLogin - result seems OK');
// test a matching password if the user is found we compare both passwords
var userReceived = res.items[0].login;
callback(null,userReceived);
}
});
},
function getPassword(userReceived, callback){
console.log('getPassword');
console.log(userReceived);
callback(null,'done')
}
], function(err){
if (err) {
console.error(err);
}
console.log('success');
});
}

Node.js Mongodb Callback issue

My app should update if tmx is newer, if older do nothing and if doesn't exist insert the document.
If the document is inserted, it works perfectly, else it doesn't update properly or says E11000 dup key.
trying to figure out if my callback are wrong or the logic. (I'm new to node.js+mongodb) MongoClient = require('mongodb').MongoClient,
assert = require('assert'),
url = 'mongodb://localhost:27017/pfc';
MongoClient.connect(url, function (err, db) {
run(db);
});
function run(db) {
fs.readFile('log.log', 'utf8', function (err, source) {
if (err) throw err;
var dataFile = JSON.parse(source);
dataFile.forEach(function (item) {
upsert(db, item, function (err, result) {
if (err) console.dir(err);
});
});
})
}
function upsert(db, doc, callback) {
db.collection('flags').findOne({vid: doc.vid}, function (err, item, result) {
if (item.vid != null) {
if (!(item.tmx instanceof Date)) {
item.tmx = new Date(item.tmx)
}
if(!(doc.tmx instanceof Date)){
doc.tmx = new Date(doc.tmx)
}
if (item.tmx < doc.tmx) {
console.dir("Date validation")
db.collection('flags').updateOne({vid: item.vid}, {
$set: {
"tmx": doc.tmx
}
},{upsert:true}, function (err, result) {
callback(err, result);
}
)
callback(err, result);
}
else{
console.dir("older")
callback(err, result);
}
}
else {
db.collection('flags').insertOne(doc, function(err, result) {
callback(err, result);
});
}
})}
Edit:
The documents from the 'log.log' file have this structure:
{
vid:2848
tmx: "2015-07-18T23:56:17.000Z"
}
{
vid: 2848
tmx: 2015-07-19T00:00:17.000Z
}
collection.find({vid: doc.vid},function(err,item){
if(!item) // didnt find in collection, items with vid: 2848
insert doc to collection
else if(item) //found an item with vid:2848
if (item.tmx < doc.tmx)//only update if doc.tmx is newer
update collection with the most recent document
with #Aaron Dufour help I got rid of the callback problem, thanks :)
but now the problem is when I have the collection already populated and go look for newest documents in log.log, it starts from the oldest document till the newest again :(
Your upsert is vulnerable to race conditions, and run calls it many times in parallel, so that is probably the issue. It is not clear exactly what doc will look like, so you might need slightly more complicated logic, but here's a version that uses Mongo's upsert to make things a bit safer:
function upsert(db, doc, callback) {
db.collection('flags').update({vid: doc.vid}, {$set: doc}, {upsert: true}, function(err) {
db.collection('flags').update({vid: doc.vid, tmx: {$lt: doc.tmx}}, {$set: tmx: doc.tmx}, function(err) {
callback();
});
});
}

Categories

Resources