1

我是新来的 passport.js 并试图涵盖我的 JWT 策略的单元测试用例。谁能建议如何做到这一点?

// Setup JWT strategy for all requests
passport.use(
  new JWTStrategy(
    {
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: JWT_PRIVATE_KEY,
    },
    async (jwtPayload: any, done: any) => {
      const isUser = jwtPayload.type === EntityType.User;
      const model = isUser ? userModel : vendorModel;
      try {
        const document = await model.findOne({ _id: jwtPayload.id });
        if (document) {
          return done(null, jwtPayload);
        } else {
          return done(null, false);
        }
      } catch (err) {
        return done(err, false);
      }
    },
  ),
);
4

2 回答 2

0

单元测试解决方案:

index.ts

import passport from 'passport';
import { Strategy as JWTStrategy, ExtractJwt } from 'passport-jwt';
import { userModel, vendorModel, EntityType } from './models';

const JWT_PRIVATE_KEY = 'secret 123';

passport.use(
  new JWTStrategy(
    {
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: JWT_PRIVATE_KEY,
    },
    async (jwtPayload: any, done: any) => {
      console.log('123123');
      const isUser = jwtPayload.type === EntityType.User;
      const model = isUser ? userModel : vendorModel;
      try {
        const document = await model.findOne({ _id: jwtPayload.id });
        if (document) {
          return done(null, jwtPayload);
        } else {
          return done(null, false);
        }
      } catch (err) {
        return done(err, false);
      }
    },
  ),
);

models.ts

export enum EntityType {
  User = 'User',
}
export const userModel = {
  async findOne(opts) {
    return 'real user document';
  },
};
export const vendorModel = {
  async findOne(opts) {
    return 'real vendor document';
  },
};

index.test.ts

import { Strategy as JWTStrategy, ExtractJwt, VerifyCallback, StrategyOptions } from 'passport-jwt';
import passport from 'passport';
import { userModel, vendorModel } from './models';

jest.mock('passport-jwt', () => {
  const mJWTStrategy = jest.fn();
  const mExtractJwt = {
    fromAuthHeaderAsBearerToken: jest.fn(),
  };
  return { Strategy: mJWTStrategy, ExtractJwt: mExtractJwt };
});
jest.mock('passport', () => {
  return { use: jest.fn() };
});

describe('62125872', () => {
  let verifyRef;
  beforeEach(() => {
    const mJwtFromRequestFunction = jest.fn();
    (ExtractJwt.fromAuthHeaderAsBearerToken as jest.MockedFunction<
      typeof ExtractJwt.fromAuthHeaderAsBearerToken
    >).mockReturnValueOnce(mJwtFromRequestFunction);

    (JWTStrategy as jest.MockedClass<any>).mockImplementation((opt: StrategyOptions, verify: VerifyCallback) => {
      verifyRef = verify;
    });
  });

  it('should verify using user model and call done with jwtpayload if user document exists', async () => {
    const payload = { type: 'User', id: 1 };
    const mDone = jest.fn();

    jest.spyOn(userModel, 'findOne').mockResolvedValueOnce('mocked user document');
    await import('./');
    await verifyRef(payload, mDone);
    expect(passport.use).toBeCalledWith(expect.any(Object));
    expect(JWTStrategy).toBeCalledWith(
      { jwtFromRequest: expect.any(Function), secretOrKey: 'secret 123' },
      expect.any(Function),
    );
    expect(ExtractJwt.fromAuthHeaderAsBearerToken).toBeCalledTimes(1);
    expect(userModel.findOne).toBeCalledWith({ _id: 1 });
    expect(mDone).toBeCalledWith(null, { type: 'User', id: 1 });
  });

  it("should verify using user model and call done with false if user document doesn't exist", async () => {
    const payload = { type: 'User', id: 1 };
    const mDone = jest.fn();

    jest.spyOn(userModel, 'findOne').mockResolvedValueOnce('');
    await import('./');
    await verifyRef(payload, mDone);
    expect(passport.use).toBeCalledWith(expect.any(Object));
    expect(JWTStrategy).toBeCalledWith(
      { jwtFromRequest: expect.any(Function), secretOrKey: 'secret 123' },
      expect.any(Function),
    );
    expect(ExtractJwt.fromAuthHeaderAsBearerToken).toBeCalledTimes(1);
    expect(userModel.findOne).toBeCalledWith({ _id: 1 });
    expect(mDone).toBeCalledWith(null, false);
  });

  // you can do the rest parts
});

单元测试结果:

 PASS  stackoverflow/62125872/index.test.ts
  62125872
    ✓ should verify using user model and call done with jwtpayload if user document exists (11ms)
    ✓ should verify using user model and call done with false if user document doesn't exist (2ms)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |      85 |    83.33 |      60 |   84.21 |                   
 index.ts  |   92.86 |       75 |     100 |   92.31 | 24                
 models.ts |   66.67 |      100 |   33.33 |   66.67 | 6,11              
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        3.716s, estimated 10s
于 2020-06-02T04:21:42.823 回答
0

用于supertest验证全周期


import request from 'supertest';
import express from 'express';
import jwt from 'jsonwebtoken'


export const createAuthToken = (userId) => {
  const body = { 
    type: EntityType.User,
    id: userId,
  };
  return jwt.sign(body, JWT_PRIVATE_KEY);
};

// this function should configure express app
const appLoader = async app => {
  (await import('../app/loaders/express')).expressLoader({ app }); // express bindings and routes
  await import('./'); // passport config
}

describe('passport-jwt auth', () => {
  const app = express();
  const token = createAuthToken('user1')


  beforeAll(async () => {
    await appLoader({ app });
  });

  it('should verify auth', async () => {
    jest.spyOn(userModel, 'findOne').mockResolvedValueOnce('mocked user document');


    await request(app)
      .get('/protected-endpoint')
      .set('Authorization', `Bearer ${token}`)
      .expect(200);
  });

  it('should verify auth - failure', async () => {
    await request(app)
      .get('/protected-endpoint')
      .set('Authorization', `Bearer wrong-token`)
      .expect(401);
  });
});
于 2021-02-23T19:46:00.250 回答