Skip to content

Commit

Permalink
Merge pull request #82 from boostcampwm-2024/be/test/auth_testcode
Browse files Browse the repository at this point in the history
[BE/test] auth.service 테스트 코드 작성
  • Loading branch information
HBLEEEEE authored Nov 21, 2024
2 parents 0948242 + a314754 commit b908ff1
Show file tree
Hide file tree
Showing 16 changed files with 426 additions and 212 deletions.
2 changes: 1 addition & 1 deletion apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"@nestjs/platform-express": "^10.0.0",
"@nestjs/schedule": "^4.1.1",
"@nestjs/swagger": "^8.0.5",
"@types/bcrypt": "^5.0.2",
"backend": "file:",
"bcrypt": "^5.1.1",
"dotenv": "^16.4.5",
Expand All @@ -49,6 +48,7 @@
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/bcrypt": "^5.0.2",
"@types/express": "^5.0.0",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
Expand Down
18 changes: 0 additions & 18 deletions apps/backend/src/auth/auth.controller.spec.ts

This file was deleted.

6 changes: 3 additions & 3 deletions apps/backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { Request } from 'express';
import { ApiOperation } from '@nestjs/swagger';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { User } from 'src/global/utils/memberData';
import { successhandler, successMessage } from 'src/global/successhandler';
import { JwtAuthGuard } from 'src/global/utils/jwtAuthGuard';
import { User } from '../global/utils/memberData';
import { successhandler, successMessage } from '../global/successhandler';
import { JwtAuthGuard } from '../global/utils/jwtAuthGuard';
import { SignUpDto } from './dto/signUp.dto';
import { LoginDto } from './dto/login.dto';
import { GoogleLoginDto } from './dto/googleLogin.dto';
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { DatabaseModule } from 'src/database/database.module';
import { DatabaseModule } from '../database/database.module';
import { JwtModule } from '@nestjs/jwt';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { PassportModule } from '@nestjs/passport';
Expand Down
243 changes: 238 additions & 5 deletions apps/backend/src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,251 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
import { JwtService } from '@nestjs/jwt';
import { DatabaseService } from '../database/database.service';
import { RedisClientType } from 'redis';
import { createClient } from 'redis';
import { ConfigService } from '@nestjs/config';
import { HttpException, HttpStatus } from '@nestjs/common';
import * as bcrypt from 'bcrypt';
import { authQueries } from './auth.queries';
import { SignUpDto } from './dto/signUp.dto';
import { LoginDto } from './dto/login.dto';
import { GoogleLoginDto } from './dto/googleLogin.dto';
import { KakaoLoginDto } from './dto/kakaoLogin.dto';

describe('AuthService', () => {
let service: AuthService;
let authService: AuthService;
let mockRedisClient: RedisClientType;
let mockDatabaseService: DatabaseService;
let mockJwtService: JwtService;

beforeEach(async () => {
mockRedisClient = createClient({ url: 'redis://localhost:6379' });
mockDatabaseService = { query: jest.fn() } as unknown as DatabaseService;
mockJwtService = { sign: jest.fn() } as unknown as JwtService;

const module: TestingModule = await Test.createTestingModule({
providers: [AuthService]
providers: [
AuthService,
{ provide: JwtService, useValue: mockJwtService },
{ provide: DatabaseService, useValue: mockDatabaseService },
{ provide: 'REDIS_CLIENT', useValue: mockRedisClient },
ConfigService
]
}).compile();

service = module.get<AuthService>(AuthService);
authService = module.get<AuthService>(AuthService);
});

describe('회원가입', () => {
it('유효하지 않은 이메일일 경우 에러를 발생시킨다.', async () => {
const signUpDto: SignUpDto = {
email: 'invalid-email',
password: 'password123',
nickname: 'testuser'
};

await expect(authService.signUp(signUpDto)).rejects.toThrowError(
new HttpException('유효한 이메일 주소를 입력해주세요.', HttpStatus.BAD_REQUEST)
);
});

it('이미 존재하는 이메일일 경우 에러르 발생시킨다.', async () => {
const signUpDto: SignUpDto = {
email: '[email protected]',
password: 'password123',
nickname: 'testuser'
};

mockDatabaseService.query = jest.fn().mockResolvedValueOnce({
rows: [{ email: '[email protected]' }]
});

await expect(authService.signUp(signUpDto)).rejects.toThrow(
new HttpException('중복된 이메일입니다.', HttpStatus.BAD_REQUEST)
);
});

it('정상적인 데이터의 경우 회원 가입에 성공한다.', async () => {
const signUpDto: SignUpDto = {
email: '[email protected]',
password: 'password123',
nickname: 'testuser'
};
const mockQueryResponse = {
rows: [{ member_id: 1 }]
};

mockDatabaseService.query = jest
.fn()
.mockResolvedValueOnce({ rows: [] })
.mockResolvedValueOnce(mockQueryResponse);

await authService.signUp(signUpDto);

expect(mockDatabaseService.query).toHaveBeenCalledWith(authQueries.findByEmailQuery, [
signUpDto.email
]);
expect(mockDatabaseService.query).toHaveBeenCalledWith(authQueries.signUpQuery, [
signUpDto.email,
expect.any(String),
signUpDto.nickname
]);
});
});

describe('로그인', () => {
it('유효하지 않은 이메일 입력시 에러를 발생시킨다.', async () => {
const loginDto: LoginDto = {
email: 'invalid-email',
password: 'password123'
};

await expect(authService.login(loginDto)).rejects.toThrow(
new HttpException('유효한 이메일 주소를 입력해주세요.', HttpStatus.BAD_REQUEST)
);
});

it('이메일과 비밀번호의 정보가 일치하지 않을 경우 에러를 발생시킨다.', async () => {
const loginDto: LoginDto = {
email: '[email protected]',
password: 'password123'
};
const mockQueryResponse = {
rows: [{ password: 'hashedPassword123' }]
};
mockDatabaseService.query = jest.fn().mockResolvedValueOnce(mockQueryResponse);

await expect(authService.login(loginDto)).rejects.toThrow(
new HttpException('이메일 또는 비밀번호가 올바르지 않습니다.', HttpStatus.UNAUTHORIZED)
);
});

it('로그인에 성공하면 토큰을 반환한다.', async () => {
const loginDto: LoginDto = {
email: '[email protected]',
password: 'password123'
};

mockDatabaseService.query = jest.fn().mockResolvedValueOnce({
rows: [
{ member_id: 1, nickname: 'testuser', password: await bcrypt.hash('password123', 10) }
]
});
mockJwtService.sign = jest.fn().mockReturnValue('mockToken');

const result = await authService.login(loginDto);

expect(result).toHaveProperty('accessToken');
expect(result).toHaveProperty('refreshToken');
});
});

it('should be defined', () => {
expect(service).toBeDefined();
describe('구글 로그인', () => {
it('구글 로그인에 성공한다.', async () => {
const googleLoginDto: GoogleLoginDto = { email: '[email protected]', name: 'testuser' };

const mockSocialLogin = jest.spyOn(authService, 'SocialLogin');
mockSocialLogin.mockResolvedValueOnce({
nickname: 'testuser',
accessToken: 'mockToken',
refreshToken: 'mockToken'
});

mockJwtService.sign = jest.fn().mockReturnValue('mockToken');

const result = await authService.googleLogin(googleLoginDto);

expect(result).toHaveProperty('nickname');
expect(result).toHaveProperty('accessToken');
expect(result).toHaveProperty('refreshToken');
expect(mockSocialLogin).toHaveBeenCalledWith(googleLoginDto.email, googleLoginDto.name);
});
});

describe('카카오 로그인', () => {
it('카카오 로그인에 성공한다.', async () => {
const kakaoLoginDto: KakaoLoginDto = { email: '[email protected]', nickname: 'testuser' };

const mockSocialLogin = jest.spyOn(authService, 'SocialLogin');
mockSocialLogin.mockResolvedValueOnce({
nickname: 'testuser',
accessToken: 'mockToken',
refreshToken: 'mockToken'
});

mockJwtService.sign = jest.fn().mockReturnValue('mockToken');

const result = await authService.kakaoLogin(kakaoLoginDto);

expect(result).toHaveProperty('nickname');
expect(result).toHaveProperty('accessToken');
expect(result).toHaveProperty('refreshToken');
expect(mockSocialLogin).toHaveBeenCalledWith(kakaoLoginDto.email, kakaoLoginDto.nickname);
});
});

describe('로그아웃', () => {
it('토큰을 갖고 있지 않을 경우 에러를 발생시킨다.', async () => {
await expect(authService.logout(undefined)).rejects.toThrow(
new HttpException('토큰이 필요합니다.', HttpStatus.BAD_REQUEST)
);
});

it('로그아웃에 성공하면 토큰을 블랙리스트에 넣는다.', async () => {
const token = 'mockToken';
const decodedToken = { exp: Math.floor(Date.now() / 1000) + 60 };

mockJwtService.decode = jest.fn().mockReturnValue(decodedToken);
mockRedisClient.set = jest.fn();

await authService.logout(token);

expect(mockRedisClient.set).toHaveBeenCalledWith(
`blacklist:${token}`,
'true',
expect.objectContaining({ PX: expect.any(Number) })
);
});
});

describe('소개 수정', () => {
it('소개 업데이트가 정상적으로 이루어진다', async () => {
const memberId = 1;
const introduce = '소개글 입니다';

mockDatabaseService.query = jest.fn().mockResolvedValueOnce({});

await authService.updateIntroduce(memberId, introduce);

expect(mockDatabaseService.query).toHaveBeenCalledWith(authQueries.updateInroduceQuery, [
introduce,
memberId
]);
});
});

describe('닉네임 수정', () => {
it('닉네임 변경이 정상적으로 이루어진다.', async () => {
const memberId = 1;
const nickname = 'newNickn';
mockDatabaseService.query = jest.fn().mockResolvedValueOnce({});

await authService.updateNickname(memberId, nickname);

expect(mockDatabaseService.query).toHaveBeenCalledWith(authQueries.updateNicknameQuery, [
nickname,
memberId
]);
});

it('닉네임이 범위 밖일 경우 에러가 발생한다.', async () => {
const memberId = 1;
const nickname = 'N';

await expect(authService.updateNickname(memberId, nickname)).rejects.toThrow(
new HttpException('닉네임은 2자에서 10자 사이로 입력해주세요.', HttpStatus.BAD_REQUEST)
);
});
});
});
4 changes: 2 additions & 2 deletions apps/backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
import { DatabaseService } from 'src/database/database.service';
import { DatabaseService } from '../database/database.service';
import { SignUpDto } from './dto/signUp.dto';
import * as bcrypt from 'bcrypt';
import { authQueries } from './auth.queries';
Expand All @@ -8,7 +8,7 @@ import { JwtService } from '@nestjs/jwt';
import { GoogleLoginDto } from './dto/googleLogin.dto';
import { KakaoLoginDto } from './dto/kakaoLogin.dto';
import { RedisClientType } from 'redis';
import { Nullable, Optional } from 'src/global/utils/dataCustomType';
import { Nullable, Optional } from '../global/utils/dataCustomType';

@Injectable()
export class AuthService {
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/src/auth/decorator/login.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { applyDecorators } from '@nestjs/common';
import { ApiResponse } from '@nestjs/swagger';
import { LoginSuccessResponseDto } from 'src/auth/dto/login.dto';
import { LoginSuccessResponseDto } from '../dto/login.dto';

export function loginResponseDecorator() {
return applyDecorators(
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/src/auth/decorator/logout.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { applyDecorators } from '@nestjs/common';
import { ApiResponse } from '@nestjs/swagger';
import { LogoutSuccessResponseDto } from '../dto/logout.dto';
import { TokenDecorator } from 'src/global/utils/tokenSwagger';
import { TokenDecorator } from '../../global/utils/tokenSwagger';

export function logoutResponseDecorator() {
return applyDecorators(
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/src/auth/decorator/oauth.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { applyDecorators } from '@nestjs/common';
import { ApiResponse } from '@nestjs/swagger';
import { LoginSuccessResponseDto } from 'src/auth/dto/login.dto';
import { LoginSuccessResponseDto } from '../dto/login.dto';

export function oauthResponseDecorator() {
return applyDecorators(
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/src/auth/decorator/signUp.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { applyDecorators } from '@nestjs/common';
import { ApiResponse } from '@nestjs/swagger';
import { SignUpSuccessResponseDto } from 'src/auth/dto/signUp.dto';
import { SignUpSuccessResponseDto } from '../dto/signUp.dto';

export function signUpResponseDecorator() {
return applyDecorators(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { applyDecorators } from '@nestjs/common';
import { ApiResponse } from '@nestjs/swagger';
import { TokenDecorator } from 'src/global/utils/tokenSwagger';
import { TokenDecorator } from '../../global/utils/tokenSwagger';
import { UpdateIntroduceSuccessResponseDto } from '../dto/updateIntroduce.dto';

export function updateIntroduceResponseDecorator() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { applyDecorators } from '@nestjs/common';
import { ApiResponse } from '@nestjs/swagger';
import { TokenDecorator } from 'src/global/utils/tokenSwagger';
import { TokenDecorator } from '../../global/utils/tokenSwagger';
import { UpdateNicknameSuccessResponseDto } from '../dto/updateNickname.dto';

export function updateNicknameResponseDecorator() {
Expand Down
18 changes: 0 additions & 18 deletions apps/backend/src/rank/rank.controller.spec.ts

This file was deleted.

Loading

0 comments on commit b908ff1

Please sign in to comment.