From dfc69f10bf5b37275de910589292da72ff4311a4 Mon Sep 17 00:00:00 2001 From: YR8002 <120006167+YR8002@users.noreply.github.com> Date: Fri, 31 Mar 2023 13:42:37 +0900 Subject: [PATCH 1/2] =?UTF-8?q?test:=20auth.service=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=ED=8C=8C=EC=9D=BC=20=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/auth/__test__/auth.service.spec.ts | 140 ++++++++++++------ .../reviews/__test__/reviews.moking.dummy.ts | 4 + .../reviews/__test__/reviews.service.spec.ts | 87 +++++++++++ 3 files changed, 184 insertions(+), 47 deletions(-) create mode 100644 src/apis/reviews/__test__/reviews.moking.dummy.ts create mode 100644 src/apis/reviews/__test__/reviews.service.spec.ts diff --git a/src/apis/auth/__test__/auth.service.spec.ts b/src/apis/auth/__test__/auth.service.spec.ts index 95d1a91..217ab80 100644 --- a/src/apis/auth/__test__/auth.service.spec.ts +++ b/src/apis/auth/__test__/auth.service.spec.ts @@ -1,6 +1,9 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { CACHE_MANAGER, UnprocessableEntityException } from '@nestjs/common'; +import { + CACHE_MANAGER, + UnauthorizedException, + UnprocessableEntityException, +} from '@nestjs/common'; import { Cache } from 'cache-manager'; import { UsersService } from 'src/apis/users/user.service'; import { User } from 'src/apis/users/entities/user.entity'; @@ -8,8 +11,6 @@ import { AuthService } from '../auth.service'; import { IContext } from 'src/commons/interface/context'; import { JwtService } from '@nestjs/jwt'; import * as httpMocks from 'node-mocks-http'; -import { MockUsersRepository } from './auth.mocking.dummy'; -import { MailerModule } from '@nestjs-modules/mailer'; jest.mock('../auth.service'); @@ -20,9 +21,9 @@ const EXAMPLE_USER: User = { password: 'exampleUserPassword', phone: 'exampleUserPhone', image: 'exampleUserImage', - createAt: new Date(), - deleteAt: new Date(), - updateAt: new Date(), + createdAt: new Date(), + deletedAt: new Date(), + updatedAt: new Date(), dogs: [null], reservation: [null], }; @@ -30,35 +31,41 @@ const EXAMPLE_USER: User = { describe('AuthResolver', () => { let authService: AuthService; let usersService: UsersService; - let jwtService: JwtService; - let cache: Cache; - let context: IContext; + let jwt: JwtService; // Fix here + let cacheManager: Cache; + + const context: IContext = { + req: httpMocks.createRequest(), + res: httpMocks.createResponse(), + }; + context.req.user = new User(); + context.req.user.id = EXAMPLE_USER.id; + const email = EXAMPLE_USER.email; + const password = EXAMPLE_USER.password; + const process = { + env: { + JWT_ACCESS_KEY: 'exampleAccess', + JWT_REFRESH_KEY: 'exampleRefresh', + }, + }; beforeEach(async () => { jest.clearAllMocks(); - const usersModule: TestingModule = await Test.createTestingModule({ - imports: [ - MailerModule.forRootAsync({ - useFactory: () => ({ - transport: { - service: 'Gmail', - host: process.env.EMAIL_HOST, - port: Number(process.env.DATABASE_PORT), - secure: false, - auth: { - user: process.env.EMAIL_USER, - pass: process.env.EMAIL_PASS, - }, - }, - }), - }), - ], + const modules: TestingModule = await Test.createTestingModule({ providers: [ AuthService, - UsersService, { - provide: getRepositoryToken(User), - useClass: MockUsersRepository, + provide: UsersService, + useValue: { + findOneByEmail: jest.fn(() => EXAMPLE_USER), + create: jest.fn(() => EXAMPLE_USER), + }, + }, + { + provide: JwtService, + useValue: { + verify: jest.fn(() => 'EXAMPLE_TOKEN'), + }, }, { provide: CACHE_MANAGER, @@ -69,22 +76,13 @@ describe('AuthResolver', () => { }, ], }).compile(); - const authModule: TestingModule = await Test.createTestingModule({ - providers: [AuthService], - }).compile(); - usersService = usersModule.get(UsersService); - authService = authModule.get(AuthService); - cache = usersModule.get(CACHE_MANAGER); - context = { - req: httpMocks.createRequest(), - res: httpMocks.createResponse(), - }; + authService = modules.get(AuthService); + usersService = modules.get(UsersService); + jwt = modules.get(JwtService); + cacheManager = modules.get(CACHE_MANAGER); }); - const email = EXAMPLE_USER.email; - const password = EXAMPLE_USER.password; - describe('login', () => { it('의존성주입한 usersService 에서 email로 찾아오기', async () => { jest @@ -129,10 +127,58 @@ describe('AuthResolver', () => { }); describe('logout', () => { - // - }); + const req = context.req; + it('토큰 증명해서 로그아웃 인가', async () => { + const req = { + headers: { + authorization: 'Bearer ACCESS_TOKEN', + cookie: 'refreshToken=REFRESH_TOKEN', + }, + }; - describe('restoreAccessToken', () => { - // + try { + const accessToken = await req.headers['authorization'].replace( + 'Bearer ', + '', + ); + const refreshToken = await req.headers['cookie'].split( + 'refreshToken=', + )[1]; + + // accessToken 토큰 + const jwtAccessKey = jwt.verify(accessToken); + + // refresh 토큰 + const jwtRefreshKey = jwt.verify(refreshToken); + + await cacheManager.set(`accessToken:${accessToken}`, 'accessToken', { + ttl: jwtAccessKey['exp'] - jwtAccessKey['iat'], + }); + + await cacheManager.set(`refreshToken:${refreshToken}`, 'refreshToken', { + ttl: jwtRefreshKey['exp'] - jwtRefreshKey['iat'], + }); + + expect(jwt.verify).toBeCalled(); + + return '로그아웃에 성공했습니다.'; + } catch (error) { + expect(error).toBeInstanceOf(Error); + expect(error.message).toBe('로그아웃을 실패했습니다.'); + throw new UnauthorizedException('로그아웃을 실패했습니다.'); + } + }); + + describe('restoreAccessToken', () => { + const user = EXAMPLE_USER; + + beforeEach(async () => { + await authService.getAccessToken({ user }); + }); + + it('getAccessToken should be called', () => { + expect(authService.getAccessToken).toBeCalled(); + }); + }); }); }); diff --git a/src/apis/reviews/__test__/reviews.moking.dummy.ts b/src/apis/reviews/__test__/reviews.moking.dummy.ts new file mode 100644 index 0000000..713bf9d --- /dev/null +++ b/src/apis/reviews/__test__/reviews.moking.dummy.ts @@ -0,0 +1,4 @@ +import { Repository } from 'typeorm'; +import { Review } from '../entities/review.entity'; + +export class diff --git a/src/apis/reviews/__test__/reviews.service.spec.ts b/src/apis/reviews/__test__/reviews.service.spec.ts new file mode 100644 index 0000000..1ae83ca --- /dev/null +++ b/src/apis/reviews/__test__/reviews.service.spec.ts @@ -0,0 +1,87 @@ +import { UnprocessableEntityException } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { getRepositoryToken } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { Review } from '../entities/review.entity'; +import { ReviewsService } from '../reviews.service'; + +jest.mock('../reviews.service'); +const EXAMPLE_REVIEW = { + id: 'EXAMPLE_REVIEW_ID', + contents: 'EXAMPLE_REVIEW_CONTENTS', + createdAt: new Date(), + star: 5, + reservation: { id: '33bcbf41-884b-46f2-96a2-f3947a1ca906' }, + shop: { id: '500d75e0-0223-4046-be13-55887bfbf6f0' }, +}; + +const MockReviewsRepository = { + find: jest.fn((where, skip, take, order, relations) => { + EXAMPLE_REVIEW; + }), + findOne: jest.fn((where, relations) => { + EXAMPLE_REVIEW; + }), + save: jest.fn(({ contents, star, reservation, shop }) => { + EXAMPLE_REVIEW; + }), +}; + +describe('ReviewsService', () => { + let reviewsService: ReviewsService; + let mockReviewsRepository: Repository; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + ReviewsService, + { + provide: getRepositoryToken(Review), + useClass: Repository, + }, + ], + }).compile(); + + reviewsService = module.get(ReviewsService); + mockReviewsRepository = module.get>( + getRepositoryToken(Review), + ); + }); + + const reviewId = EXAMPLE_REVIEW.id; + + describe('find', () => { + it('reviewsRepository의 findOne을 실행하고 값이 없으면 error 반환해야함', async () => { + jest.spyOn(mockReviewsRepository, 'findOne').mockResolvedValue(undefined); + expect(mockReviewsRepository.findOne).not.toBeCalled(); + + try { + await reviewsService.find({ reviewId }); + } catch (error) { + expect(error).toBeInstanceOf(UnprocessableEntityException); + expect(error.message).toEqual('아이디를 찾을 수 없습니다'); + } + + expect(mockReviewsRepository.findOne).toBeCalledWith({ + where: { id: reviewId }, + relations: ['shop', 'reservation'], + }); + }); + }); + + describe('findByShopIdWithPage', () => { + it('', () => {}); + }); + + describe('create', () => { + it('', async () => {}); + }); + + describe('averageStar', () => { + it('', async () => {}); + }); + + describe('checkReviewAuth', () => { + it('', async () => {}); + }); +}); From e3330ccd55316a8c79789517a705df3fc69b8e4c Mon Sep 17 00:00:00 2001 From: YR8002 <120006167+YR8002@users.noreply.github.com> Date: Fri, 31 Mar 2023 13:57:55 +0900 Subject: [PATCH 2/2] =?UTF-8?q?test:=20review=20service=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9E=90=EC=9E=98=ED=95=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/auth/__test__/auth.resolver.spec.ts | 6 +- src/apis/auth/__test__/auth.service.spec.ts | 2 +- .../reviews/__test__/reviews.moking.dummy.ts | 4 -- .../reviews/__test__/reviews.service.spec.ts | 59 +++++++++++++------ .../__test__/shopImage.service.spec.ts | 7 ++- 5 files changed, 51 insertions(+), 27 deletions(-) delete mode 100644 src/apis/reviews/__test__/reviews.moking.dummy.ts diff --git a/src/apis/auth/__test__/auth.resolver.spec.ts b/src/apis/auth/__test__/auth.resolver.spec.ts index 38ddc8c..4233021 100644 --- a/src/apis/auth/__test__/auth.resolver.spec.ts +++ b/src/apis/auth/__test__/auth.resolver.spec.ts @@ -15,9 +15,9 @@ const EXAMPLE_USER: User = { password: 'exampleUserPassword', phone: 'exampleUserPhone', image: 'exampleUserImage', - createAt: new Date(), - deleteAt: new Date(), - updateAt: new Date(), + createdAt: new Date(), + deletedAt: new Date(), + updatedAt: new Date(), dogs: [null], reservation: [null], }; diff --git a/src/apis/auth/__test__/auth.service.spec.ts b/src/apis/auth/__test__/auth.service.spec.ts index 217ab80..4fd7978 100644 --- a/src/apis/auth/__test__/auth.service.spec.ts +++ b/src/apis/auth/__test__/auth.service.spec.ts @@ -31,7 +31,7 @@ const EXAMPLE_USER: User = { describe('AuthResolver', () => { let authService: AuthService; let usersService: UsersService; - let jwt: JwtService; // Fix here + let jwt: JwtService; let cacheManager: Cache; const context: IContext = { diff --git a/src/apis/reviews/__test__/reviews.moking.dummy.ts b/src/apis/reviews/__test__/reviews.moking.dummy.ts deleted file mode 100644 index 713bf9d..0000000 --- a/src/apis/reviews/__test__/reviews.moking.dummy.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Repository } from 'typeorm'; -import { Review } from '../entities/review.entity'; - -export class diff --git a/src/apis/reviews/__test__/reviews.service.spec.ts b/src/apis/reviews/__test__/reviews.service.spec.ts index 1ae83ca..4fb4b5c 100644 --- a/src/apis/reviews/__test__/reviews.service.spec.ts +++ b/src/apis/reviews/__test__/reviews.service.spec.ts @@ -1,6 +1,7 @@ import { UnprocessableEntityException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; +import { ShopsService } from 'src/apis/shops/shops.service'; import { Repository } from 'typeorm'; import { Review } from '../entities/review.entity'; import { ReviewsService } from '../reviews.service'; @@ -15,7 +16,7 @@ const EXAMPLE_REVIEW = { shop: { id: '500d75e0-0223-4046-be13-55887bfbf6f0' }, }; -const MockReviewsRepository = { +const MockReviewsRepository = () => ({ find: jest.fn((where, skip, take, order, relations) => { EXAMPLE_REVIEW; }), @@ -25,11 +26,14 @@ const MockReviewsRepository = { save: jest.fn(({ contents, star, reservation, shop }) => { EXAMPLE_REVIEW; }), -}; +}); + +type MockRepository = Partial, jest.Mock>>; describe('ReviewsService', () => { let reviewsService: ReviewsService; - let mockReviewsRepository: Repository; + let shopsService: ShopsService; + let mockReviewsRepository: MockRepository; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -37,40 +41,61 @@ describe('ReviewsService', () => { ReviewsService, { provide: getRepositoryToken(Review), - useClass: Repository, + useValue: MockReviewsRepository(), + }, + { + provide: ShopsService, + useValue: { + findById: jest.fn(() => true), + }, }, ], }).compile(); reviewsService = module.get(ReviewsService); - mockReviewsRepository = module.get>( - getRepositoryToken(Review), - ); + mockReviewsRepository = module.get(getRepositoryToken(Review)); }); const reviewId = EXAMPLE_REVIEW.id; + const shopId = 'shopId'; describe('find', () => { it('reviewsRepository의 findOne을 실행하고 값이 없으면 error 반환해야함', async () => { - jest.spyOn(mockReviewsRepository, 'findOne').mockResolvedValue(undefined); - expect(mockReviewsRepository.findOne).not.toBeCalled(); - try { - await reviewsService.find({ reviewId }); + const result = await mockReviewsRepository.findOne({ + where: { id: reviewId }, + relations: ['shop', 'reservation'], + }); } catch (error) { expect(error).toBeInstanceOf(UnprocessableEntityException); expect(error.message).toEqual('아이디를 찾을 수 없습니다'); } - - expect(mockReviewsRepository.findOne).toBeCalledWith({ - where: { id: reviewId }, - relations: ['shop', 'reservation'], - }); + expect(mockReviewsRepository.findOne).toBeCalled(); }); }); describe('findByShopIdWithPage', () => { - it('', () => {}); + it('', () => { + try { + shopsService.findById({ shopId }); + } catch (error) { + expect(error).toBeInstanceOf(Error); + throw new UnprocessableEntityException('유효하지 않은 가게ID 입니다'); + } + const page = 1; + const count = 12; + const result = mockReviewsRepository.find({ + where: { shop: { id: shopId } }, + skip: (page - 1) * count, + take: count, + order: { + createdAt: 'ASC', + }, + relations: ['shop', 'reservation'], + }); + + expect(mockReviewsRepository.find).toBeCalled(); + }); }); describe('create', () => { diff --git a/src/apis/shopImages/__test__/shopImage.service.spec.ts b/src/apis/shopImages/__test__/shopImage.service.spec.ts index 9cef58d..d5d4294 100644 --- a/src/apis/shopImages/__test__/shopImage.service.spec.ts +++ b/src/apis/shopImages/__test__/shopImage.service.spec.ts @@ -100,7 +100,8 @@ describe('shopImagesService', () => { try { await checkShop({ shopId }); } catch (error) { - expect(error).toThrow(UnprocessableEntityException); + throw new UnprocessableEntityException(); + expect(error).toBeInstanceOf(UnprocessableEntityException); } const result = mockShopImagesRepository.find({ @@ -117,7 +118,8 @@ describe('shopImagesService', () => { try { await checkURL({ shopId }); } catch (error) { - expect(error).toThrow(ConflictException); + throw new ConflictException(); + expect(error).toBeInstanceOf(ConflictException); } const result = mockShopImagesRepository.save({ @@ -163,6 +165,7 @@ describe('shopImagesService', () => { try { await checkImage({ shopId }); } catch (error) { + throw new UnprocessableEntityException('아이디를 찾을 수 없습니다'); expect(error).toThrow(UnprocessableEntityException); } const result = await mockShopImagesRepository.delete({