From 5f41e185e032d772dc84fa24159de9ef9faf980e Mon Sep 17 00:00:00 2001 From: rio <75190035+HyoJongPark@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:52:07 +0900 Subject: [PATCH 1/9] =?UTF-8?q?test:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20#364?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/images/images.service.spec.ts | 68 ++++++++++++++++++++++ backend/test/testImage.png | Bin 0 -> 38582 bytes 2 files changed, 68 insertions(+) create mode 100644 backend/src/images/images.service.spec.ts create mode 100644 backend/test/testImage.png diff --git a/backend/src/images/images.service.spec.ts b/backend/src/images/images.service.spec.ts new file mode 100644 index 0000000..8f64a47 --- /dev/null +++ b/backend/src/images/images.service.spec.ts @@ -0,0 +1,68 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ImagesService } from './images.service'; +import { ImagesRepository } from './images.repository'; +import * as fs from 'fs'; +import { ManagedUpload } from 'aws-sdk/clients/s3'; + +jest.mock('./images.repository'); + +describe('FriendsService Test', () => { + let imagesService: ImagesService; + let imagesRepository: ImagesRepository; + + const buffer = fs.readFileSync('./test/testImage.png'); + const originalname = 'testImage.jpeg'; + const file = { originalname, buffer } as Express.Multer.File; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ImagesService, ImagesRepository], + }).compile(); + + imagesService = module.get(ImagesService); + imagesRepository = module.get(ImagesRepository); + }); + + describe('일기 이미지 업로드 테스트', () => { + beforeEach(() => jest.clearAllMocks()); + + it('일기 이미지 정상 업로드', async () => { + //given + const userId = 1; + const date = new Date(); + const data = { + Location: `https://dandi-object-storage.kr.object.ncloudstorage.com/${userId}/${date.getFullYear()}/${date.getMonth()}/${originalname}`, + } as ManagedUpload.SendData; + + (imagesRepository.uploadImage as jest.Mock).mockResolvedValue(data); + + //when + const result = await imagesService.uploadDiaryImage(userId, file); + + //then + expect(imagesRepository.uploadImage).toHaveBeenCalledTimes(1); + expect(result.Location).toEqual(data.Location); + }); + }); + + describe('프로필 이미지 업로드 테스트', () => { + beforeEach(() => jest.clearAllMocks()); + + it('프로필 이미지 정상 업로드', async () => { + //given + const userId = 1; + const data = { + Location: `https://dandi-object-storage.kr.object.ncloudstorage.com/${userId}/profile/${originalname}`, + } as ManagedUpload.SendData; + + (imagesRepository.uploadImage as jest.Mock).mockResolvedValue(data); + + //when + const result = await imagesService.uploadDiaryImage(userId, file); + + //then + expect(imagesRepository.uploadImage).toHaveBeenCalledTimes(1); + expect(result.Location).toEqual(data.Location); + }); + }); +}); diff --git a/backend/test/testImage.png b/backend/test/testImage.png new file mode 100644 index 0000000000000000000000000000000000000000..6c44efa438b7b1131e516891cf10ddb8e6ea0ad7 GIT binary patch literal 38582 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 Jvd$@?2>>PoxuyUB literal 0 HcmV?d00001 From ed65c43ea438485837b6f244bb19e57eed3fb383 Mon Sep 17 00:00:00 2001 From: rio <75190035+HyoJongPark@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:52:52 +0900 Subject: [PATCH 2/9] =?UTF-8?q?chore:=20=EC=82=AC=EC=9A=A9=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20API=20=EC=82=AD=EC=A0=9C=20#36?= =?UTF-8?q?4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/images/images.controller.ts | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/backend/src/images/images.controller.ts b/backend/src/images/images.controller.ts index 94428ee..da4e124 100644 --- a/backend/src/images/images.controller.ts +++ b/backend/src/images/images.controller.ts @@ -36,23 +36,4 @@ export class ImagesController { return { imageURL: uploadedFile.Location }; } - - @Post('/profile') - @UseGuards(JwtAuthGuard) - @UseInterceptors(FileInterceptor('image')) - @ApiOperation({ description: '프로필 이미지 업로드 API' }) - @ApiCreatedResponse({ description: '이미지 업로드 성공' }) - async uploadProfileImage( - @User() user: UserEntity, - @UploadedFile( - new ParseFilePipe({ - validators: [new FileTypeValidator({ fileType: IMAGE_TYPE_REGEX })], - }), - ) - file: Express.Multer.File, - ): Promise> { - const uploadedFile = await this.imagesService.uploadProfileImage(user.id, file); - - return { imageURL: uploadedFile.Location }; - } } From 931d0d25aaa46f36347a7ad62afed4468253267c Mon Sep 17 00:00:00 2001 From: rio <75190035+HyoJongPark@users.noreply.github.com> Date: Fri, 19 Jan 2024 15:15:07 +0900 Subject: [PATCH 3/9] =?UTF-8?q?chore:=20=EC=A0=91=EA=B7=BC=20=EC=A0=9C?= =?UTF-8?q?=EC=96=B4=EC=9E=90=20=EC=84=A4=EC=A0=95=20#364?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/auth/auth.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts index 31c0eee..1993c82 100644 --- a/backend/src/auth/auth.service.ts +++ b/backend/src/auth/auth.service.ts @@ -51,7 +51,7 @@ export class AuthService { }; } - async signUp(user: AuthUserDto, socialType: SocialType): Promise { + private async signUp(user: AuthUserDto, socialType: SocialType): Promise { return await this.usersRepository.createUser(user, socialType); } From 08dbedac7a51e53252caed485a84d968a08e6281 Mon Sep 17 00:00:00 2001 From: rio <75190035+HyoJongPark@users.noreply.github.com> Date: Fri, 19 Jan 2024 15:54:00 +0900 Subject: [PATCH 4/9] =?UTF-8?q?test:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20#364?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/auth/auth.repository.ts | 2 +- backend/src/auth/auth.service.spec.ts | 208 ++++++++++++++++++++++++++ backend/src/auth/auth.service.ts | 2 +- 3 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 backend/src/auth/auth.service.spec.ts diff --git a/backend/src/auth/auth.repository.ts b/backend/src/auth/auth.repository.ts index f74010c..ea724b1 100644 --- a/backend/src/auth/auth.repository.ts +++ b/backend/src/auth/auth.repository.ts @@ -6,7 +6,7 @@ import { v4 as uuidv4 } from 'uuid'; export class AuthRepository { constructor(@InjectRedis() private readonly redis: Redis) {} - setRefreshToken(accessToken: string): void { + async setRefreshToken(accessToken: string) { const refreshToken = uuidv4(); this.redis.set(accessToken, refreshToken, 'EX', REFRESH_TOKEN_EXPIRE_DATE); } diff --git a/backend/src/auth/auth.service.spec.ts b/backend/src/auth/auth.service.spec.ts new file mode 100644 index 0000000..1102ffc --- /dev/null +++ b/backend/src/auth/auth.service.spec.ts @@ -0,0 +1,208 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { JwtService } from '@nestjs/jwt'; +import { AuthService } from './auth.service'; +import { AuthRepository } from './auth.repository'; +import { UsersRepository } from 'src/users/users.repository'; +import { GET_NAVER_PROFILE_URL, NAVER_OAUTH_URL } from './utils/auth.constant'; +import { SocialType } from 'src/users/entity/socialType'; +import { OAuthLoginDto } from './dto/auth.dto'; +import { + ForbiddenException, + InternalServerErrorException, + UnauthorizedException, +} from '@nestjs/common'; + +jest.mock('@nestjs/jwt'); +jest.mock('./auth.repository'); +jest.mock('../users/users.repository'); + +describe('FriendsService Test', () => { + let jwtService: JwtService; + let authService: AuthService; + let authRepository: AuthRepository; + let usersRepository: UsersRepository; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [JwtService, AuthService, AuthRepository, UsersRepository], + }).compile(); + + jwtService = module.get(JwtService); + authService = module.get(AuthService); + authRepository = module.get(AuthRepository); + usersRepository = module.get(UsersRepository); + }); + + describe('login 테스트', () => { + const mockDto: OAuthLoginDto = { + code: 'testCode', + state: 'testState', + socialType: SocialType.NAVER, + }; + + const profile = { + id: '1', + email: 'test@test.com', + nickname: 'testNickname', + profile_image: 'testImage', + }; + const mockUser = { + id: 1, + nickname: 'testNickname', + }; + const tokenResponse = { + status: 200, + json: function () { + return { access_token: 'mocked_token' }; + }, + }; + const profileResponse = { + status: 200, + json: function () { + return { response: profile }; + }, + }; + + beforeEach(() => jest.clearAllMocks()); + + it('DB에 존재하지 않는 사용자 로그인 요청 시 회원가입 후 결과 반환', async () => { + //given + tokenResponse.status = 200; + profileResponse.status = 200; + + (usersRepository.findBySocialIdAndSocialType as jest.Mock).mockResolvedValue(null); + (usersRepository.createUser as jest.Mock).mockResolvedValue(mockUser); + (jwtService.sign as jest.Mock).mockResolvedValue('mocked_token'); + global.fetch = jest.fn().mockImplementation((url: string) => { + if (url === NAVER_OAUTH_URL) { + return tokenResponse; + } + if (url === GET_NAVER_PROFILE_URL) { + return profileResponse; + } + }); + + //when + const result = await authService.login(mockDto); + + //then + expect(global.fetch).toHaveBeenCalledTimes(2); + expect(usersRepository.findBySocialIdAndSocialType).toHaveBeenCalledTimes(1); + expect(usersRepository.createUser).toHaveBeenCalledTimes(1); + expect(authRepository.setRefreshToken).toHaveBeenCalledTimes(1); + expect(jwtService.sign).toHaveBeenCalledTimes(1); + expect(result).toEqual({ token: 'mocked_token', userId: 1 }); + }); + + it('DB에 존재하는 사용자 로그인 요청 시 회원가입 진행 x', async () => { + //given + tokenResponse.status = 200; + profileResponse.status = 200; + + (usersRepository.findBySocialIdAndSocialType as jest.Mock).mockResolvedValue(mockUser); + (jwtService.sign as jest.Mock).mockResolvedValue('mocked_token'); + global.fetch = jest.fn().mockImplementation((url: string) => { + if (url === NAVER_OAUTH_URL) { + return tokenResponse; + } + if (url === GET_NAVER_PROFILE_URL) { + return profileResponse; + } + }); + + //when + const result = await authService.login(mockDto); + + //then + expect(global.fetch).toHaveBeenCalledTimes(2); + expect(usersRepository.findBySocialIdAndSocialType).toHaveBeenCalledTimes(1); + expect(usersRepository.createUser).toHaveBeenCalledTimes(0); + expect(authRepository.setRefreshToken).toHaveBeenCalledTimes(1); + expect(jwtService.sign).toHaveBeenCalledTimes(1); + expect(result).toEqual({ token: 'mocked_token', userId: 1 }); + }); + + it('유효하지 않은 토큰 응답 시 예외 발생', async () => { + //given + tokenResponse.status = 401; + + global.fetch = jest.fn().mockImplementation((url: string) => { + if (url === NAVER_OAUTH_URL) { + return tokenResponse; + } + }); + + //when + await expect(async () => await authService.login(mockDto)).rejects.toThrow( + new UnauthorizedException('유효하지 않은 인가 코드입니다.'), + ); + expect(global.fetch).toHaveBeenCalledTimes(1); + }); + + it('유효하지 않은 access 토큰으로 프로필 요청 시 예외 발생', async () => { + //given + tokenResponse.status = 200; + profileResponse.status = 401; + + global.fetch = jest.fn().mockImplementation((url: string) => { + if (url === NAVER_OAUTH_URL) { + return tokenResponse; + } + if (url === GET_NAVER_PROFILE_URL) { + return profileResponse; + } + }); + + //when + await expect(async () => await authService.login(mockDto)).rejects.toThrow( + new UnauthorizedException('유효하지 않은 accessToken입니다.'), + ); + expect(global.fetch).toHaveBeenCalledTimes(2); + expect(usersRepository.findBySocialIdAndSocialType).toHaveBeenCalledTimes(0); + }); + + it('호출 권한이 존재하지 않는 프로필 요청 시 예외 발생', async () => { + //given + tokenResponse.status = 200; + profileResponse.status = 403; + + global.fetch = jest.fn().mockImplementation((url: string) => { + if (url === NAVER_OAUTH_URL) { + return tokenResponse; + } + if (url === GET_NAVER_PROFILE_URL) { + return profileResponse; + } + }); + + //when + await expect(async () => await authService.login(mockDto)).rejects.toThrow( + new ForbiddenException('데이터 호출 권한이 없습니다.'), + ); + expect(global.fetch).toHaveBeenCalledTimes(2); + expect(usersRepository.findBySocialIdAndSocialType).toHaveBeenCalledTimes(0); + }); + + it('프로필 요청 시 정상 응답 외 응답 시 예외 발생', async () => { + //given + tokenResponse.status = 200; + profileResponse.status = 404; + + global.fetch = jest.fn().mockImplementation((url: string) => { + if (url === NAVER_OAUTH_URL) { + return tokenResponse; + } + if (url === GET_NAVER_PROFILE_URL) { + return profileResponse; + } + }); + + //when + await expect(async () => await authService.login(mockDto)).rejects.toThrow( + new InternalServerErrorException('Naver 서버 에러입니다.'), + ); + expect(global.fetch).toHaveBeenCalledTimes(2); + expect(usersRepository.findBySocialIdAndSocialType).toHaveBeenCalledTimes(0); + }); + }); +}); diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts index 1993c82..0d6b6ba 100644 --- a/backend/src/auth/auth.service.ts +++ b/backend/src/auth/auth.service.ts @@ -39,7 +39,7 @@ export class AuthService { const accessKey = uuidv4(); this.authRepository.setRefreshToken(accessKey); - const accessToken = this.jwtService.sign({ + const accessToken = await this.jwtService.sign({ id: user.id, nickname: user.nickname, accessKey, From f55580c7a4d5730ab916ad4351341a80830b1a03 Mon Sep 17 00:00:00 2001 From: rio <75190035+HyoJongPark@users.noreply.github.com> Date: Fri, 19 Jan 2024 16:14:35 +0900 Subject: [PATCH 5/9] =?UTF-8?q?test:=20access=20token=20=EC=9E=AC=EB=B0=9C?= =?UTF-8?q?=EA=B8=89=20=EB=A1=9C=EC=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20#364?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/auth/auth.service.spec.ts | 41 +++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/backend/src/auth/auth.service.spec.ts b/backend/src/auth/auth.service.spec.ts index 1102ffc..ffb6f9b 100644 --- a/backend/src/auth/auth.service.spec.ts +++ b/backend/src/auth/auth.service.spec.ts @@ -205,4 +205,45 @@ describe('FriendsService Test', () => { expect(usersRepository.findBySocialIdAndSocialType).toHaveBeenCalledTimes(0); }); }); + + describe('refreshAccessToken 테스트', () => { + const mockReq: any = { + cookies: { utk: 'mock_token' }, + }; + const mockPayload = { + id: 1, + nickname: 'testNickname', + accessKey: 'testKey', + }; + + beforeEach(() => jest.clearAllMocks()); + + it('저장소에 리프레시 토큰이 존재하면 새 access token 발급', async () => { + //given + (jwtService.decode as jest.Mock).mockResolvedValue(mockPayload); + (authRepository.getRefreshToken as jest.Mock).mockResolvedValue('mock_token'); + + //when + const result = await authService.refreshAccessToken(mockReq); + + //then + expect(jwtService.decode).toHaveBeenCalledTimes(1); + expect(authRepository.getRefreshToken).toHaveBeenCalledTimes(1); + expect(jwtService.sign).toHaveBeenCalledTimes(1); + }); + + it('저장소에 리프레시 토큰이 존재하지 않으면 예외 발생', async () => { + //given + (jwtService.decode as jest.Mock).mockResolvedValue(mockPayload); + (authRepository.getRefreshToken as jest.Mock).mockResolvedValue(null); + + //when-then + await expect(async () => await authService.refreshAccessToken(mockReq)).rejects.toThrow( + new UnauthorizedException('로그인이 필요합니다.'), + ); + expect(jwtService.decode).toHaveBeenCalledTimes(1); + expect(authRepository.getRefreshToken).toHaveBeenCalledTimes(1); + expect(jwtService.sign).toHaveBeenCalledTimes(0); + }); + }); }); From e6a2c348f3c008207d94e986a9ac53c8910783a9 Mon Sep 17 00:00:00 2001 From: rio <75190035+HyoJongPark@users.noreply.github.com> Date: Fri, 19 Jan 2024 16:17:24 +0900 Subject: [PATCH 6/9] =?UTF-8?q?test:=20refresh=20token=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20#364?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/auth/auth.service.spec.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/backend/src/auth/auth.service.spec.ts b/backend/src/auth/auth.service.spec.ts index ffb6f9b..e961552 100644 --- a/backend/src/auth/auth.service.spec.ts +++ b/backend/src/auth/auth.service.spec.ts @@ -246,4 +246,21 @@ describe('FriendsService Test', () => { expect(jwtService.sign).toHaveBeenCalledTimes(0); }); }); + + describe('removeRefreshToken 테스트', () => { + beforeEach(() => jest.clearAllMocks()); + + it('존재 유무에 상관없이 refreshToken 삭제 요청 가능', async () => { + //given + const mockReq: any = { + cookies: { utk: 'mock_token' }, + }; + + //when + await authService.removeRefreshToken(mockReq); + + //then + expect(authRepository.removeRefreshToken).toHaveBeenCalledTimes(1); + }); + }); }); From d63736d2eaebb3c0fdf692b6fb5c9da740cbfdcc Mon Sep 17 00:00:00 2001 From: rio <75190035+HyoJongPark@users.noreply.github.com> Date: Fri, 19 Jan 2024 16:21:51 +0900 Subject: [PATCH 7/9] =?UTF-8?q?test:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95=20#364?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/images/images.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/images/images.service.spec.ts b/backend/src/images/images.service.spec.ts index 8f64a47..75bec5e 100644 --- a/backend/src/images/images.service.spec.ts +++ b/backend/src/images/images.service.spec.ts @@ -58,7 +58,7 @@ describe('FriendsService Test', () => { (imagesRepository.uploadImage as jest.Mock).mockResolvedValue(data); //when - const result = await imagesService.uploadDiaryImage(userId, file); + const result = await imagesService.uploadProfileImage(userId, file); //then expect(imagesRepository.uploadImage).toHaveBeenCalledTimes(1); From 0a1f8163c58c2bb32df4914c5017fc16dcb482d0 Mon Sep 17 00:00:00 2001 From: rio <75190035+HyoJongPark@users.noreply.github.com> Date: Fri, 19 Jan 2024 16:27:47 +0900 Subject: [PATCH 8/9] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94=20asyn?= =?UTF-8?q?c=20=EC=82=AD=EC=A0=9C=20#364?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/auth/auth.repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/auth/auth.repository.ts b/backend/src/auth/auth.repository.ts index ea724b1..9699f24 100644 --- a/backend/src/auth/auth.repository.ts +++ b/backend/src/auth/auth.repository.ts @@ -6,7 +6,7 @@ import { v4 as uuidv4 } from 'uuid'; export class AuthRepository { constructor(@InjectRedis() private readonly redis: Redis) {} - async setRefreshToken(accessToken: string) { + setRefreshToken(accessToken: string) { const refreshToken = uuidv4(); this.redis.set(accessToken, refreshToken, 'EX', REFRESH_TOKEN_EXPIRE_DATE); } From 718f12b1e03da01cf0ae5baadb4393406ae8625c Mon Sep 17 00:00:00 2001 From: rio <75190035+HyoJongPark@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:35:56 +0900 Subject: [PATCH 9/9] =?UTF-8?q?test:=20=EB=8F=99=EA=B8=B0=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EB=AA=A8=ED=82=B9=20=EB=B0=A9=EC=8B=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20#364?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/auth/auth.service.spec.ts | 4 ++-- backend/src/auth/auth.service.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/auth/auth.service.spec.ts b/backend/src/auth/auth.service.spec.ts index e961552..a885de3 100644 --- a/backend/src/auth/auth.service.spec.ts +++ b/backend/src/auth/auth.service.spec.ts @@ -72,7 +72,7 @@ describe('FriendsService Test', () => { (usersRepository.findBySocialIdAndSocialType as jest.Mock).mockResolvedValue(null); (usersRepository.createUser as jest.Mock).mockResolvedValue(mockUser); - (jwtService.sign as jest.Mock).mockResolvedValue('mocked_token'); + (jwtService.sign as jest.Mock).mockReturnValue('mocked_token'); global.fetch = jest.fn().mockImplementation((url: string) => { if (url === NAVER_OAUTH_URL) { return tokenResponse; @@ -100,7 +100,7 @@ describe('FriendsService Test', () => { profileResponse.status = 200; (usersRepository.findBySocialIdAndSocialType as jest.Mock).mockResolvedValue(mockUser); - (jwtService.sign as jest.Mock).mockResolvedValue('mocked_token'); + (jwtService.sign as jest.Mock).mockReturnValue('mocked_token'); global.fetch = jest.fn().mockImplementation((url: string) => { if (url === NAVER_OAUTH_URL) { return tokenResponse; diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts index 0d6b6ba..1993c82 100644 --- a/backend/src/auth/auth.service.ts +++ b/backend/src/auth/auth.service.ts @@ -39,7 +39,7 @@ export class AuthService { const accessKey = uuidv4(); this.authRepository.setRefreshToken(accessKey); - const accessToken = await this.jwtService.sign({ + const accessToken = this.jwtService.sign({ id: user.id, nickname: user.nickname, accessKey,