Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

보드 모듈 리팩토링 #361

Merged
merged 9 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 78 additions & 88 deletions nestjs-BE/server/src/boards/boards.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { HttpStatus, NotFoundException } from '@nestjs/common';
import { BoardsController } from './boards.controller';
import { BoardsService } from './boards.service';
import { UploadService } from '../upload/upload.service';
import { Board } from './schemas/board.schema';
import { CreateBoardDto } from './dto/create-board.dto';
import { HttpStatus, NotFoundException } from '@nestjs/common';
import { UpdateWriteOpResult } from 'mongoose';
import { ConfigModule, ConfigService } from '@nestjs/config';

describe('BoardsController', () => {
let controller: BoardsController;
let boardsService: BoardsService;
let uploadService: UploadService;
let configService: ConfigService;

beforeEach(async () => {
Expand All @@ -22,126 +18,120 @@ describe('BoardsController', () => {
{
provide: BoardsService,
useValue: {
create: jest.fn(),
createBoard: jest.fn(),
deleteBoard: jest.fn(),
restoreBoard: jest.fn(),
},
},
{
provide: UploadService,
useValue: {
uploadFile: jest.fn(),
},
},
],
}).compile();

controller = module.get<BoardsController>(BoardsController);
boardsService = module.get<BoardsService>(BoardsService);
uploadService = module.get<UploadService>(UploadService);
configService = module.get<ConfigService>(ConfigService);
});

it('createBoard created board', async () => {
describe('createBoard', () => {
const bodyMock = {
boardName: 'board name',
spaceId: 'space uuid',
} as CreateBoardDto;
const imageMock = { filename: 'image' } as Express.Multer.File;
jest.spyOn(uploadService, 'uploadFile').mockResolvedValue('image url');
jest.spyOn(boardsService, 'create').mockResolvedValue({
uuid: 'board uuid',
createdAt: 'created date' as unknown as Date,
} as Board);

const board = controller.createBoard(bodyMock, imageMock);

await expect(board).resolves.toEqual({
statusCode: HttpStatus.CREATED,
message: 'Board created.',
data: {
boardId: 'board uuid',
date: 'created date',

it('created board', async () => {
(boardsService.createBoard as jest.Mock).mockResolvedValue({
uuid: 'board uuid',
createdAt: 'created date',
imageUrl: 'image url',
},
});

const board = controller.createBoard(bodyMock, imageMock);

await expect(board).resolves.toEqual({
statusCode: HttpStatus.CREATED,
message: 'Board created.',
data: {
boardId: 'board uuid',
date: 'created date',
imageUrl: 'image url',
},
});
});
expect(uploadService.uploadFile).toHaveBeenCalled();
});

it('createBoard request does not have image file', async () => {
const bodyMock = {
boardName: 'board name',
spaceId: 'space uuid',
} as CreateBoardDto;
jest.spyOn(boardsService, 'create').mockResolvedValue({
uuid: 'board uuid',
createdAt: 'created date' as unknown as Date,
} as Board);

const response = controller.createBoard(
bodyMock,
null as unknown as Express.Multer.File,
);

await expect(response).resolves.toEqual({
statusCode: HttpStatus.CREATED,
message: 'Board created.',
data: {
boardId: 'board uuid',
date: 'created date',
imageUrl: configService.get<string>('APP_ICON_URL'),
},
it('request does not have image file', async () => {
(boardsService.createBoard as jest.Mock).mockResolvedValue({
uuid: 'board uuid',
createdAt: 'created date',
});

const response = controller.createBoard(
bodyMock,
undefined as Express.Multer.File,
);

await expect(response).resolves.toEqual({
statusCode: HttpStatus.CREATED,
message: 'Board created.',
data: {
boardId: 'board uuid',
date: 'created date',
imageUrl: configService.get<string>('APP_ICON_URL'),
},
});
});
expect(uploadService.uploadFile).not.toHaveBeenCalled();
});

it('deleteBoard success', async () => {
describe('deleteBoard', () => {
const bodyMock = { boardId: 'board uuid' };
jest.spyOn(boardsService, 'deleteBoard').mockResolvedValue({
matchedCount: 1,
} as UpdateWriteOpResult);

const response = controller.deleteBoard(bodyMock);
it('success', async () => {
(boardsService.deleteBoard as jest.Mock).mockResolvedValue({
matchedCount: 1,
});

const response = controller.deleteBoard(bodyMock);

await expect(response).resolves.toEqual({
statusCode: HttpStatus.OK,
message: 'Board deleted.',
await expect(response).resolves.toEqual({
statusCode: HttpStatus.OK,
message: 'Board deleted.',
});
});
});

it('deleteBoard fail', async () => {
const bodyMock = { boardId: 'board uuid' };
jest.spyOn(boardsService, 'deleteBoard').mockResolvedValue({
matchedCount: 0,
} as UpdateWriteOpResult);
it('fail', async () => {
(boardsService.deleteBoard as jest.Mock).mockRejectedValue(
new NotFoundException(),
);

const response = controller.deleteBoard(bodyMock);
const response = controller.deleteBoard(bodyMock);

await expect(response).rejects.toThrow(NotFoundException);
await expect(response).rejects.toThrow(NotFoundException);
});
});

it('restoreBoard success', async () => {
describe('restoreBoard', () => {
const bodyMock = { boardId: 'board uuid' };
jest.spyOn(boardsService, 'restoreBoard').mockResolvedValue({
matchedCount: 1,
} as UpdateWriteOpResult);

const response = controller.restoreBoard(bodyMock);
it('success', async () => {
(boardsService.restoreBoard as jest.Mock).mockResolvedValue({
matchedCount: 1,
});

const response = controller.restoreBoard(bodyMock);

await expect(response).resolves.toEqual({
statusCode: HttpStatus.OK,
message: 'Board restored.',
await expect(response).resolves.toEqual({
statusCode: HttpStatus.OK,
message: 'Board restored.',
});
});
});

it('restoreBoard fail', async () => {
const bodyMock = { boardId: 'board uuid' };
jest.spyOn(boardsService, 'restoreBoard').mockResolvedValue({
matchedCount: 0,
} as UpdateWriteOpResult);
it('fail', async () => {
(boardsService.restoreBoard as jest.Mock).mockRejectedValue(
new NotFoundException(),
);

const response = controller.restoreBoard(bodyMock);
const response = controller.restoreBoard(bodyMock);

await expect(response).rejects.toThrow(NotFoundException);
await expect(response).rejects.toThrow(NotFoundException);
});
});
});
67 changes: 13 additions & 54 deletions nestjs-BE/server/src/boards/boards.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ import {
HttpStatus,
Query,
Patch,
NotFoundException,
UseInterceptors,
UploadedFile,
} from '@nestjs/common';
import { BoardsService } from './boards.service';
import { CreateBoardDto } from './dto/create-board.dto';
import {
ApiBody,
ApiConsumes,
Expand All @@ -23,7 +20,9 @@ import {
ApiQuery,
ApiTags,
} from '@nestjs/swagger';
import { Public } from '../auth/decorators/public.decorator';
import { FileInterceptor } from '@nestjs/platform-express';
import { BoardsService } from './boards.service';
import { CreateBoardDto } from './dto/create-board.dto';
import { DeleteBoardDto } from './dto/delete-board.dto';
import { RestoreBoardDto } from './dto/restore-board.dto';
import {
Expand All @@ -34,20 +33,12 @@ import {
RestoreBoardFailure,
RestoreBoardSuccess,
} from './swagger/boards.type';
import { FileInterceptor } from '@nestjs/platform-express';
import { UploadService } from '../upload/upload.service';
import { ConfigService } from '@nestjs/config';

const BOARD_EXPIRE_DAY = 7;
import { Public } from '../auth/decorators/public.decorator';

@Controller('boards')
@ApiTags('boards')
export class BoardsController {
constructor(
private boardsService: BoardsService,
private uploadService: UploadService,
private configService: ConfigService,
) {}
constructor(private boardsService: BoardsService) {}

@ApiOperation({
summary: '보드 생성',
Expand All @@ -67,14 +58,14 @@ export class BoardsController {
@Body() createBoardDto: CreateBoardDto,
@UploadedFile() image: Express.Multer.File,
) {
const imageUrl = image
? await this.uploadService.uploadFile(image)
: this.configService.get<string>('APP_ICON_URL');
const document = await this.boardsService.create(createBoardDto, imageUrl);
const document = await this.boardsService.createBoard(
createBoardDto,
image,
);
const responseData = {
boardId: document.uuid,
date: document.createdAt,
imageUrl,
imageUrl: document.imageUrl,
};
return {
statusCode: HttpStatus.CREATED,
Expand All @@ -100,32 +91,10 @@ export class BoardsController {
@Get('list')
async findBySpaceId(@Query('spaceId') spaceId: string) {
const boardList = await this.boardsService.findBySpaceId(spaceId);
const responseData = boardList.reduce<Array<any>>((list, board) => {
let isDeleted = false;

if (board.deletedAt && board.deletedAt > board.restoredAt) {
const expireDate = new Date(board.deletedAt);
expireDate.setDate(board.deletedAt.getDate() + BOARD_EXPIRE_DAY);
if (new Date() > expireDate) {
this.boardsService.deleteExpiredBoard(board.uuid);
return list;
}
isDeleted = true;
}

list.push({
boardId: board.uuid,
boardName: board.boardName,
createdAt: board.createdAt,
imageUrl: board.imageUrl,
isDeleted,
});
return list;
}, []);
return {
statusCode: HttpStatus.OK,
message: 'Retrieved board list.',
data: responseData,
data: boardList,
};
}

Expand All @@ -142,12 +111,7 @@ export class BoardsController {
@Public()
@Patch('delete')
async deleteBoard(@Body() deleteBoardDto: DeleteBoardDto) {
const updateResult = await this.boardsService.deleteBoard(
deleteBoardDto.boardId,
);
if (!updateResult.matchedCount) {
throw new NotFoundException('Target board not found.');
}
await this.boardsService.deleteBoard(deleteBoardDto.boardId);
return { statusCode: HttpStatus.OK, message: 'Board deleted.' };
}

Expand All @@ -164,12 +128,7 @@ export class BoardsController {
@Public()
@Patch('restore')
async restoreBoard(@Body() resotreBoardDto: RestoreBoardDto) {
const updateResult = await this.boardsService.restoreBoard(
resotreBoardDto.boardId,
);
if (!updateResult.matchedCount) {
throw new NotFoundException('Target board not found.');
}
await this.boardsService.restoreBoard(resotreBoardDto.boardId);
return { statusCode: HttpStatus.OK, message: 'Board restored.' };
}
}
2 changes: 1 addition & 1 deletion nestjs-BE/server/src/boards/boards.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { BoardsService } from './boards.service';
import { BoardsController } from './boards.controller';
import { MongooseModule } from '@nestjs/mongoose';
import { Board, BoardSchema } from './schemas/board.schema';
import { UploadModule } from '../upload/upload.module';

Expand Down
Loading
Loading