From 8e22030e38e190d13388d61c5fc4fa1a0fd236d4 Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 12:50:53 +0900 Subject: [PATCH 01/18] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC,=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용자의 닉네임 및 프로필 URL 조회 --- backend/was/src/members/members.controller.ts | 18 ++++++++++++ backend/was/src/members/members.service.ts | 29 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 backend/was/src/members/members.controller.ts create mode 100644 backend/was/src/members/members.service.ts diff --git a/backend/was/src/members/members.controller.ts b/backend/was/src/members/members.controller.ts new file mode 100644 index 00000000..1fffec3b --- /dev/null +++ b/backend/was/src/members/members.controller.ts @@ -0,0 +1,18 @@ +import { Controller, Get, Req, UseGuards } from '@nestjs/common'; +import { JwtAuthGuard } from '@auth/guard'; +import { MemberDto } from './dto/member.dto'; +import { MembersService } from './members.service'; + +@UseGuards(JwtAuthGuard) +@Controller('/members') +export class MembersController { + constructor(private readonly membersService: MembersService) {} + + @Get() + async findMemberByEmail(@Req() req: any): Promise { + return await this.membersService.findMemberByEmail( + req.user.email, + req.user.providerId, + ); + } +} diff --git a/backend/was/src/members/members.service.ts b/backend/was/src/members/members.service.ts new file mode 100644 index 00000000..81904f3c --- /dev/null +++ b/backend/was/src/members/members.service.ts @@ -0,0 +1,29 @@ +import { Repository } from 'typeorm'; +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { MEMBERS_CODEMAP } from '@exceptions/codemap/members-codemap'; +import { CustomException } from '@exceptions/custom-exception'; +import { MemberDto } from './dto/member.dto'; +import { Member } from './entities'; + +@Injectable() +export class MembersService { + constructor( + @InjectRepository(Member) + private readonly membersRepository: Repository, + ) {} + + async findMemberByEmail( + email: string, + providerId: number, + ): Promise { + const member: Member | null = await this.membersRepository.findOne({ + where: { email, providerId }, + select: ['nickname', 'profileUrl'], + }); + if (!member) { + throw new CustomException(MEMBERS_CODEMAP.NOT_FOUND); + } + return MemberDto.fromEntity(member); + } +} From 4b5331116cacb47d5fb446e14440852682b32e49 Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 13:12:04 +0900 Subject: [PATCH 02/18] =?UTF-8?q?feat:=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EB=B0=8F=20=ED=94=84=EB=A1=9C=EB=B0=94=EC=9D=B4?= =?UTF-8?q?=EB=8D=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/src/members/members.module.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/was/src/members/members.module.ts b/backend/was/src/members/members.module.ts index bb903e01..3533f607 100644 --- a/backend/was/src/members/members.module.ts +++ b/backend/was/src/members/members.module.ts @@ -1,8 +1,12 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Member } from './entities'; +import { MembersController } from './members.controller'; +import { MembersService } from './members.service'; @Module({ imports: [TypeOrmModule.forFeature([Member])], + controllers: [MembersController], + providers: [MembersService], }) export class MembersModule {} From 78d976672f5f4f1d6c33fac0840f56e82c9edcd8 Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 13:13:49 +0900 Subject: [PATCH 03/18] =?UTF-8?q?feat:=20DTO=20=EB=B0=8F=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EC=BD=94=EB=93=9C=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/src/exceptions/codemap/members-codemap.ts | 11 +++++++++++ backend/was/src/members/dto/member.dto.ts | 10 ++++++++++ 2 files changed, 21 insertions(+) create mode 100644 backend/was/src/exceptions/codemap/members-codemap.ts create mode 100644 backend/was/src/members/dto/member.dto.ts diff --git a/backend/was/src/exceptions/codemap/members-codemap.ts b/backend/was/src/exceptions/codemap/members-codemap.ts new file mode 100644 index 00000000..2995ae79 --- /dev/null +++ b/backend/was/src/exceptions/codemap/members-codemap.ts @@ -0,0 +1,11 @@ +import { ExceptionCodemap } from './type'; + +export const MEMBERS_CODEMAP: ExceptionCodemap = { + NOT_FOUND: { + status: 404, + message: '사용자를 찾을 수 없습니다.', + code: 'MME001', + description: + '해당 이메일에 해당하는 사용자가 존재하지 않습니다. 쿠키에 들어있는 email, providerId가 올바른지 확인해주세요.', + }, +}; diff --git a/backend/was/src/members/dto/member.dto.ts b/backend/was/src/members/dto/member.dto.ts new file mode 100644 index 00000000..3952fe54 --- /dev/null +++ b/backend/was/src/members/dto/member.dto.ts @@ -0,0 +1,10 @@ +import { Member } from '../entities'; + +export class MemberDto { + static fromEntity(entity: Member): MemberDto { + return { + nickname: entity.nickname, + profileUrl: entity.profileUrl, + }; + } +} From 2a4bc4a4f1bb93922401a105d177fb5bb0f7de75 Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 13:23:10 +0900 Subject: [PATCH 04/18] =?UTF-8?q?style:=20Shorthand=20Property=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 프로퍼티명과 변수명이 동일한 경우 이름 생략 --- backend/was/src/auth/service/auth.service.ts | 5 +---- backend/was/src/chat/chat.service.ts | 7 ++----- backend/was/src/tarot/tarot.service.ts | 7 ++----- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/backend/was/src/auth/service/auth.service.ts b/backend/was/src/auth/service/auth.service.ts index 9b685fca..04ae500a 100644 --- a/backend/was/src/auth/service/auth.service.ts +++ b/backend/was/src/auth/service/auth.service.ts @@ -107,10 +107,7 @@ export class AuthService { ): Promise { try { return await this.membersRepository.findOne({ - where: { - email: email, - providerId: providerId, - }, + where: { email, providerId }, select: ['id'], }); } catch (err: unknown) { diff --git a/backend/was/src/chat/chat.service.ts b/backend/was/src/chat/chat.service.ts index 5365e537..3286b2e2 100644 --- a/backend/was/src/chat/chat.service.ts +++ b/backend/was/src/chat/chat.service.ts @@ -230,7 +230,7 @@ export class ChatService { ): Promise { try { const room: ChattingRoom | null = await manager.findOne(ChattingRoom, { - where: { id: id }, + where: { id }, select: ['id', 'participant'], }); if (!room) { @@ -252,10 +252,7 @@ export class ChatService { ): Promise { try { const member: Member | null = await manager.findOne(Member, { - where: { - email: email, - providerId: providerId, - }, + where: { email, providerId }, select: ['id'], }); if (!member) { diff --git a/backend/was/src/tarot/tarot.service.ts b/backend/was/src/tarot/tarot.service.ts index 1cf41e54..4a190a29 100644 --- a/backend/was/src/tarot/tarot.service.ts +++ b/backend/was/src/tarot/tarot.service.ts @@ -33,10 +33,7 @@ export class TarotService { try { const tarotCard: TarotCard | null = await this.tarotCardRepository.findOne({ - where: { - cardNo: cardNo, - cardPack: undefined, - }, + where: { cardNo, cardPack: undefined }, select: ['cardNo', 'ext', 'cardPack'], }); @@ -53,7 +50,7 @@ export class TarotService { try { const tarotResult: TarotResult | null = await this.tarotResultRepository.findOne({ - where: { id: id }, + where: { id }, select: ['cardUrl', 'message'], }); if (!tarotResult) { From 8b91b486c45c97c3023ff4759b1136fb9ab0524f Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 16:53:21 +0900 Subject: [PATCH 05/18] =?UTF-8?q?test:=20MemberService=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../was/src/members/members.service.spec.ts | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 backend/was/src/members/members.service.spec.ts diff --git a/backend/was/src/members/members.service.spec.ts b/backend/was/src/members/members.service.spec.ts new file mode 100644 index 00000000..f131d501 --- /dev/null +++ b/backend/was/src/members/members.service.spec.ts @@ -0,0 +1,103 @@ +import { Repository } from 'typeorm'; +import { Test, TestingModule } from '@nestjs/testing'; +import { getRepositoryToken } from '@nestjs/typeorm'; +import { MEMBERS_CODEMAP } from '@exceptions/codemap/members-codemap'; +import { CustomException } from '@exceptions/custom-exception'; +import { MemberDto } from './dto/member.dto'; +import { Member } from './entities'; +import { MembersService } from './members.service'; + +describe('MembersService', () => { + let membersService: MembersService; + let membersRepository: Repository; + + beforeAll(async () => { + const moduleRef: TestingModule = await Test.createTestingModule({ + providers: [ + MembersService, + { + provide: getRepositoryToken(Member), + useClass: Repository, + }, + ], + }).compile(); + + membersService = moduleRef.get(MembersService); + membersRepository = moduleRef.get>( + getRepositoryToken(Member), + ); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(membersService).toBeDefined(); + }); + + describe('findMemberByEmail', () => { + it('이메일에 해당하는 사용자 정보를 조회한다.', async () => { + const testData = [ + { + input: { email: 'tarotmilktea@kakao.com', providerId: 0 }, + entity: { + id: '12345678-1234-5678-1234-567812345671', + email: 'tarotmilktea@kakao.com', + providerId: 0, + nickname: '타로밀크티', + profileUrl: 'profileUrl', + }, + dto: { + nickname: '타로밀크티', + profileUrl: 'profileUrl', + }, + }, + { + input: { email: 'tarotmilktea2@kakao.com', providerId: 0 }, + entity: { + id: '12345678-1234-5678-1234-567812345671', + email: 'tarotmilktea2@kakao.com', + providerId: 0, + }, + dto: { + nickname: undefined, + profileUrl: undefined, + }, + }, + ]; + for (const { input, entity, dto } of testData) { + const findOneMock = jest + .spyOn(membersRepository, 'findOne') + .mockResolvedValue(entity); + + const expectation: MemberDto = await membersService.findMemberByEmail( + input.email, + input.providerId, + ); + expect(expectation).toEqual(dto); + expect(findOneMock).toHaveBeenCalledWith({ + where: { email: input.email, providerId: input.providerId }, + select: ['nickname', 'profileUrl'], + }); + } + }); + + it('이메일에 해당하는 사용자가 없는 경우 에러를 반환한다.', async () => { + const email: string = 'tarotmilktea@kakao.com'; + const providerId: number = 0; + + const findOneMock = jest + .spyOn(membersRepository, 'findOne') + .mockResolvedValue(null); + + await expect( + membersService.findMemberByEmail(email, providerId), + ).rejects.toThrow(new CustomException(MEMBERS_CODEMAP.NOT_FOUND)); + expect(findOneMock).toHaveBeenCalledWith({ + where: { email, providerId }, + select: ['nickname', 'profileUrl'], + }); + }); + }); +}); From d22b126bcf1898388c6ff605300c894d60337e58 Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 17:03:44 +0900 Subject: [PATCH 06/18] =?UTF-8?q?style:=20Shorthand=20Property=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../was/src/auth/service/auth.service.spec.ts | 5 +-- backend/was/src/chat/chat.service.spec.ts | 15 ++------ backend/was/src/tarot/tarot.service.spec.ts | 37 ++++--------------- 3 files changed, 12 insertions(+), 45 deletions(-) diff --git a/backend/was/src/auth/service/auth.service.spec.ts b/backend/was/src/auth/service/auth.service.spec.ts index c4d61119..e8b78f73 100644 --- a/backend/was/src/auth/service/auth.service.spec.ts +++ b/backend/was/src/auth/service/auth.service.spec.ts @@ -134,10 +134,7 @@ describe('AuthService', () => { await expect( authService.login(id, providerId, profile, token), ).resolves.not.toThrow(); - expect(memberUpdateMock).toHaveBeenCalledWith( - { id: id }, - updateMemberDto, - ); + expect(memberUpdateMock).toHaveBeenCalledWith({ id }, updateMemberDto); }); }); }); diff --git a/backend/was/src/chat/chat.service.spec.ts b/backend/was/src/chat/chat.service.spec.ts index 6f10bdf3..49aa3fb3 100644 --- a/backend/was/src/chat/chat.service.spec.ts +++ b/backend/was/src/chat/chat.service.spec.ts @@ -102,11 +102,7 @@ describe('ChatService', () => { }, ]; for (const { memberId, email, providerId, roomId } of testData) { - const member: Member = { - id: memberId, - email: email, - providerId: providerId, - }; + const member: Member = { id: memberId, email, providerId }; const room: ChattingRoom = { id: roomId, participant: member, @@ -125,10 +121,7 @@ describe('ChatService', () => { result, userInfo, ); - expect(expectation).toEqual({ - memberId: memberId, - roomId: roomId, - }); + expect(expectation).toEqual({ memberId, roomId }); expect(transactionMock).toHaveBeenCalled(); } }); @@ -149,9 +142,7 @@ describe('ChatService', () => { }, ]; for (const { roomId, memberId, messages } of testData) { - const member: Member = { - id: memberId, - }; + const member: Member = { id: memberId }; const room: ChattingRoom = { id: roomId, participant: member, diff --git a/backend/was/src/tarot/tarot.service.spec.ts b/backend/was/src/tarot/tarot.service.spec.ts index 93e82038..94f1688b 100644 --- a/backend/was/src/tarot/tarot.service.spec.ts +++ b/backend/was/src/tarot/tarot.service.spec.ts @@ -67,11 +67,7 @@ describe('TarotService', () => { cardUrl: `${BUCKET_URL}/basic/2.jpg`, }, ].forEach(async ({ id, cardNo, message, cardUrl }) => { - const tarotResult: TarotResult = { - id: id, - message: message, - cardUrl: cardUrl, - }; + const tarotResult: TarotResult = { id, message, cardUrl }; const createTarotResultDto: CreateTarotResultDto = CreateTarotResultDto.fromResult(cardNo, message); @@ -82,10 +78,7 @@ describe('TarotService', () => { await expect( tarotService.createTarotResult(createTarotResultDto), ).resolves.not.toThrow(); - expect(saveMock).toHaveBeenCalledWith({ - cardUrl: cardUrl, - message: message, - }); + expect(saveMock).toHaveBeenCalledWith({ cardUrl, message }); }); }); }); @@ -109,11 +102,7 @@ describe('TarotService', () => { ext: ExtEnum.JPG, }, ].forEach(async ({ id, cardNo, ext }) => { - const tarotCard: TarotCard = { - id: id, - cardNo: cardNo, - ext: ext, - }; + const tarotCard: TarotCard = { id, cardNo, ext }; const tarotCardDto: TarotCardDto = TarotCardDto.fromEntity(tarotCard); const findOneMock = jest @@ -124,10 +113,7 @@ describe('TarotService', () => { await tarotService.findTarotCardByCardNo(tarotCard.cardNo); expect(expectation).toEqual(tarotCardDto); expect(findOneMock).toHaveBeenCalledWith({ - where: { - cardNo: cardNo, - cardPack: undefined, - }, + where: { cardNo, cardPack: undefined }, select: ['cardNo', 'ext', 'cardPack'], }); }); @@ -143,10 +129,7 @@ describe('TarotService', () => { tarotService.findTarotCardByCardNo(cardNo), ).rejects.toThrow(new CustomException(TAROT_CODEMAP.CARD_NOT_FOUND)); expect(findOneMock).toHaveBeenCalledWith({ - where: { - cardNo: cardNo, - cardPack: undefined, - }, + where: { cardNo, cardPack: undefined }, select: ['cardNo', 'ext', 'cardPack'], }); }); @@ -172,11 +155,7 @@ describe('TarotService', () => { cardUrl: `${BUCKET_URL}/basic/2.jpg`, }, ].forEach(async ({ id, message, cardUrl }) => { - const tarotResult: TarotResult = { - id: id, - message: message, - cardUrl: cardUrl, - }; + const tarotResult: TarotResult = { id, message, cardUrl }; const tarotResultDto = TarotResultDto.fromEntity(tarotResult); const findOneMock = jest @@ -187,7 +166,7 @@ describe('TarotService', () => { await tarotService.findTarotResultById(id); expect(expectation).toEqual(tarotResultDto); expect(findOneMock).toHaveBeenCalledWith({ - where: { id: id }, + where: { id }, select: ['cardUrl', 'message'], }); }); @@ -206,7 +185,7 @@ describe('TarotService', () => { new CustomException(TAROT_CODEMAP.RESULT_NOT_FOUND), ); expect(findOneMock).toHaveBeenCalledWith({ - where: { id: id }, + where: { id }, select: ['cardUrl', 'message'], }); }); From e3ab8b999dd75d8704b26a8db8df7be4112c140c Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 17:33:31 +0900 Subject: [PATCH 07/18] =?UTF-8?q?docs:=20verbose=20=EC=98=B5=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/test/jest-e2e.json | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/was/test/jest-e2e.json b/backend/was/test/jest-e2e.json index 24bc96b3..c0159ab2 100644 --- a/backend/was/test/jest-e2e.json +++ b/backend/was/test/jest-e2e.json @@ -1,4 +1,5 @@ { + "verbose": true, "moduleFileExtensions": ["js", "json", "ts"], "rootDir": "..", "moduleNameMapper": { From 1f57da296e695824eb7b9ac1c12c3308e05b9c1f Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 17:34:09 +0900 Subject: [PATCH 08/18] =?UTF-8?q?test:=20Member=20e2e=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/test/members.e2e-spec.ts | 90 ++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 backend/was/test/members.e2e-spec.ts diff --git a/backend/was/test/members.e2e-spec.ts b/backend/was/test/members.e2e-spec.ts new file mode 100644 index 00000000..5dcbe5e2 --- /dev/null +++ b/backend/was/test/members.e2e-spec.ts @@ -0,0 +1,90 @@ +import * as cookieParser from 'cookie-parser'; +import * as request from 'supertest'; +import { EntityManager } from 'typeorm'; +import { INestApplication } from '@nestjs/common'; +import { Test } from '@nestjs/testing'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { JwtAuthGuard } from '@auth/guard'; +import { JwtStrategy } from '@auth/strategies/jwt.strategy'; +import { Member } from '@members/entities'; +import { MembersController } from '@members/members.controller'; +import { MembersService } from '@members/members.service'; +import { diffJwtToken, id, jwtToken } from './common/constants'; +import { SqliteModule } from './common/database/sqlite.module'; + +describe('Member', () => { + let app: INestApplication; + let entityManager: EntityManager; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [SqliteModule, TypeOrmModule.forFeature([Member])], + controllers: [MembersController], + providers: [MembersService, JwtStrategy, JwtAuthGuard], + }).compile(); + + app = moduleRef.createNestApplication(); + entityManager = moduleRef.get(EntityManager); + + app.use(cookieParser()); + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + describe('GET /members', () => { + const route: string = '/members'; + + beforeAll(async () => { + const member: Member = new Member(); + member.id = id; + member.email = 'tarotmilktea@kakao.com'; + member.providerId = 0; + member.nickname = '타로밀크티'; + member.profileUrl = 'profileUrl'; + await entityManager.save(member); + }); + + [ + { + scenario: + '로그인 한 사용자가 자신의 정보를 조회하면 해당하는 닉네임과 프로필 URL을 전송한다.', + cookie: `magicconch=${jwtToken}`, + status: 200, + body: { + nickname: '타로밀크티', + profileUrl: 'profileUrl', + }, + }, + { + scenario: + '로그인 하지 않은 사용자가 사용자 정보 조회를 시도하면 401번 에러를 반환한다.', + status: 401, + }, + { + scenario: '존재하지 않는 사용자 정보를 받으면 404번 에러를 반환한다.', + cookie: `magicconch=${diffJwtToken}`, + status: 404, + }, + ].forEach(({ scenario, cookie, status, body }) => { + it(scenario, () => { + if (!cookie) { + return request(app.getHttpServer()).get(route).expect(status); + } + if (!body) { + return request(app.getHttpServer()) + .get(route) + .set('Cookie', cookie) + .expect(status); + } + return request(app.getHttpServer()) + .get(route) + .set('Cookie', cookie) + .expect(status) + .expect((res) => expect(res.body).toEqual(body)); + }); + }); + }); +}); From cc905e11298391cf1df67923c291e7a071c17dc4 Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 18:49:34 +0900 Subject: [PATCH 09/18] =?UTF-8?q?refactor:=20jest=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=A4=91=EB=B3=B5=20=EC=A0=95=EC=9D=98=20?= =?UTF-8?q?=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 단위 테스트, e2e 테스트를 위한 jest 설정이 거의 유사하지만 중복으로 정의되어 있었음 - 이를 js를 통해 동적으로 설정해주도록 구현함 - jest.config.json : 공통 부분 정의 - jest.config.js : 파라미터를 전달받아 동적으로 jest 설정정보 구성 - jest-unit.config.js & jest-e2e.config.js : 각각 단위/e2e 테스트를 위한 설정정보를 반환하는 함수 --- backend/was/jest-e2e.config.js | 3 +++ backend/was/jest-unit.config.js | 3 +++ backend/was/jest.config.js | 17 +++++++++++++++++ .../{test/jest-e2e.json => jest.config.json} | 15 ++++++++------- 4 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 backend/was/jest-e2e.config.js create mode 100644 backend/was/jest-unit.config.js create mode 100644 backend/was/jest.config.js rename backend/was/{test/jest-e2e.json => jest.config.json} (82%) diff --git a/backend/was/jest-e2e.config.js b/backend/was/jest-e2e.config.js new file mode 100644 index 00000000..4078d4bd --- /dev/null +++ b/backend/was/jest-e2e.config.js @@ -0,0 +1,3 @@ +const jestConfig = require('./jest.config'); + +module.exports = jestConfig('e2e'); diff --git a/backend/was/jest-unit.config.js b/backend/was/jest-unit.config.js new file mode 100644 index 00000000..894040d6 --- /dev/null +++ b/backend/was/jest-unit.config.js @@ -0,0 +1,3 @@ +const jestConfig = require('./jest.config'); + +module.exports = jestConfig('unit'); diff --git a/backend/was/jest.config.js b/backend/was/jest.config.js new file mode 100644 index 00000000..bd571855 --- /dev/null +++ b/backend/was/jest.config.js @@ -0,0 +1,17 @@ +const commonJestConfig = require('./jest.config.json'); + +module.exports = (target) => { + if (target === 'unit') { + return { + ...commonJestConfig, + testRegex: '.*\\.spec\\.ts$', + }; + } + if (target === 'e2e') { + return { + ...commonJestConfig, + testRegex: '.e2e-spec.ts$', + }; + } + return commonJestConfig; +}; diff --git a/backend/was/test/jest-e2e.json b/backend/was/jest.config.json similarity index 82% rename from backend/was/test/jest-e2e.json rename to backend/was/jest.config.json index c0159ab2..77da37d0 100644 --- a/backend/was/test/jest-e2e.json +++ b/backend/was/jest.config.json @@ -1,9 +1,15 @@ { "verbose": true, + "moduleDirectories": ["node_modules", "src"], "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": "..", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "collectCoverageFrom": ["**/*.(t|j)s"], + "coverageDirectory": "../coverage", + "testEnvironment": "node", "moduleNameMapper": { - "^src/(.*)$": "/src/$1", + "src/(.*)": "/src/$1", "@auth/(.*)$": "/src/auth/$1", "@chat/(.*)$": "/src/chat/$1", "@chatbot/(.*)$": "/src/chatbot/$1", @@ -16,10 +22,5 @@ "@members/(.*)$": "/src/members/$1", "@socket/(.*)$": "/src/socket/$1", "@tarot/(.*)$": "/src/tarot/$1" - }, - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" } } From 42f43d44805ff43b9dc340ff6b167d55d3bdc1ff Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 18:53:51 +0900 Subject: [PATCH 10/18] =?UTF-8?q?chore:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20jest=20=EC=84=A4=EC=A0=95=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/package.json | 41 ++-------------------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/backend/was/package.json b/backend/was/package.json index 02dbbd25..3789bb2a 100644 --- a/backend/was/package.json +++ b/backend/was/package.json @@ -13,11 +13,11 @@ "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest --runInBand", + "test": "jest --config ./jest-unit.config.js --runInBand", "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json --runInBand" + "test:e2e": "jest --config ./jest-e2e.config.js --runInBand" }, "lint-staged": { "{src,apps,libs,test}/**/*.ts": [ @@ -77,42 +77,5 @@ "ts-loader": "^9.4.3", "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0" - }, - "jest": { - "verbose": true, - "moduleDirectories": [ - "node_modules", - "src" - ], - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node", - "moduleNameMapper": { - "src/(.*)": "/$1", - "@auth/(.*)$": "/auth/$1", - "@chat/(.*)$": "/chat/$1", - "@chatbot/(.*)$": "/chatbot/$1", - "@common/(.*)$": "/common/$1", - "@config/(.*)$": "/common/config/$1", - "@constants/(.*)$": "/common/constants/$1", - "@interceptors/(.*)$": "/common/interceptors/$1", - "@exceptions/(.*)$": "/exceptions/$1", - "@logger/(.*)$": "/logger/$1", - "@members/(.*)$": "/members/$1", - "@socket/(.*)$": "/socket/$1", - "@tarot/(.*)$": "/tarot/$1" - } } } From d8ac96f0b7f449e621d12576d4bfc7fb6623bc0f Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 18:54:37 +0900 Subject: [PATCH 11/18] =?UTF-8?q?docs:=20ignorePatterns=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/.eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/was/.eslintrc.js b/backend/was/.eslintrc.js index f318ed7c..59fefee4 100644 --- a/backend/was/.eslintrc.js +++ b/backend/was/.eslintrc.js @@ -15,7 +15,7 @@ module.exports = { node: true, jest: true, }, - ignorePatterns: ['.eslintrc.js'], + ignorePatterns: ['.eslintrc.js', 'jest*.config.js'], rules: { 'no-console': 'warn', '@typescript-eslint/no-unused-vars': 'off', From 00603700728e4b6b20ffe50a5a2b7dbc6d1a1a08 Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 19:12:56 +0900 Subject: [PATCH 12/18] =?UTF-8?q?feat:=20swagger=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=A0=95=EB=B3=B4=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/src/members/dto/create-member.dto.ts | 9 +++++++++ backend/was/src/members/dto/member.dto.ts | 6 ++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/backend/was/src/members/dto/create-member.dto.ts b/backend/was/src/members/dto/create-member.dto.ts index f91ba4f6..93b412a5 100644 --- a/backend/was/src/members/dto/create-member.dto.ts +++ b/backend/was/src/members/dto/create-member.dto.ts @@ -6,6 +6,7 @@ import { IsString, IsUrl, } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; import { ProviderIdEnum } from '@constants/etc'; import { ProfileDto } from '@auth/dto'; @@ -18,10 +19,18 @@ export class CreateMemberDto { readonly providerId: number; @IsString() + @ApiProperty({ + description: '사용자 닉네임', + required: true, + }) readonly nickname: string; @IsUrl() @IsOptional() + @ApiProperty({ + description: '사용자 프로필 URL', + required: false, + }) readonly profileUrl?: string; static fromProfile(providerId: number, profile: ProfileDto): CreateMemberDto { diff --git a/backend/was/src/members/dto/member.dto.ts b/backend/was/src/members/dto/member.dto.ts index 3952fe54..bfa5d429 100644 --- a/backend/was/src/members/dto/member.dto.ts +++ b/backend/was/src/members/dto/member.dto.ts @@ -1,9 +1,11 @@ +import { PartialType } from '@nestjs/swagger'; import { Member } from '../entities'; +import { CreateMemberDto } from './create-member.dto'; -export class MemberDto { +export class MemberDto extends PartialType(CreateMemberDto) { static fromEntity(entity: Member): MemberDto { return { - nickname: entity.nickname, + nickname: entity.nickname ?? '', profileUrl: entity.profileUrl, }; } From 69020f373c10184fcf14df8413154eb889108b65 Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 19:16:33 +0900 Subject: [PATCH 13/18] =?UTF-8?q?style:=20path=20alias=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20prettier=20=EC=9E=AC?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/src/auth/dto/auth-status.dto.ts | 2 +- backend/was/src/chat/dto/chatting-message.dto.ts | 2 +- backend/was/src/chat/dto/chatting-room.dto.ts | 2 +- backend/was/src/chat/dto/update-chatting-room.dto.ts | 2 +- backend/was/src/tarot/dto/tarot-result.dto.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/was/src/auth/dto/auth-status.dto.ts b/backend/was/src/auth/dto/auth-status.dto.ts index 26708544..ecf40a0e 100644 --- a/backend/was/src/auth/dto/auth-status.dto.ts +++ b/backend/was/src/auth/dto/auth-status.dto.ts @@ -1,5 +1,5 @@ -import { ApiProperty } from '@nestjs/swagger'; import { IsBoolean } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; export class AuthStatusDto { @IsBoolean() diff --git a/backend/was/src/chat/dto/chatting-message.dto.ts b/backend/was/src/chat/dto/chatting-message.dto.ts index 7d51ed24..7c1549d8 100644 --- a/backend/was/src/chat/dto/chatting-message.dto.ts +++ b/backend/was/src/chat/dto/chatting-message.dto.ts @@ -1,5 +1,5 @@ -import { ApiProperty } from '@nestjs/swagger'; import { IsBoolean, IsString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; import { ChattingMessage } from '../entities'; export class ChattingMessageDto { diff --git a/backend/was/src/chat/dto/chatting-room.dto.ts b/backend/was/src/chat/dto/chatting-room.dto.ts index 0769ed9b..16187dfb 100644 --- a/backend/was/src/chat/dto/chatting-room.dto.ts +++ b/backend/was/src/chat/dto/chatting-room.dto.ts @@ -1,5 +1,5 @@ -import { ApiProperty } from '@nestjs/swagger'; import { IsArray, IsDate, IsString, IsUUID } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; import { ChattingRoom } from '../entities'; export class ChattingRoomDto { diff --git a/backend/was/src/chat/dto/update-chatting-room.dto.ts b/backend/was/src/chat/dto/update-chatting-room.dto.ts index d9c89d78..7bf7d30d 100644 --- a/backend/was/src/chat/dto/update-chatting-room.dto.ts +++ b/backend/was/src/chat/dto/update-chatting-room.dto.ts @@ -1,5 +1,5 @@ -import { ApiProperty } from '@nestjs/swagger'; import { IsString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; export class UpdateChattingRoomDto { @IsString() diff --git a/backend/was/src/tarot/dto/tarot-result.dto.ts b/backend/was/src/tarot/dto/tarot-result.dto.ts index a6778f67..b9e6c3bf 100644 --- a/backend/was/src/tarot/dto/tarot-result.dto.ts +++ b/backend/was/src/tarot/dto/tarot-result.dto.ts @@ -1,5 +1,5 @@ -import { ApiProperty } from '@nestjs/swagger'; import { IsString, IsUrl } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; import { TarotResult } from '../entities'; export class TarotResultDto { From 6049fe96cd9080e4dbe33b61606e3960a83bbcea Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 19:17:25 +0900 Subject: [PATCH 14/18] =?UTF-8?q?feat:=20MembersController=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EB=8D=B0=EC=BD=94=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/src/members/members.decorators.ts | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 backend/was/src/members/members.decorators.ts diff --git a/backend/was/src/members/members.decorators.ts b/backend/was/src/members/members.decorators.ts new file mode 100644 index 00000000..5fdb209d --- /dev/null +++ b/backend/was/src/members/members.decorators.ts @@ -0,0 +1,6 @@ +import { SwaggerDecoratorBuilder } from '@kimyu0218/swagger-decorator-builder'; + +export const FindMemberByEmailDecorator = (target: string, returnType: any) => + new SwaggerDecoratorBuilder(target, 'GET', returnType) + .removeResponse(403) + .build(); From 60ae51def3ccb7750bacb041433da1819d1874cd Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 19:18:33 +0900 Subject: [PATCH 15/18] =?UTF-8?q?feat:=20swagger=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EB=8D=B0=EC=BD=94=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/src/members/members.controller.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/was/src/members/members.controller.ts b/backend/was/src/members/members.controller.ts index 1fffec3b..a07b0772 100644 --- a/backend/was/src/members/members.controller.ts +++ b/backend/was/src/members/members.controller.ts @@ -1,14 +1,18 @@ import { Controller, Get, Req, UseGuards } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; import { JwtAuthGuard } from '@auth/guard'; import { MemberDto } from './dto/member.dto'; +import { FindMemberByEmailDecorator } from './members.decorators'; import { MembersService } from './members.service'; @UseGuards(JwtAuthGuard) @Controller('/members') +@ApiTags('✅ Members API') export class MembersController { constructor(private readonly membersService: MembersService) {} @Get() + @FindMemberByEmailDecorator('사용자 정보', MemberDto) async findMemberByEmail(@Req() req: any): Promise { return await this.membersService.findMemberByEmail( req.user.email, From 91bf5f36b66b94f21bd85002f8cbf76d826fe64c Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 19:22:57 +0900 Subject: [PATCH 16/18] =?UTF-8?q?style:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/src/members/members.service.spec.ts | 6 ++++-- backend/was/test/members.e2e-spec.ts | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/was/src/members/members.service.spec.ts b/backend/was/src/members/members.service.spec.ts index f131d501..34155647 100644 --- a/backend/was/src/members/members.service.spec.ts +++ b/backend/was/src/members/members.service.spec.ts @@ -46,11 +46,13 @@ describe('MembersService', () => { email: 'tarotmilktea@kakao.com', providerId: 0, nickname: '타로밀크티', - profileUrl: 'profileUrl', + profileUrl: + 'https://kr.object.ncloudstorage.com/magicconch/basic/0.jpg', }, dto: { nickname: '타로밀크티', - profileUrl: 'profileUrl', + profileUrl: + 'https://kr.object.ncloudstorage.com/magicconch/basic/0.jpg', }, }, { diff --git a/backend/was/test/members.e2e-spec.ts b/backend/was/test/members.e2e-spec.ts index 5dcbe5e2..dd524121 100644 --- a/backend/was/test/members.e2e-spec.ts +++ b/backend/was/test/members.e2e-spec.ts @@ -43,7 +43,8 @@ describe('Member', () => { member.email = 'tarotmilktea@kakao.com'; member.providerId = 0; member.nickname = '타로밀크티'; - member.profileUrl = 'profileUrl'; + member.profileUrl = + 'https://kr.object.ncloudstorage.com/magicconch/basic/0.jpg'; await entityManager.save(member); }); @@ -55,7 +56,8 @@ describe('Member', () => { status: 200, body: { nickname: '타로밀크티', - profileUrl: 'profileUrl', + profileUrl: + 'https://kr.object.ncloudstorage.com/magicconch/basic/0.jpg', }, }, { From 17724d3565dd1e007a455182a1dda9f8843984a7 Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 19:25:54 +0900 Subject: [PATCH 17/18] =?UTF-8?q?fix:=20nickname=EC=9D=80=20=ED=95=84?= =?UTF-8?q?=EC=88=98=20=EB=8F=99=EC=9D=98=ED=95=AD=EB=AA=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/src/members/members.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/was/src/members/members.service.spec.ts b/backend/was/src/members/members.service.spec.ts index 34155647..44df3c0f 100644 --- a/backend/was/src/members/members.service.spec.ts +++ b/backend/was/src/members/members.service.spec.ts @@ -63,7 +63,7 @@ describe('MembersService', () => { providerId: 0, }, dto: { - nickname: undefined, + nickname: '타로밀크티2', profileUrl: undefined, }, }, From 86feaed94531b3c01cea4f720caa871dba08153a Mon Sep 17 00:00:00 2001 From: kimyu0218 Date: Wed, 6 Mar 2024 19:34:15 +0900 Subject: [PATCH 18/18] =?UTF-8?q?style:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20de?= =?UTF-8?q?scription=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/was/test/members.e2e-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/was/test/members.e2e-spec.ts b/backend/was/test/members.e2e-spec.ts index dd524121..d7a3225d 100644 --- a/backend/was/test/members.e2e-spec.ts +++ b/backend/was/test/members.e2e-spec.ts @@ -12,7 +12,7 @@ import { MembersService } from '@members/members.service'; import { diffJwtToken, id, jwtToken } from './common/constants'; import { SqliteModule } from './common/database/sqlite.module'; -describe('Member', () => { +describe('Members', () => { let app: INestApplication; let entityManager: EntityManager;