0

I am trying to run a test where I want to verify that my helper file is running correctly, and if I have an expired token, I get an error kickback and cannot proceed.

I have a feeling that I can only fake the time directly in the test, and not outside of it. Thing is, I don't want to copy the jwt.verify function in my test because that defeats the purpose if I change the code in the actual helper file. Any help on this one to make this work?

I am faking the time with sinon. If I test to see what time I get now and after the clock tick, I do get the right results. But for some reason this is not applying to the function in another file.

my local.js file

const moment = require('moment');
const jwt = require('jsonwebtoken');

const secret = process.env.TOKEN_SECRET;

function encodeToken(user) {
  const playload = {
    exp: moment().add(1, 'hours').unix(), // expires the token in an hour
    iat: moment().unix(),
    sub: user.id
  };

  return jwt.sign(playload, secret);
}

function decodeToken(token, callback) {
  const payload = jwt.verify(token, secret, function (err, decoded) {
    const now = moment().unix();
    console.log('tim: ' + decoded.exp); //just to see
    console.log('now: ' + now); // just to see
    if (now > decoded.exp) {
      callback('Token has expired.');
    }
    callback(null, decoded);
  });
}

module.exports = {
  encodeToken,
  decodeToken
};

and my test file:

process.env.NODE_ENV = 'test';

const chai = require('chai');
const should = chai.should();
const sinon = require('sinon');

const localAuth = require('../../src/server/auth/local');

describe('decodeToken()', function () {
    var clock;
    beforeEach(function () {
      clock = sinon.useFakeTimers();
    });

    afterEach(function () {
      clock.restore();
    });
    it('should return a decoded payload', function (done) {
      const token = localAuth.encodeToken({
        id: 1
      });
      should.exist(token);
      token.should.be.a('string');
      clock.tick(36001000000);
      localAuth.decodeToken(token, (err, res) => {
        should.exist(err);
        res.should.eql('Token has expired.');
        done();
      });
    });
  });

1 Answers1

1

JWT checks the expiry and throws error by itself. So we just have to assert from the error message. I have made some changes to the code and made it working.

I tested this as below, (code snippets)

const moment = require('moment');
const jwt = require('jsonwebtoken');

const secret = 'abczzxczxczxc';

function encodeToken(user) {
  const payload = {
    exp: moment().add(1, 'hours').unix(), // expires the token in an hour
    iat: moment().unix(),
    sub: user.id
  };

  const token = jwt.sign(payload, secret);
  return token;
}

function decodeToken(token, callback) {
  jwt.verify(token, secret, function(err, decoded) {
    callback(err, decoded);
  });
}

module.exports = {
  encodeToken,
  decodeToken
};

Tested as below,

process.env.NODE_ENV = 'test';

const chai = require('chai');
const should = chai.should();
const sinon = require('sinon');

const localAuth = require('./');

describe('decodeToken()', function () {
  var clock;
  beforeEach(function () {
    clock = sinon.useFakeTimers();
  });

  afterEach(function () {
    clock.restore();
  });
  it('should return a decoded payload', function (done) {
    const token = localAuth.encodeToken({
      id: 1
    });
    token.should.exist;
    token.should.be.a('string');
    clock.tick(36001000000);
    localAuth.decodeToken(token, (err, res) => {
      should.exist(err);
      err.message.should.eql('jwt expired');
      done();
    });
  });
});

Output

➜ faketimer ./node_modules/mocha/bin/mocha index_test.js

decodeToken() ✓ should return a decoded payload

1 passing (17ms)

anoop
  • 3,229
  • 23
  • 35
  • that was exactly what I needed. I didn't know that JWT checks expiry by itself so I really appreciate the explanation :) And apologies for the delay... was out sick for a few days. Thank you so much again! – Thomas Gorczynski Feb 23 '17 at 14:19