diff --git a/package-lock.json b/package-lock.json index a27fad9..6489ae3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,7 +62,7 @@ "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", - "jest": "^29.5.0", + "jest": "^29.7.0", "prettier": "^3.0.0", "prisma": "^5.4.2", "source-map-support": "^0.5.21", diff --git a/package.json b/package.json index f048b10..21c4764 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", - "jest": "^29.5.0", + "jest": "^29.7.0", "prettier": "^3.0.0", "prisma": "^5.4.2", "source-map-support": "^0.5.21", diff --git a/src/users/users.controller.spec.ts b/src/users/users.controller.spec.ts index a76d310..4723dce 100644 --- a/src/users/users.controller.spec.ts +++ b/src/users/users.controller.spec.ts @@ -1,20 +1,180 @@ +// users.controller.spec.ts import { Test, TestingModule } from '@nestjs/testing'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; +// import { INestApplication } from '@nestjs/common'; +import * as request from 'supertest'; +import { UsersModule } from './users.module'; +import { PrismaService } from '../prisma/prisma.service'; -describe('UsersController', () => { +describe('UsersController unit test', () => { + // let app: INestApplication; let controller: UsersController; - beforeEach(async () => { + // const userServiceTest = { findAll: () => ['test1', 'test2'] }; + // console.log('userServiceTest:', userServiceTest); + + let requestMock = {}; + let responseMock = {}; + + beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [UsersController], + imports: [UsersModule], providers: [UsersService], - }).compile(); + }) + // .overrideProvider(UsersService) + // .useValue(userServiceTest) + .compile(); controller = module.get(UsersController); + // app = module.createNestApplication(); + // await app.init(); }); + // jest test it('should be defined', () => { expect(controller).toBeDefined(); }); + + describe('Unit Tests', () => { + + // 3. userId를 통한 유저 조회 + // @Get(':id') + // @UseGuards(JwtAccessAuthGuard) // passport를 사용하여 인증 확인 + // @ApiBearerAuth() // Swagger 문서에 Bearer 토큰 인증 추가 + // @ApiOperation({ summary: 'ID로 회원 조회' }) + // @ApiResponse({ status: 200, description: '유저 정보 조회 성공' }) + // async findOne(@Param('id') id: string) { + // const user = this.usersService.findOne(+id); + // if (!user) { + // throw new NotFoundException('User does not exist'); + // } + // return user; + // } + it('should return a status of 200', () => { + controller.findOne('1'); + expect(controller.findOne('1')).toBe('1'); + }); + }); + }); + +/* + // supertest + describe('User Signup Tests', () => { + const validUser = { + email: 'createUserDto@test.com', + password: 'password', + confirmPassword: 'password', + nickname: 'nicknameDTO', + intro: 'introDTO', + }; + + // TestCase 01: 회원가입 테스트 - 유효한 CreateUserDto + it('should create a user if CreateUserDto is valid', async () => { + await request(app.getHttpServer()) + .post('/users/signup') + .send(validUser) + .expect(201); + }); + + // TestCase 02: 회원가입 테스트 - 비밀번호와 비밀번호 확인이 일치하지 않을 때 + it('should not create a user if password and confirmPassword do not match', async () => { + const invalidUser = { ...validUser, confirmPassword: 'wrongPassword' }; + await request(app.getHttpServer()) + .post('/users/signup') + .send(invalidUser) + .expect(400); + }); + + // TestCase 03: 회원가입 테스트 - 이메일 중복 테스트 + it('should not create a user if email is invalid', async () => { + const invalidUser = { ...validUser, email: 'invalidEmail' }; + await request(app.getHttpServer()) + .post('/users/signup') + .send(invalidUser) + .expect(400); + }); + + // TestCase 04: 회원가입 테스트 - 닉네임 중복 테스트 + it('should not create a user if nickname is already taken', async () => { + const invalidUser = { ...validUser, nickname: 'existingNickname' }; + await request(app.getHttpServer()) + .post('/users/signup') + .send(invalidUser) + .expect(400); + }); + + // TestCase 05: 회원가입 테스트 - 회원가입 성공 테스트 create(createUserDto: CreateUserDto) + it('should successfully create a user', async () => { + const newUser = { + email: 'newUser@test.com', + password: 'newPassword', + confirmPassword: 'newPassword', + nickname: 'newNickname', + intro: 'newIntro', + }; + await request(app.getHttpServer()) + .post('/users/signup') + .send(newUser) + .expect(201); + }); + }); + + describe('User Operations Tests', () => { + // TestCase 06: 사용자 ID로 사용자 조회 테스트 findOne(id: number) + it('should find a user by ID', async () => { + const userId = 1; // 예시 ID + await request(app.getHttpServer()).get(`/users/${userId}`).expect(200); + }); + + // TestCase 07: 유저 본인 조회 테스트 (users/me) findMe + it('should find the current user', async () => { + await request(app.getHttpServer()).get('/users/me').expect(200); + }); + + // TestCase 08: 유저 정보 수정 테스트 update(id: number, updateUserDto: UpdateUserDto) + it('should update user information', async () => { + const userId = 1; // 예시 ID + const updateUserDto = { + nickname: 'newNickname', + intro: 'newIntro', + }; + await request(app.getHttpServer()) + .put(`/users/${userId}`) + .send(updateUserDto) + .expect(200); + }); + + // TestCase 09: 회원 탈퇴 테스트 remove(userId: number, password: string) + it('should remove a user', async () => { + const userId = 1; // 예시 ID + const password = 'password'; // 예시 비밀번호 + await request(app.getHttpServer()) + .delete(`/users/${userId}`) + .send({ password }) + .expect(200); + }); + + // TestCase 10: 사용자가 생성한 모임(Event) 리스트를 조회한다. findHostedEvents(id: number) + it('should find hosted events by user ID', async () => { + const userId = 1; // 예시 ID + await request(app.getHttpServer()) + .get(`/users/${userId}/hosted-events`) + .expect(200); + }); + + // TestCase 11: 사용자가 참여한 모임(Event) 리스트를 조회한다. findJoinedEvents(id: number) + it('should find joined events by user ID', async () => { + const userId = 1; // 예시 ID + await request(app.getHttpServer()) + .get(`/users/${userId}/joined-events`) + .expect(200); + }); + }); + + afterAll(async () => { + await app.close(); + }); +*/ diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index ebb51e6..cee2272 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -8,8 +8,10 @@ import { DeleteUserDto } from './dto/delete-user.dto'; import { ApiBearerAuth, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiResponse, ApiTags, ApiBody, ApiConsumes, ApiProperty } from '@nestjs/swagger'; import { UserEntity } from './entities/user.entity'; import { JwtAccessAuthGuard } from 'src/auth/guards/jwt-auth.guard'; +// import { JwtAccessAuthGuard } from '../auth/guards/jwt-auth.guard'; import { User } from '@prisma/client'; import { AwsS3Service } from 'src/aws/aws.s3'; +// import { AwsS3Service } from '../aws/aws.s3'; import { UploadedFile, UseInterceptors } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; @@ -118,7 +120,6 @@ export class UsersController { @ApiResponse({ status: 400, description: '중복된 닉네임입니다' }) @ApiResponse({ status: 401, description: '패스워드가 일치하지 않습니다' }) @ApiResponse({ status: 404, description: '유저 정보가 존재하지 않습니다' }) - async update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) { const user = await this.usersService.findOne(+id); if (!user) { @@ -197,6 +198,19 @@ export class UsersController { throw new NotFoundException('User does not exist'); } + // 이미지 파일 Validation 체크 + const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB + const SUPPORTED_FILE_TYPES = ["image/jpeg", "image/png", "image/gif"]; + if (!file) { + throw new NotFoundException('이미지 파일을 선택해주세요.'); + } + if (file.size > MAX_FILE_SIZE) { + throw new NotFoundException('파일 크기는 5MB를 초과할 수 없습니다.'); + } + if (!SUPPORTED_FILE_TYPES.includes(file.mimetype)) { + throw new NotFoundException('지원되는 파일 형식은 JPEG, PNG, GIF 뿐입니다.'); + } + // 이미지를 s3에 업로드한다. const uploadedFile = await this.awsS3Service.uploadFile(file) as { Location: string }; diff --git a/src/users/users.module.ts b/src/users/users.module.ts index 89ae518..a8da934 100644 --- a/src/users/users.module.ts +++ b/src/users/users.module.ts @@ -3,8 +3,11 @@ import { Module } from '@nestjs/common'; import { UsersService } from './users.service'; import { UsersController } from './users.controller'; import { PrismaModule } from 'src/prisma/prisma.module'; +// import { PrismaModule } from '../prisma/prisma.module'; import { AwsModule } from 'src/aws/aws.module'; import { AwsS3Service } from 'src/aws/aws.s3'; +// import { AwsModule } from '../aws/aws.module'; +// import { AwsS3Service } from '../aws/aws.s3'; @Module({ controllers: [UsersController], diff --git a/src/users/users.service.spec.ts b/src/users/users.service.spec.ts index 62815ba..59e3a31 100644 --- a/src/users/users.service.spec.ts +++ b/src/users/users.service.spec.ts @@ -15,4 +15,17 @@ describe('UsersService', () => { it('should be defined', () => { expect(service).toBeDefined(); }); + + it('TC06: should not create a user if email is invalid', async () => { + const createUserDto = { email: 'invalidEmail' }; + await expect(service.create(createUserDto)).rejects.toThrow(); + }); + + it('TC06: should not create a user if password and confirmPassword do not match', async () => { + const createUserDto = { + password: 'password1', + confirmPassword: 'password2' /* ... */, + }; + await expect(service.create(createUserDto)).rejects.toThrow(); + }); }); diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 9f49bc6..3e3e395 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -7,6 +7,7 @@ import { PrismaService } from 'src/prisma/prisma.service'; import * as bcrypt from 'bcrypt'; import { User, UserDetail } from '@prisma/client'; import { IUsersServiceFindByEmail, IUsersServiceFindByNickname } from './interfaces/users-service.interface'; +// import { PrismaService } from '../prisma/prisma.service';