How to send a authentication with a request with supertest - node.js

I'm trying to test a authenticated route.
This is my code:
let request = require('supertest');
var superagent = require('superagent');
var agent = superagent.agent();
var theAccount = {
name: '*********',
role: 'admin',
id: '115039452833383267752'
};
request = request('http://localhost:3000');
describe('Live-score', () => {
before(function (done) {
request
.post('/api/login')
.send(theAccount)
.end(function (err, res) {
if (err) {
throw err;
}
agent.saveCookies(res);
done();
});
});
it('Should work', (done) => {
agent.attachCookies(req);
request
.get('/api/live-score')
.send(agent)
.set('Accept', 'text/html')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, done);
});
However I get the following error:
TypeError: agent.saveCookies is not a function
I'm using Google Passport strategy.

The one place I saw some similar code to this, the agent was declared within the before block.
You could try:
before(function (done) {
agent = superagent.agent();
request
.post('/api/login')
.send(theAccount)
.end(function (err, res) {
if (err) {
throw err;
}
agent.saveCookies(res);
done();
});
});
Reference: https://github.com/visionmedia/superagent/issues/352

Related

Testing JWT Authentication Using Mocha and Chai

I'm stuck writing a test for my get endpoint which requires a token of the admin to return a list of users.
Here is my user endpoint:
app.get("/users", (req,res) => {
const payload = req.payload
if (payload && payload.user === "admin") {
User.find({}, (err, users) => {
if(!err) {
res.status(200).send(users)
} else { res.status(500).send(err) }
})
} else { res.status(500).send("Authentication Error!)}
}
Here's my jwt middleware:
module.exports = {
validateToken: (req, res, next) => {
const authorizationHeader = req.headers.authorization;
let result;
if (authorizationHeader) {
const token = req.headers.authorization.split(" ")[1]; // Bearer <token>
const options = {
expiresIn: "2d",
issuer: "clint maruti"
};
try {
// verify makes sure that the token hasn't expired and has been issued by us
result = jwt.verify(token, process.env.JWT_SECRET, options);
// Let's pass back the decoded token to the request object
req.decoded = result;
// We call next to pass execution to the subsequent middleware
next();
} catch (err) {
// Throw an error just in case anything goes wrong with verification
throw new Error(err)
}
} else {
result = {
error: 'Authentication error. Token required.',
status: 401
}
res.status(401).send(result)
}
}
};
Here's my sample test:
let User = require("../models/users");
// Require dev dependencies
let chai = require("chai");
let chaiHttp = require("chai-http");
let app = require("../app");
let should = chai.should();
chai.use(chaiHttp);
let defaultUser = {
name: "admin",
password: "admin#123"
};
let token;
// parent block
describe("User", () => {
beforeEach(done => {
chai
.request(app)
.post("/users")
.send(defaultUser)
.end((err, res) => {
res.should.have.status(200);
done();
});
});
beforeEach(done => {
chai
.request(app)
.post("/login")
.send(defaultUser)
.end((err, res) => {
token = res.body.token;
res.should.have.status(200);
done();
});
});
afterEach(done => {
// After each test we truncate the database
User.remove({}, err => {
done();
});
});
describe("/get users", () => {
it("should fetch all users successfully", done => {
chai
.request(app)
.set("Authentication", token)
.get("/users")
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a("object");
res.body.should.have.property("users");
done();
});
});
});
});
Problem:
My test is giving me an assertion error 500 instead of 200 of the status code, I have written 2 beforeEach functions. One, to register the admin and the other one to Login the admin and get the token. I wonder if this is what is bringing the error? Please help
I found the answer here:
How to send Headers ('Authorization','Bearer token') in Mocha Test cases
You have to set { Authorization: "Bearer" + token } instead of "Authentication", token
You have to call .set after .get
describe("/get users", () => {
it("should fetch all users successfully", (done) => {
chai
.request(app)
.get("/users")
.set({ Authorization: `Bearer ${token}` })
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a("object");
res.body.should.have.property("users");
done();
});
});
});
chai-http has auth function to send the Authorization Bearer token.
Accroding to chai-http code on Github, token can be pass using:
.auth(accessToken, { type: 'bearer' })
An example provided on the similar Question:
https://stackoverflow.com/a/66106588/4067905

Chai assertion for expressjs token-based app

I'm writting tests for an expressjs token-based app, however I read the chai doc and find only cookie based auth. With an workaround the test passed with the code above, however I need the token to be used in others it statements. Any tips are welcome. I'm using mochajs to run tests.
var chai = require('chai');
var chaiHttp = require('chai-http');
var mongoose = require('mongoose');
var server = require('../app');
var Dish = require('../models/dishes');
var should = chai.should();
chai.use(chaiHttp);
describe('Users', function() {
it('should return success on /login POST', function(done) {
var agent = chai.request.agent(server)
agent
.post('/users/login')
.send({"username": "admin", "password": "password"})
.then(function(res) {
res.should.have.cookie('token');
return agent.get('/users')
.then(function(res) {
res.should.have.status(200);
});
});
done();
});
it('should return success on /users GET', function(done) {
chai.request(server)
.get('/users')
.end(function(err, res) {
res.should.have.status(200);
done();
});
});
});
describe('Dishes', function() {
it('should list ALL dishes on /dishes GET', function(done) {
chai.request(server)
.get('/dishes')
.end(function(err, res) {
res.should.have.status(200);
done();
});
});
it('should list a SINGLE dish on /dish/<id> GET');
it('should add a SINGLE dish on /dishes POST');
it('should update a SINGLE dish on /dish/<id> PUT');
it('should delete a SINGLE dish on /dish/<id> DELETE');
});
You can start your test by before, and then get you token
let token = '';
describe('Users', () => {
before((done) => {
const userCredentials = {
username: 'admin',
password: 'password'
};
chai.request(server)
.post('/api/v1/auth')
.send(userCredentials)
.end((err, res) => {
console.log('res', res.body.data.customer.token);
token = `Bearer ${res.body.data.customer.token}`;
done();
});
})
// Your test
})

Can't made authenticated requests with Mocha and Supertest

I'm trying to test an API with Mocha and Supertest without lucky to make it work.
I have the following code:
var supertest = require('supertest');
describe('Routing', function() {
var url = 'http://example.com';
var server = supertest.agent(url);
var credentials = {
user: 'username',
pass: 'password'
};
describe('Login', function() {
it('should login ok given valid credentials', function(done) {
server
.post('/login.php')
.send(credentials)
.end(function(err, res) {
if (err) {
throw err;
}
server.saveCookies(res);
done();
});
});
it('should correctly make an authenticated request', function(done){
server
.get('/api/me/accounts?_=1449865354112')
.end(function(err,res) {
if (err) {
throw err;
}
res.status.should.be.equal(200);
done();
});
});
});
});
The login request works fine, I get authenticated. The second call throws a 401 status.
I read the documentation but I can't make it work.
What is wrong?
thanks!
UPDATE:
I finally get authenticated by sending the params using .field('user', 'myUsername') and .field('pass', 'myPassword').
Also I have to persist the cookie between calls:
cookie = res.headers['set-cookie']; when I get authenticated, and .set('cookie', cookie) in the next requests.
.send() is for your data. .auth() is for your credentials. Try:
it('should login ok given valid credentials', function(done) {
server
.post('/login.php')
.auth(credentials)
.send({"some": "value"})
.expect(200)
.end(function(err, res) {
if (err) {
done(error);
}
server.saveCookies(res);
done();
});
});
See http://visionmedia.github.io/superagent/docs/test.html for a bit more information on supertest.
That is because the session (cookie) is not persisted between your two tests.
First you should do the two calls inside the same test.
Second i remember that i have used superagent to persist the session between two calls to the same server. But it seems that supertest now expose the agent to persist the session.
var supertest = require('supertest');
var app = express();
var agent = supertest.agent(app);
// then you can persist cookie
agent
.post('/login.php')
.auth(credentials)
...
edit :
here is an example of how i have used superagent for tests :
var request = require('superagent');
var postData= {
email: 'john#test.com',
password: 'test'
};
var user1 = request.agent();
user1.post('http://localhost:3000/user/login')
.send(postData)
.end(function (err, res) {
expect(err).to.not.exist;
expect(res.status).to.equal(200);
var result = res.body;
expect(result.data.message).to.equal('Login successful');
user1.get('http://localhost:3000/user')
.end(function (err, res) {
expect(err).to.not.exist;
expect(res.status).to.equal(200);
var result = res.body;
expect(result.data.email).to.equal('john#test.com');
done();
});
});

Why am I getting a 401 response in my tests?

I am trying to test a route with authentication in my Node / Express / Mongoose back-end.
Here's the test file
var should = require('should');
var _ = require('lodash');
var async = require('async');
var app = require('../../../../app');
var request = require('supertest');
var mongoose = require('mongoose');
var User = mongoose.model('User');
var Firm = mongoose.model('Firm');
var firm, user, userPassword, createdFirm, loggedInUser;
describe('GET /api/firms', function(){
beforeEach(function (done) {
firm = new Firm({
company: 'My test company',
corporateMail: 'test.com'
});
userPassword = 'password';
user = new User({
fistname: 'Fake User',
lastname: 'Fake User',
email: 'test#test.com',
job: 'Partner',
firmName:firm.company,
password:userPassword,
isActivated:true,
_firmId:firm._id
});
function createFirm(cb){
request(app)
.post('/api/firms')
.send(firm)
.expect(201)
.end(function(err, res){
if ( err ) throw err;
createdFirm = res.body;
cb();
});
}
function createUser(cb){
request(app)
.post('/api/common/users')
.send(user)
.expect(200)
.end(function(err, res){
createdUser = res.body;
if ( err ) throw err;
cb();
});
};
async.series([function(cb){
createFirm(cb);
}, function(cb){
createUser(cb);
}], done);
});
afterEach(function (done) {
firm.remove();
user.remove();
done();
});
it('should respond with 401 error', function(done) {
request(app)
.get('/api/firms')
.expect(401)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
it('should login', function(done) {
request(app)
.post('/auth/local')
.send({email:user.email, password:user.password})
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
it('should respond with 200 after login', function(done) {
request(app)
.get('/api/firms')
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
In the workflow the firm object is created first and then returns its Id so I can create the user with the firmId as a reference.
I would like to test the /api/firms route after the user is authenticated but in spite of my various attempts (using superagent, logging in the before section) I always get a 401 response in the last should section instead of an expected 200.
Actually the important thing to keep in mind is, as KJ3 said, how the authentication is set up. In my case I forgot to mention that I was using jwt. The way it works is the following, you supply a username and a password and the server returns a token created with jwt.
So it makes sense to send the token back for each request in the tests.
The way to achieve this is first by storing the token after authentication in the before section
function createUser(cb){
request(app)
.post('/api/users')
.send(user)
.expect(200)
.end(function(err, res){
if ( err ) throw err;
authToken = res.body.token;
cb();
});
};
Then by adding .set in the request with the token in the correct format ('Bearer ' + token , which is defined in the authentication service):
it('should respond with 200', function(done) {
var authToken = 'Bearer ' + createdUser.token;
request(app)
.get('/api/firms')
.set('Authorization', authToken)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
In the case the test sends a 200 back, which is expected and sends a 401 if the .set(...) is commented out.
Good news is that this is achieved with supertest, so no need to add anything, less good news is that you need to add the .set(...) to each test request.
If you were to go through the last 2 tests in a browser, depending on how you have it setup, yes it would work thanks to cookies and sessions, but here the /api/firms test is independent of the auth/local test. So a 401 is the correct response.
It really depends on how your auth is setup, but you need to authenticate on the /api/firms test too. Either by sending the credentials again (every single one of my mocha tests authenticates each time) or implement sessions into the tests, see this SO post for some direction.

How to write a post request test in mocha with data to test if response matches?

Question:
How do would I write a post request test in mocha that tests if the response matches?
The response will just be a url string as it is a redirect for a 3rd party service.
Working Example Payload:
curl -H "Content-Type: application/json" -X POST -d '{"participant":{"nuid":"98ASDF988SDF89SDF89989SDF9898"}}' http://localhost:9000/api/members
member.controller.js // post method
// Creates a new member in the DB.
exports.create = function(req, res) {
Member.findByIdAndUpdate(req.body.participant.nuid,
{ "$setOnInsert": { "_id": req.body.participant.nuid } },
{ "upsert": true },
function(err,doc) {
if (err) throw err;
res.send({
'redirectUrl': req.protocol + '://' + req.get('host') + '/registration/' + req.body.participant.nuid
})
}
);
};
Expected res.send
{"redirectUrl":"http://localhost:9000/registration/98ASDF988SDF89SDF89989SDF9898"}
Working Example GET request Test
var should = require('should');
var app = require('../../app');
var request = require('supertest');
describe('GET /api/members', function() {
it('should respond with JSON array', function(done) {
request(app)
.get('/api/members')
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
res.body.should.be.instanceof(Array);
done();
});
});
it('should respond with redirect on post', function(done) {
// need help here
});
});
Try with this:
it('should respond with redirect on post', function(done) {
request(app)
.post('/api/members')
.send({"participant":{"nuid":"98ASDF988SDF89SDF89989SDF9898"}})
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) done(err);
res.body.should.have.property('participant');
res.body.participant.should.have.property('nuid', '98ASDF988SDF89SDF89989SDF9898');
});
done();
});
You can also set the type to "form" and content type to json as I show below:
it("returns a token when user and password are valid", (done) => {
Users.createUserNotAdmin().then((user: any) => {
supertestAPI
.post("/login")
.set("Connection", "keep alive")
.set("Content-Type", "application/json")
.type("form")
.send({"email": user.email, password: "123456"})
.end((error: any, resp: any) => {
chai.expect(JSON.parse(resp.text)["token"].length).above(400, "The token length should be bigger than 400 characters.");
done();
})
});
});
You also have to set the body-parser when you create the server as I show below:
server.use(bodyParser.urlencoded({ extended: false }));
server.use(bodyParser.json());

Resources