I'm trying to unit test a simple piece of Express middleware, a cascading athenticator that checks first for a JWT token using a passport-jwt-strategy
, and then if that fails, using a passport-openid-strategy
. Each of the strategies is already well tested so what I am trying to test is their integration.
The module I am testing looks like this:
"use strict";
let passport = require('passport');
let Strategies = require('./strategies');
let setupDone = false;
// set up passport
let setup = function (app) {
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (obj, done) {
done(null, obj);
});
passport.use('jwt', Strategies.jwt);
passport.use('openid', Strategies.openId);
app.use(passport.initialize());
app.use(passport.session());
setupDone = true;
};
let authenticate = function (req, res, next) {
if (!setupDone) throw new Error('You must have run setup(app) before you can use the middleware');
console.log(' cascadingAuthentication');
// first try the token option
passport.authenticate('jwt', function (jwterr, user, info) {
console.log(' jwt auth', jwterr, user, info);
if (jwterr || !user) {
passport.authenticate('openid', function (oautherr, user, info) {
if (oautherr || !user) {
return next(oautherr);
} else {
next();
}
});
} else {
req.user = user;
next();
}
});
};
module.exports = {
setup: setup,
authenticate: authenticate
}
My Jasmine
test looks like this
"use strict";
let CascadingAuthentication = require('../../lib/middleware/cascadingAuthentication');
let TokenUtils = require('../support/tokenUtils');
let email = '[email protected]';
describe('cascadingAuthentication', function () {
describe('when there is a token in the header', function () {
let req;
let res = {};
let app = {
use: function (used) { console.log('app.use called with', typeof used); }
};
beforeEach(function (done) {
let token = TokenUtils.makeJWT(email);
req = {
app: app,
header: {
Authorization: `Bearer ${token}`
}
}
CascadingAuthentication.setup(app);
CascadingAuthentication.authenticate(req, res, function () {
done();
});
});
it('populates req.user', function () {
expect(req.user).toEqual(jasmine.any(Object));
});
});
});
The issue I have is that, when I run the test, I see the first console.log(' cascadingAuthentication')
but I never see the second console.log('jwt auth', err, user, info)
. The code just dies inside passport.authenticate
without ever calling the callback, without raising an error, or without providing any kind of feedback at all.
I'm running my tests via gulp
using Jasmine
.
My questions are: in order,
- Can you see anything obvious that I have done that I might have just missed?
- Is there anything else I ought to mock out in my
req
,res
, orapp
that might make this test work? - Is there any way to debug this interactively; stepping through the code under test as it runs, rather than just adding
console.log
statements (which seems a little 1980s to me).
Digging through
passport
's source I have worked out there were two problems with my code.The first is that
passport.authenticate
returns a middleware function, it doesn't actually execute that function. So the solution was simply to call the returned function.So my authenticate method now looks like:
(The above example is trimmed for use in the question)
The other issue was in my test I used
header
instead ofheaders
in my mockreq
object, and alsoauthorization
ought to have had a lower casea
.With those two fixes the test now passes.