Skip to content

Commit

Permalink
test: add refresh token unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
a20688392 committed Aug 9, 2023
1 parent 9fa9130 commit 701a7ef
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 23 deletions.
21 changes: 10 additions & 11 deletions src/auth/auth.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type HttpException, ConflictException } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { JwtModule, JwtService } from "@nestjs/jwt";
import { JwtModule } from "@nestjs/jwt";
import { PassportModule } from "@nestjs/passport";
import { type TestingModule, Test } from "@nestjs/testing";
import { getRepositoryToken, TypeOrmModule } from "@nestjs/typeorm";
Expand All @@ -25,8 +25,6 @@ describe("AuthController", () => {
let authService: AuthService;
let userRepository: Repository<UserEntity> | undefined;

const fakeAccessToken = "mocked_access_token";

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
Expand All @@ -46,13 +44,6 @@ describe("AuthController", () => {
// 使用測試資料庫的 Repository
useValue: UserEntity,
},
{
provide: JwtService,
useValue: {
// 模擬JwtService中的方法
sign: jest.fn(),
},
},
LocalStrategy,
JwtAccessStrategy,
],
Expand Down Expand Up @@ -112,9 +103,16 @@ describe("AuthController", () => {
} as JwtUser,
} as unknown as Request;

const fakeAccessToken = "mocked_access_token";
const fakeRefreshToken = "mocked_refresh_token";

jest
.spyOn(authService, "generateAccessToken")
.mockImplementation(async () => fakeAccessToken);
.mockReturnValue(Promise.resolve(fakeAccessToken));

jest
.spyOn(authService, "generateRefreshToken")
.mockReturnValue(Promise.resolve(fakeRefreshToken));

const mockAuthService = jest.spyOn(authService, "login");

Expand All @@ -124,6 +122,7 @@ describe("AuthController", () => {

const expectedResponse: GenerateTokenResponse = {
accessToken: fakeAccessToken,
refreshToken: fakeRefreshToken,
statusCode: 201,
};

Expand Down
45 changes: 33 additions & 12 deletions src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
HttpStatus,
} from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { JwtModule, JwtService } from "@nestjs/jwt";
import { JwtModule } from "@nestjs/jwt";
import { PassportModule } from "@nestjs/passport";
import { type TestingModule, Test } from "@nestjs/testing";
import { getRepositoryToken, TypeOrmModule } from "@nestjs/typeorm";
Expand All @@ -17,6 +17,7 @@ import { UserService } from "src/user/user.service";
import type { Repository } from "typeorm";

import { AuthService } from "./auth.service";
import { type JwtUser } from "./jwt/jwt.interface";
import { JwtAccessStrategy } from "./jwt/jwt-access.strategy";
import { LocalStrategy } from "./local/local.strategy";

Expand All @@ -25,8 +26,6 @@ describe("AuthService", () => {
let userService: UserService;
let userRepository: Repository<UserEntity> | undefined;

const fakeAccessToken = "mocked_access_token";

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
Expand All @@ -45,20 +44,13 @@ describe("AuthService", () => {
// 使用測試資料庫的 Repository
useValue: UserEntity,
},
{
provide: JwtService,
useValue: {
// 模擬JwtService中的方法
sign: jest.fn(),
},
},
LocalStrategy,
JwtAccessStrategy,
],
}).compile();

userService = module.get<UserService>(UserService);
authService = module.get<AuthService>(AuthService);
userService = module.get<UserService>(UserService);
userRepository = module.get<Repository<UserEntity>>(
getRepositoryToken(UserEntity),
);
Expand Down Expand Up @@ -161,16 +153,23 @@ describe("AuthService", () => {
email: "[email protected]",
id: 1,
};
const fakeAccessToken = "mocked_access_token";
const fakeRefreshToken = "mocked_refresh_token";
const expectedStatusCode = HttpStatus.CREATED;

jest
.spyOn(authService, "generateAccessToken")
.mockImplementation(async () => fakeAccessToken);
.mockReturnValue(Promise.resolve(fakeAccessToken));

jest
.spyOn(authService, "generateRefreshToken")
.mockReturnValue(Promise.resolve(fakeRefreshToken));

const result = await authService.login(mockUser);

expect(result).toEqual({
accessToken: fakeAccessToken,
refreshToken: fakeRefreshToken,
statusCode: expectedStatusCode,
});
});
Expand Down Expand Up @@ -234,6 +233,28 @@ describe("AuthService", () => {
});
});

describe("generate Token", () => {
it("should generate access token", async () => {
const userId = 1;
const payload: JwtUser = {
id: userId,
};
const result = await authService.generateAccessToken(payload);

expect(result).toBeDefined();
});

it("should generate refresh token", async () => {
const userId = 1;
const payload: JwtUser = {
id: userId,
};
const result = await authService.generateRefreshToken(payload);

expect(result).toBeDefined();
});
});

afterEach(async () => {
await userRepository?.clear();
});
Expand Down
97 changes: 97 additions & 0 deletions src/auth/jwt/jwt-refresh.guard.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { type ExecutionContext, UnauthorizedException } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { JwtModule, JwtService } from "@nestjs/jwt";
import { PassportModule } from "@nestjs/passport";
import { Test } from "@nestjs/testing";
import jestConfig from "src/config/jest.config";

import { JwtRefreshGuard } from "./jwt-refresh.guard";
import { JwtRefreshStrategy } from "./jwt-refresh.strategy";

describe("JwtRefreshGuard", () => {
let jwtRefreshGuard: JwtRefreshGuard;
let jwtService: JwtService;
let configService: ConfigService;

beforeEach(async () => {
jest.useFakeTimers();
const moduleRef = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
load: [jestConfig],
}),
PassportModule,
JwtModule.register({}),
],
providers: [JwtRefreshGuard, JwtRefreshStrategy, JwtService],
}).compile();

jwtRefreshGuard = moduleRef.get<JwtRefreshGuard>(JwtRefreshGuard);
jwtService = moduleRef.get<JwtService>(JwtService);
configService = moduleRef.get<ConfigService>(ConfigService);
});

it("should be defined", () => {
expect(jwtRefreshGuard).toBeDefined();
});

it("should return true for a valid JWT", async () => {
const payload = { id: 1 };
const secret: string | undefined = configService.get("jwtSecret.refresh");
const token = jwtService.sign(payload, {
expiresIn: "7d",
secret,
});

const response = {};
const context: ExecutionContext = {
getRequest: () => ({
headers: {
authorization: `bearer ${token}`,
},
}),
getResponse: () => response,
switchToHttp: () => context,
} as unknown as ExecutionContext;

const canActivate = await jwtRefreshGuard.canActivate(context);

expect(canActivate).toBe(true);
});

it("should throw an error for an expired JWT", async () => {
const secret: string | undefined = configService.get("jwtSecret.refresh");
const token = jwtService.sign(
{
id: 1,
},
{
expiresIn: "7d",
secret,
},
);

jest.advanceTimersByTime(8 * 24 * 60 * 60 * 1000);

const response = {};
const context: ExecutionContext = {
getRequest: () => ({
headers: {
authorization: `bearer ${token}`,
},
}),
getResponse: () => response,
switchToHttp: () => context,
} as unknown as ExecutionContext;

try {
await jwtRefreshGuard.canActivate(context);
} catch (error) {
expect(error).toBeInstanceOf(UnauthorizedException);
}
});

afterEach(async () => {
jest.clearAllTimers();
});
});
8 changes: 8 additions & 0 deletions src/auth/responses/generate-token.response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ export class GenerateTokenResponse {
})
public readonly accessToken: string;

@ApiProperty({
description: "Generate refreshToken",
example:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Impob25AZ21haWwuY29tIiwiaWQiOjIsImlhdCI6MTY4ODQ2Mzc4OCwiZXhwIjoxNjg4NTUwMTg4fQ.F3YqRedhg62eXpJ946OOTE52Y5-GIYHC8GTtT8JNMc8",
type: "string",
})
public readonly refreshToken: string;

@ApiProperty({
description: "HTTP Code",
example: "201",
Expand Down

0 comments on commit 701a7ef

Please sign in to comment.