mocha test for sails hook that depends on a sails app - sails.js

I'm trying to write a mocha test for a sails installable hook (myhook) that is dependent on a particular sails app (myapp). I'd like the bootstrap.test.js to lift myapp with myhook. Thus, I have myapp a devDependency in myhook project.
My bootstrap.test.js has something like this:
var myapp = require('myapp');
// put it in global (special case) for npm test only
global.thehook = require('../api/hooks/myhook/index');
before(function(done) {
this.timeout(10000);
console.log("Bootstrap lifting sails...");
myapp.lift({
hooks: {
"myhook": global.thehook,
"grunt": false
},
log: {level: "error"},
}, function(err) {
if (err) return done(err);
// here you can load fixtures, etc.
done(err, sails);
});
});
after(function(done) {
myapp.lower(done);
});
Thinking .lift() and .lower would apply to the sails app. But, that doesn't seem to be the case.
How do I make this work?

You will need to use the sails dependency in place of myapp.
var sails = require('sails');
before(function(done) {
sails.lift({
// test configuration
}, function (error) {
// ...
done();
});
});
after(function(done) {
sails.lower(function (error) {
//...
done();
});
})
The sails dependency starts in the root of the project directory and will lift the application, so there's no need to require app.js for lifting the app.

Related

React + Sails + Socket.io

This is quite a broad question, however I currently have a Sails API server and a React Front-end (Standalone).
Note: The React Front-End is NOT part of Sails
I'm trying to get to grips with sockets, so I figured I would start simple. I want to achieve the following:
User visits my website (React)
React opens a socket and connects to Sails
Sails streams the data from within a function/model
React updates when new data is added to the model
I semi understand how this works using Express and React, however I cannot get my head around how Sails implements their version of WebSockets on top of Sockets.io.
What I've done is install the sockets.io-client within React, and then trying to use sails.sockets inside Sails.
This is what I currently have:
React Component NB: I don't think this is correct at all
componentDidMount =()=> {
this.getSessionData();
UserStore.listen(this.getSessionData);
Socket.emit('/listSessions', function(data){
console.log(data);
})
}
Sails Function (listSessions)
listSessions: function(req, res) {
Session.find({ where: {visible: true}, sort: 'createdAt DESC'},
function(err, sessions){
if(req.isSocket){
Session.watch(req.socket);
console.log('User subscribed to ' + req.socket.id);
}
if(err) return res.json(500, {
error: err,
message: 'Something went wrong when finding trades'
});
return res.json(200, {
sessions: sessions,
});
})
},
Sails Function (createSession) Trying to use publishCreate to use in conjunction with Session.watch in the above function
createSession: function(req, res){
var token = jwt.sign({
expiresIn: 30,
}, 'overwatch');
Session.create({
username: req.body.username,
platform: req.body.platform,
lookingFor: req.body.lookingFor,
microphone: req.body.microphone,
gameMode: req.body.gameMode,
comments: req.body.comments,
avatar: null,
level: null,
hash: token,
competitiveRank: null,
region: req.body.region,
visible: true,
}).exec(function(err, created){
Session.publishCreate(created);
if(err) {
console.log(err);
return res.send({
error: err,
message: 'Something went wrong when adding a session',
code: 91
})
}
if(req.isSocket){
Session.watch(req.socket);
console.log('User subscribed to ' + req.socket.id);
}
return res.send({
session: created,
code: 00,
})
});
},
Both of the Sails functions are called using POST/GET.
I'm completely stumped as where to go with this, and it seems to documentation or explanation on how to get this working is limited. All the Sails documentation on Sockets seems to relate to using Sails as a front-end and server
OK so I managed to solve this:
Simply put:
Within React, I had to include https://github.com/balderdashy/sails.io.js/tree/master
Then within my React component I did:
componentDidMount =()=> {
io.socket.get('/listSessions',(resData, jwres) => {
console.log('test');
this.setState({
sessions: resData.sessions,
loaded: true,
})
})
io.socket.on('session', (event) => {
if(event.verb == 'created') {
let sessions = this.state.sessions;
sessions.push(event.data);
this.setState({
sessions: sessions
})
} else {
console.log('nah');
}
});
}
This makes a virtual get request to Sails using Socket.io, and sets the response in state. It also watches for updates to the 'session' connection and updates the state with these updates meaning I can update a list in real time
Within my Sails controller I have:
listSessions: function(req, res) {
if(req.isSocket){
Session.find({ where: {visible: true}, sort: 'createdAt DESC'},
function(err, sessions){
Session.watch(req.socket);
if(err) return res.json(500, {
error: err,
message: 'Something went wrong when finding trades'
});
return res.json(200, {
sessions: sessions,
});
})
}
},
The Session.watch line listens for updates via publishCreate on the model which is found in my model as follows:
afterCreate: function(message, next) {
Session.publishCreate(message);
next();
},
Adding to answer by #K20GH , add the following to my "index.js" in React to help get sails.io.js from the CDN :
const fetchJsFromCDN = (src, externals = []) => {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.setAttribute('src', src);
script.addEventListener('load', () => {
resolve(
externals.map(key => {
const ext = window[key];
typeof ext === 'undefined' &&
console.warn(`No external named '${key}' in window`);
return ext;
})
);
});
script.addEventListener('error', reject);
document.body.appendChild(script);
});
};
fetchJsFromCDN(
'https://cdnjs.cloudflare.com/ajax/libs/sails.io.js/1.0.1/sails.io.min.js',
['io']
).then(([io]) => {
if (process.env.NODE_ENV === 'development') {
io.sails.url = 'http://localhost:1337';
}
});
Once you have this, you'll be able to use the HTTP type GET, PUT, POST and DELETE methods. So here you can do:
componentDidMount =()=> {
io.socket.get('/listSessions',(resData, jwres) => {
console.log('test');
this.setState({
sessions: resData.sessions,
loaded: true,
})
})
io.socket.on('session', (event) => {
if(event.verb == 'created') {
let sessions = this.state.sessions;
sessions.push(event.data);
this.setState({
sessions: sessions
})
} else {
console.log('Not created session');
}
});
}
And you can do the required setup in sails for the models of sessions as suggested above

how to deal with mongodb race condition in integration test

I have a mongoose schema with a unique field and I am trying to write a backend (express) integration test which checks that POSTing the same entity twice results in HTTP 400. When testing manually behaviour is as excpected. Automatic testing however requires a wait:
it('should not accept two projects with the same name', function(done) {
var project = // ...
postProjectExpect201(project,
() => {
setTimeout( () => {
postProjectExpect400(project, done);
},100);
}
);
});
The two post... methods do as named and the code above works fine, but if the timeout is removed, BOTH requests receive HTTP 200 (though only one entity created in the database).
I'm new to those technologies and I'm not sure what's going on. Could this be a mongodb related concurrency issue and if so how should I deal with it?
The database call looks like this:
Project.create(req.body)
.then(respondWithResult(res, 201))
.catch(next);
I already tried connecting to mongodb with ?w=1 option btw.
Update:
To be more verbose: Project is a mongoose model and next is my express error handler which catches the duplicate error.
The test functions:
var postProjectExpect201=function(project, done, validateProject) {
request(app)
.post('/api/projects')
.send(project)
.expect(201)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
validateProject && validateProject(res.body);
done();
});
};
var postProjectExpect400=function(project, done) {
request(app)
.post('/api/projects')
.send(project)
.expect(400)
.end((err, res) => {
if (err) {
return done(err);
}
done();
});
};

Disable bootstrap.js when run unit tests

I have sails.on('lifted',...) in config\bootstrap.js.
How to not run this when running mocha unit tests?
While running tests, you can load sails instead of lifting it.
var Sails = require('sails').Sails;
before(function (done) {
new Sails().load(
{}, // your configuration
done
);
});
after(function (done) {
if (sails) { return sails.lower(done); }
return done();
});

How to seed dev database in Sails.js in a reproducible way

I'm looking for a best way to seed my development database in sails js.
In rails I would just use the seeds.rb file but even without that I could use a rake task.
With sails I am unsure of how I could do this outside of manually doing it with sails console.
Please note that solutions which add logic to config/models and the models themselves for production seeding are not what I am looking for. I don't want these records to exist in production.
You can seed your database in the config/bootstrap.js file.
To seed it for a particular environment, what I usually do is:
// config/bootstrap.js
module.exports.bootstrap = function (cb) {
if(process.env.NODE_ENV !== 'development')
return cb();
// Do whatever you want
// And don't forget to...
return cb();
};
And to drop the database each time during the Sails lifting:
// config/env/development.js
module.exports = {
models: {
migrate: 'drop'
}
};
You can use test framework, like Mocha. At your development mode, switch your table name to development table. Here is step by step:
Install mocha with npm install mocha --save-dev
Create test/boostrap.test.js and fill with (configure as your needs), look at my configured connections, it'll override default connections at config.
var Sails = require('sails'),
sails;
before(function (done) {
Sails.lift({
log : {
level: 'error'
},
connections: {
mongodbServer: {
database: 'table_test'
}
},
models : {
migrate: 'drop'
}
}, function (err, server) {
sails = server;
done(err, sails);
});
});
after(function (done) {
// here you can clear fixtures, etc.
sails.lower(done);
});
Create another file for seeding your data, for example create test/inject/seed.js and fill with something like.
describe('data seeding', function(){
it('should seed data', function(done){
sails.models.someModel
.create({
name: 'Some Name'
})
.then(function(result){
done();
})
.catch(done);
});
});
Add this at your package.json under "scripts" key.
"test": "_mocha test/bootstrap.test.js test/inject/**/*.inject.js --no-timeouts"
Run it with npm test to seed your data.
If you need to use it at development mode, when you run sails lift, edit your config/env/development.js and add something like this.
module.exports = {
connections: {
mongodbServer: {
database: 'table_test'
}
}
};
Now your sails lift will use table_test instead of production table, so your production table will be clean.

How to set up unit tests in sailsjs

I cannot run sailsjs unit tests. It seems sails cannot be lifted,
my test (/test/unit/test.js):
var Sails = require('sails');
var app;
before(function() {
console.log('before');
Sails.lift({
log: {
level: 'error'
}
}, function(err, server) {
console.log('lifted');
app = server;
done(err, app);
});
});
// Global after hook
after(function(done) {
app.lower(done);
});
describe('mailer service', function() {
it('should connect to gmail', function() {
console.log(app);
});
});
In my app folder I run: mocha test/unit/test.js
The "app" variable is undefined, console.log('lifted') is not being triggered. What am I doing wrong?
First of all.
You need to call before with a done parameter :
before(function(done){...})
Does your app lift succesfully when you run it with ?
sails lift