From 41f7b89fcf6e4524c8a8d08a6f9dc1b380bce427 Mon Sep 17 00:00:00 2001 From: Seongtae Date: Sun, 14 Jan 2024 17:00:49 +0900 Subject: [PATCH 1/4] chore: change jest setup --- package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/package.json b/package.json index 6a6b253..bdf3cde 100644 --- a/package.json +++ b/package.json @@ -110,9 +110,6 @@ "node_modules", ".entity.ts", ".constants.ts" - ], - "setupFiles": [ - "/.jest/envFile.ts" ] } } From b0e6733ba0fe7d6fd80c678ec86007e374808f23 Mon Sep 17 00:00:00 2001 From: Seongtae Date: Sun, 14 Jan 2024 17:02:52 +0900 Subject: [PATCH 2/4] test: user login and logout --- src/auth/auth.service.spec.ts | 159 ++++++++++++++++++++++++++++++---- src/auth/jwt/jwt.service.ts | 4 +- 2 files changed, 143 insertions(+), 20 deletions(-) diff --git a/src/auth/auth.service.spec.ts b/src/auth/auth.service.spec.ts index e7d9756..5002373 100644 --- a/src/auth/auth.service.spec.ts +++ b/src/auth/auth.service.spec.ts @@ -1,14 +1,25 @@ -import { CACHE_MANAGER } from '@nestjs/common'; -import { JwtService } from '@nestjs/jwt'; +import { + CACHE_MANAGER, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; +import { JwtModule, JwtService } from '@nestjs/jwt'; import { Test, TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; +import { getRepositoryToken, TypeOrmModule } from '@nestjs/typeorm'; import { CONFIG_OPTIONS } from '../common/common.constants'; import { MailService } from '../mail/mail.service'; -import { User } from '../users/entities/user.entity'; -import { ObjectLiteral, Repository } from 'typeorm'; +import { User, UserRole } from '../users/entities/user.entity'; +import { DataSource, ObjectLiteral, Repository } from 'typeorm'; import { AuthService } from './auth.service'; import { customJwtService } from './jwt/jwt.service'; import { Cache } from 'cache-manager'; +import { LoginBodyDto, LogoutBodyDto } from './dtos/login.dto'; +import { UsersModule } from '../users/users.module'; +import { UserRepository } from '../users/repository/user.repository'; +import { TWOHOUR } from './jwt/jwt.payload'; +import * as dotenv from 'dotenv'; + +dotenv.config({ path: __dirname + '/../../.env.dev' }); const mockRepository = () => ({ findOne: jest.fn(), @@ -21,15 +32,42 @@ type MockRepository = Partial< Record, jest.Mock> >; +const mockUserId = 1; + +const mockCacheManager = { + get: jest.fn().mockResolvedValue(mockUserId), + set: jest.fn(), + del: jest.fn(), +}; + describe('AuthService', () => { - let service: AuthService; - let usersRepository: MockRepository; - let mailService: MailService; + let authService: AuthService; + let usersRepository: UserRepository; let jwtService: customJwtService; let cacheManager: Cache; + let dataSource: DataSource; - beforeEach(async () => { + beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ + imports: [ + TypeOrmModule.forRoot({ + type: 'postgres', + username: 'quick_archive_test', + password: 'test1234', + database: 'quick_archive_test', + host: '127.0.0.1', + port: 5432, + entities: ['src/**/entities/*.{ts,js}'], + synchronize: true, + }), + UsersModule, + JwtModule.registerAsync({ + useFactory: () => ({ + secret: process.env.JWT_ACCESS_TOKEN_PRIVATE_KEY, + signOptions: { expiresIn: TWOHOUR }, + }), + }), + ], providers: [ AuthService, MailService, @@ -41,11 +79,7 @@ describe('AuthService', () => { }, { provide: CACHE_MANAGER, - useValue: { - get: jest.fn(), - set: jest.fn(), - del: jest.fn(), - }, + useValue: mockCacheManager, }, { provide: CONFIG_OPTIONS, @@ -63,14 +97,101 @@ describe('AuthService', () => { ], }).compile(); - service = module.get(AuthService); - usersRepository = module.get(getRepositoryToken(User)); - mailService = module.get(MailService); + authService = module.get(AuthService); + usersRepository = module.get(UserRepository); jwtService = module.get(customJwtService); cacheManager = module.get(CACHE_MANAGER); + dataSource = await module.get(DataSource); + }); + + afterEach(async () => { + await dataSource.synchronize(true); + jest.clearAllMocks(); + }); + + afterAll(async () => { + await dataSource.destroy(); + }); + + // Mock 유저 생성 + beforeEach(async () => { + const user = new User(); + user.name = 'test_user'; + user.email = 'test1@email.com'; + user.password = 'qwer1234'; + user.role = UserRole.Client; + user.verified = true; + + await usersRepository.save(user); }); - it('should be defined', () => { - expect(service).toBeDefined(); + describe('Jwt Login 테스트', () => { + it('로그인에 성공한다', async () => { + const loginBodyDto = new LoginBodyDto(); + loginBodyDto.email = 'test1@email.com'; + loginBodyDto.password = 'qwer1234'; + loginBodyDto.auto_login = true; + + const accessTokenSpy = jest.spyOn(jwtService, 'sign'); + const refreshTokenSpy = jest.spyOn(jwtService, 'generateRefreshToken'); + + const loginOutput = await authService.jwtLogin(loginBodyDto); + + const accessToken = accessTokenSpy.mock.results[0].value; + const refreshToken = refreshTokenSpy.mock.results[0].value; + + expect(loginOutput.access_token).toBe(accessToken); + expect(loginOutput.refresh_token).toBe(refreshToken); + }); + }); + + describe('로그아웃 테스트', () => { + // given + const userId = mockUserId; + const logoutBodyDto = new LogoutBodyDto(); + logoutBodyDto.refresh_token = 'refreshToken'; + + it('로그아웃에 성공한다', async () => { + // when + // then + await expect( + authService.logout(userId, logoutBodyDto), + ).resolves.not.toThrow(); + }); + + it('유저가 존재하지 않아 실패한다', async () => { + // given + const userId = 2; + + // when + // then + await expect(authService.logout(userId, logoutBodyDto)).rejects.toThrow( + NotFoundException, + ); + }); + + it('토큰이 존재하지 않아 실패한다.', async () => { + // given + // cacheManager.get = jest.fn().mockImplementationOnce(() => undefined); + + mockCacheManager.get = jest.fn().mockResolvedValue(undefined); + + // when + await expect(authService.logout(userId, logoutBodyDto)).rejects.toThrow( + NotFoundException, + ); + }); + + it('토큰이 유효하지 않아 실패한다.', async () => { + // given + // cacheManager.get = jest.fn().mockImplementationOnce(() => 2); + + mockCacheManager.get = jest.fn().mockResolvedValue(2); + + // when + await expect(authService.logout(userId, logoutBodyDto)).rejects.toThrow( + UnauthorizedException, + ); + }); }); }); diff --git a/src/auth/jwt/jwt.service.ts b/src/auth/jwt/jwt.service.ts index 6e00255..e717f1c 100644 --- a/src/auth/jwt/jwt.service.ts +++ b/src/auth/jwt/jwt.service.ts @@ -7,7 +7,9 @@ export class customJwtService { constructor(private readonly jwtService: JwtService) {} sign(payload: Payload): string { - return this.jwtService.sign(payload); + return this.jwtService.sign(payload, { + secret: process.env.JWT_ACCESS_TOKEN_PRIVATE_KEY, + }); } verify(token: string, options?: JwtVerifyOptions): Payload { From 704602a721264b7a9f15e1111f16a9f4b77fc7c7 Mon Sep 17 00:00:00 2001 From: Seongtae Date: Sun, 14 Jan 2024 17:06:26 +0900 Subject: [PATCH 3/4] format: delete test comment at auth service test --- src/auth/auth.service.spec.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/auth/auth.service.spec.ts b/src/auth/auth.service.spec.ts index 5002373..371e497 100644 --- a/src/auth/auth.service.spec.ts +++ b/src/auth/auth.service.spec.ts @@ -172,8 +172,6 @@ describe('AuthService', () => { it('토큰이 존재하지 않아 실패한다.', async () => { // given - // cacheManager.get = jest.fn().mockImplementationOnce(() => undefined); - mockCacheManager.get = jest.fn().mockResolvedValue(undefined); // when @@ -184,8 +182,6 @@ describe('AuthService', () => { it('토큰이 유효하지 않아 실패한다.', async () => { // given - // cacheManager.get = jest.fn().mockImplementationOnce(() => 2); - mockCacheManager.get = jest.fn().mockResolvedValue(2); // when From 61f1b7d1194eef1d59dc8554036cad35159f31c2 Mon Sep 17 00:00:00 2001 From: Seongtae Date: Sun, 14 Jan 2024 17:35:26 +0900 Subject: [PATCH 4/4] chore: delete jwtService from providers array at auth test --- src/auth/auth.service.spec.ts | 3 +-- src/auth/jwt/jwt.service.ts | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/auth/auth.service.spec.ts b/src/auth/auth.service.spec.ts index 371e497..de108ff 100644 --- a/src/auth/auth.service.spec.ts +++ b/src/auth/auth.service.spec.ts @@ -3,7 +3,7 @@ import { NotFoundException, UnauthorizedException, } from '@nestjs/common'; -import { JwtModule, JwtService } from '@nestjs/jwt'; +import { JwtModule } from '@nestjs/jwt'; import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken, TypeOrmModule } from '@nestjs/typeorm'; import { CONFIG_OPTIONS } from '../common/common.constants'; @@ -72,7 +72,6 @@ describe('AuthService', () => { AuthService, MailService, customJwtService, - JwtService, { provide: getRepositoryToken(User), useValue: mockRepository(), diff --git a/src/auth/jwt/jwt.service.ts b/src/auth/jwt/jwt.service.ts index e717f1c..6e00255 100644 --- a/src/auth/jwt/jwt.service.ts +++ b/src/auth/jwt/jwt.service.ts @@ -7,9 +7,7 @@ export class customJwtService { constructor(private readonly jwtService: JwtService) {} sign(payload: Payload): string { - return this.jwtService.sign(payload, { - secret: process.env.JWT_ACCESS_TOKEN_PRIVATE_KEY, - }); + return this.jwtService.sign(payload); } verify(token: string, options?: JwtVerifyOptions): Payload {