Search code examples
mocha.jsjwtsinonchaisinon-chai

useFakeTimers mocha chai sinon - not the right result on a test


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();
      });
    });
  });

Solution

  • 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)