Skip to content

Commit

Permalink
Merge pull request #339 from boostcampwm2023/BE-feature/unit-test
Browse files Browse the repository at this point in the history
์œ ๋‹› ํ…Œ์ŠคํŠธ ์ž‘์„ฑ
  • Loading branch information
Conut-1 authored Jun 18, 2024
2 parents 815e26c + 84c3893 commit 4d7a1c6
Show file tree
Hide file tree
Showing 38 changed files with 1,148 additions and 133 deletions.
28 changes: 28 additions & 0 deletions nestjs-BE/server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions nestjs-BE/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"jest-mock-extended": "^3.0.7",
"prettier": "^3.0.0",
"prisma": "^5.6.0",
"source-map-support": "^0.5.21",
Expand Down
143 changes: 143 additions & 0 deletions nestjs-BE/server/src/auth/auth.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';
import { PrismaService } from '../prisma/prisma.service';
import { DeepMockProxy, mockDeep } from 'jest-mock-extended';
import { PrismaClient, RefreshToken, User } from '@prisma/client';
import { AuthService } from './auth.service';
import { UsersService } from '../users/users.service';
import { RefreshTokensService } from './refresh-tokens.service';
import { ProfilesService } from '../profiles/profiles.service';
import { BadRequestException, NotFoundException } from '@nestjs/common';

describe('AuthController', () => {
let controller: AuthController;
let refreshTokensService: DeepMockProxy<RefreshTokensService>;
let usersService: DeepMockProxy<UsersService>;
let authService: DeepMockProxy<AuthService>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
providers: [
AuthService,
PrismaService,
UsersService,
ProfilesService,
RefreshTokensService,
],
})
.overrideProvider(AuthService)
.useValue(mockDeep<AuthService>())
.overrideProvider(PrismaService)
.useValue(mockDeep<PrismaClient>())
.overrideProvider(UsersService)
.useValue(mockDeep<UsersService>())
.overrideProvider(ProfilesService)
.useValue(mockDeep<ProfilesService>())
.overrideProvider(RefreshTokensService)
.useValue(mockDeep<RefreshTokensService>())
.compile();

controller = module.get<AuthController>(AuthController);
refreshTokensService = module.get(RefreshTokensService);
authService = module.get(AuthService);
usersService = module.get(UsersService);
});

it('kakaoLogin user have been logged in', async () => {
const requestMock = { kakaoUserId: 0 };
const kakaoUserAccountMock = { email: 'kakao email' };
const tokenMock = {
refresh_token: 'refresh token',
access_token: 'access token',
};
authService.getKakaoAccount.mockResolvedValue(kakaoUserAccountMock);
usersService.findUserByEmailAndProvider.mockResolvedValue({
uuid: 'user uuid',
} as User);
authService.login.mockResolvedValue(tokenMock);

const response = controller.kakaoLogin(requestMock);

await expect(response).resolves.toEqual({
statusCode: 200,
message: 'Success',
data: tokenMock,
});
expect(usersService.createUser).not.toHaveBeenCalled();
});

it('kakaoLogin user login first time', async () => {
const requestMock = { kakaoUserId: 0 };
const kakaoUserAccountMock = { email: 'kakao email' };
const tokenMock = {
refresh_token: 'refresh token',
access_token: 'access token',
};
authService.getKakaoAccount.mockResolvedValue(kakaoUserAccountMock);
usersService.createUser.mockResolvedValue({ uuid: 'user uuid' } as User);
authService.login.mockResolvedValue(tokenMock);

const response = controller.kakaoLogin(requestMock);

await expect(response).resolves.toEqual({
statusCode: 200,
message: 'Success',
data: tokenMock,
});
expect(usersService.createUser).toHaveBeenCalled();
});

it('kakaoLogin kakao login fail', async () => {
const requestMock = { kakaoUserId: 0 };
authService.getKakaoAccount.mockResolvedValue(null);

const response = controller.kakaoLogin(requestMock);

await expect(response).rejects.toThrow(NotFoundException);
});

it('renewAccessToken respond new access token', async () => {
const requestMock = { refresh_token: 'refresh token' };
authService.renewAccessToken.mockResolvedValue('new access token');

const response = controller.renewAccessToken(requestMock);

await expect(response).resolves.toEqual({
statusCode: 200,
message: 'Success',
data: { access_token: 'new access token' },
});
});

it('renewAccessToken received expired token', async () => {
const requestMock = { refresh_token: 'refresh token' };
authService.renewAccessToken.mockRejectedValue(new Error());

const response = controller.renewAccessToken(requestMock);

await expect(response).rejects.toThrow(Error);
});

it('logout received token deleted', async () => {
const requestMock = { refresh_token: 'refresh token' };
const token = {} as RefreshToken;
refreshTokensService.deleteRefreshToken.mockResolvedValue(token);

const response = controller.logout(requestMock);

await expect(response).resolves.toEqual({
statusCode: 204,
message: 'No Content',
});
});

it('logout received token not found', async () => {
const requestMock = { refresh_token: 'bad refresh token' };
refreshTokensService.deleteRefreshToken.mockResolvedValue(null);

const response = controller.logout(requestMock);

await expect(response).rejects.toThrow(BadRequestException);
});
});
6 changes: 3 additions & 3 deletions nestjs-BE/server/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import {
import { AuthService } from './auth.service';
import { Public } from './public.decorator';
import { KakaoUserDto } from './dto/kakao-user.dto';
import { UsersService } from 'src/users/users.service';
import { UsersService } from '../users/users.service';
import { RefreshTokenDto } from './dto/refresh-token.dto';
import { ProfilesService } from 'src/profiles/profiles.service';
import { ProfilesService } from '../profiles/profiles.service';
import { ApiTags, ApiResponse, ApiOperation } from '@nestjs/swagger';
import customEnv from 'src/config/env';
import customEnv from '../config/env';
import { RefreshTokensService } from './refresh-tokens.service';

@Controller('auth')
Expand Down
75 changes: 75 additions & 0 deletions nestjs-BE/server/src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
import { DeepMockProxy, mockDeep } from 'jest-mock-extended';
import { PrismaClient, RefreshToken } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import { JwtModule, JwtService } from '@nestjs/jwt';
import { RefreshTokensService } from './refresh-tokens.service';

const fetchSpy = jest.spyOn(global, 'fetch');

describe('AuthService', () => {
let service: AuthService;
let prisma: DeepMockProxy<PrismaClient>;
let jwtService: DeepMockProxy<JwtService>;
let refreshTokensService: DeepMockProxy<RefreshTokensService>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [JwtModule],
providers: [AuthService, PrismaService, RefreshTokensService],
})
.overrideProvider(PrismaService)
.useValue(mockDeep<PrismaClient>())
.overrideProvider(JwtService)
.useValue(mockDeep<JwtService>())
.overrideProvider(RefreshTokensService)
.useValue(mockDeep<RefreshToken>())
.compile();

service = module.get<AuthService>(AuthService);
prisma = module.get(PrismaService);
jwtService = module.get(JwtService);
refreshTokensService = module.get(RefreshTokensService);
});

afterEach(() => {
fetchSpy.mockRestore();
});

it('login success', async () => {
jwtService.signAsync.mockResolvedValue('access token');
refreshTokensService.createRefreshToken.mockResolvedValue({
token: 'refresh token',
} as unknown as RefreshToken);

const tokens = service.login('user uuid');

await expect(tokens).resolves.toEqual({
access_token: 'access token',
refresh_token: 'refresh token',
});
});

it('renewAccessToken success', async () => {
jwtService.verify.mockReturnValue({});
jwtService.signAsync.mockResolvedValue('access token');
refreshTokensService.findRefreshToken.mockResolvedValue({
user_id: 'user uuid',
} as RefreshToken);

const token = service.renewAccessToken('refresh token');

await expect(token).resolves.toBe('access token');
});

it('renewAccessToken fail', async () => {
jwtService.verify.mockImplementation(() => {
throw new Error();
});

const token = service.renewAccessToken('refresh token');

await expect(token).rejects.toThrow();
});
});
7 changes: 6 additions & 1 deletion nestjs-BE/server/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { jwtConstants, kakaoOauthConstants } from './constants';
import { stringify } from 'qs';
import { PrismaService } from 'src/prisma/prisma.service';
import { PrismaService } from '../prisma/prisma.service';
import { RefreshTokensService } from './refresh-tokens.service';

@Injectable()
Expand Down Expand Up @@ -55,6 +55,11 @@ export class AuthService {
});
const token =
await this.refreshTokensService.findRefreshToken(refreshToken);
if (!token) {
throw new UnauthorizedException(
'Refresh token expired. Please log in again.',
);
}
const accessToken = await this.createAccessToken(token.user_id);
return accessToken;
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion nestjs-BE/server/src/auth/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import customEnv from 'src/config/env';
import customEnv from '../config/env';

export const jwtConstants = {
accessSecret: customEnv.JWT_ACCESS_SECRET,
Expand Down
Loading

0 comments on commit 4d7a1c6

Please sign in to comment.