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', 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 80% rename from backend/was/test/jest-e2e.json rename to backend/was/jest.config.json index 24bc96b3..77da37d0 100644 --- a/backend/was/test/jest-e2e.json +++ b/backend/was/jest.config.json @@ -1,8 +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", @@ -15,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" } } 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" - } } } 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/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/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.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/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/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/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/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 new file mode 100644 index 00000000..bfa5d429 --- /dev/null +++ b/backend/was/src/members/dto/member.dto.ts @@ -0,0 +1,12 @@ +import { PartialType } from '@nestjs/swagger'; +import { Member } from '../entities'; +import { CreateMemberDto } from './create-member.dto'; + +export class MemberDto extends PartialType(CreateMemberDto) { + static fromEntity(entity: Member): MemberDto { + return { + nickname: entity.nickname ?? '', + profileUrl: entity.profileUrl, + }; + } +} diff --git a/backend/was/src/members/members.controller.ts b/backend/was/src/members/members.controller.ts new file mode 100644 index 00000000..a07b0772 --- /dev/null +++ b/backend/was/src/members/members.controller.ts @@ -0,0 +1,22 @@ +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, + req.user.providerId, + ); + } +} 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(); 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 {} 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..44df3c0f --- /dev/null +++ b/backend/was/src/members/members.service.spec.ts @@ -0,0 +1,105 @@ +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: + 'https://kr.object.ncloudstorage.com/magicconch/basic/0.jpg', + }, + dto: { + nickname: '타로밀크티', + profileUrl: + 'https://kr.object.ncloudstorage.com/magicconch/basic/0.jpg', + }, + }, + { + input: { email: 'tarotmilktea2@kakao.com', providerId: 0 }, + entity: { + id: '12345678-1234-5678-1234-567812345671', + email: 'tarotmilktea2@kakao.com', + providerId: 0, + }, + dto: { + nickname: '타로밀크티2', + 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'], + }); + }); + }); +}); 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); + } +} 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 { 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'], }); }); 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) { diff --git a/backend/was/test/members.e2e-spec.ts b/backend/was/test/members.e2e-spec.ts new file mode 100644 index 00000000..d7a3225d --- /dev/null +++ b/backend/was/test/members.e2e-spec.ts @@ -0,0 +1,92 @@ +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('Members', () => { + 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 = + 'https://kr.object.ncloudstorage.com/magicconch/basic/0.jpg'; + await entityManager.save(member); + }); + + [ + { + scenario: + '로그인 한 사용자가 자신의 정보를 조회하면 해당하는 닉네임과 프로필 URL을 전송한다.', + cookie: `magicconch=${jwtToken}`, + status: 200, + body: { + nickname: '타로밀크티', + profileUrl: + 'https://kr.object.ncloudstorage.com/magicconch/basic/0.jpg', + }, + }, + { + 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)); + }); + }); + }); +});