I am using koa-jwt which in turn uses jsonwebtoken. Here's my router implementation:

index.ts

const jwt = require('koa-jwt');
...
// some other code
...

export const publicEndpoints = ['/', '/openapi.json', '/healthcheck'];

export default new Router<ApplicationState, ApplicationContext>()
  .use(configure)
  .use((ctx,next) => {
    console.log("REACH 02 opts",ctx,next);
    console.log("REACH 02 jwt", jwt);
  })
  .use(
    jwt({
      secret: customSecretLoader,
      audience: (jwksConfig.audience as unknown) as string,
      algorithms: ['RS256'],
    }).unless({ path: publicEndpoints })
  )
  // Public endpoints
  .use('/openapi.json', swagger)
  .use('/', helloworld)
  .use('/healthcheck', healthcheck)

  // Secure endpoints
  .get('/secure', helloworld)
  .middleware();

calling /secure should pass through the middleware that calls jwt and passes the token

I want to test every secure route to make sure that it does not pass any requests that don't have the right token and passes the ones that do.

The prior was easy, I just needed to call a secure route:

index.test.ts

  it('Unauthenticated secure request returns 401', async () => {
    const response = await request(server).get('/secure');
    expect(response.status).toEqual(401);
  });

The latter however, to get it working I need to mock the jwt() function call and have it return 200 but the problem is that regardless of what I write in my test I am still calling the original implementation of koa-jwt.

*To have some context, this is the koa-js library that I am trying to mock https://github.com/koajs/jwt/blob/master/lib/index.js. It returns middleware() which calls verify which in turn is using jsonwebtoken enter image description here enter image description here

Mocking the entire exported function

index.test.ts

`var jwt = require('koa-jwt');`

...
// some code
...

  it('Authenticated secure request returns 200', async () => {

    jwt = jest.fn(() => {
      Promise.resolve({
        status: 200,
        success: 'Token is valid'
      });
    });

    console.log("REACH 01 jwt", jwt);

    const response = await request(server).get('/secure');
    console.log("REACH RESPONSE",response);
    expect(response.status).toEqual(200);
  });

The output I get from that in the console log is:

REACH 01 jwt function mockConstructor() {
        return fn.apply(this, arguments);
        }

which is what I expect but when the jwt() is hit in index.ts the output I get is:

    REACH 02 jwt (opts = {}) => {
        const { debug, getToken, isRevoked, key = 'user', passthrough, tokenKey } = opts;
        const tokenResolvers = [resolveCookies, resolveAuthHeader];

        if (getToken && typeof getToken === 'function') {
            tokenResolvers.unshift(getToken);
        }

        const middleware = async function jwt(ctx, next) {
            let token;
            tokenResolvers.find(resolver => token = resolver(ctx, opts));
.....

I expect that having mocked koa-jwt that I would see the same output in both console logs.

I tried a few different things that gave me the same result: - mocking the middleware function in the exported default function in koa-js - mocking the dependency of koa-js which is jsonwebtoken

What am I missing?

1 Answers

0
Naguib Ihab On Best Solutions

The solution was mocking the entire module outside of the test:

import....

// some other code

jest.mock('koa-jwt', () => {
  const fn = jest.fn((opts) => // 1st level i.e. jwt()
  {
    const middlewareMock = jest.fn(async (ctx, next) => { // 2nd level i.e. middleware() 
      // Unreachable
    });
    // @ts-ignore
    middlewareMock.unless = jest.fn(() => jest.fn((ctx, next) => {
      next();
    })); // 4th level i.e. middleware().unless()
    return middlewareMock;
  });
  return fn;
});

... 

describe('routes: index', () => {
  // Testing each secure endpoint with authentication
  it('Authenticated requests to secure endpoints return 200', async () => {
    secureEndpoints.forEach(async (endpoint) => {
      const response = await request(server).get(endpoint);
      expect(response.status).toEqual(200);
    });
  });
});