Skip to content

Commit

Permalink
test: board users (#1452)
Browse files Browse the repository at this point in the history
  • Loading branch information
patricia-mdias authored May 3, 2023
1 parent 88fe4bc commit 50511b4
Show file tree
Hide file tree
Showing 19 changed files with 328 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import { BoardUserDtoFactory } from 'src/libs/test-utils/mocks/factories/dto/boa
import { UpdateFailedException } from 'src/libs/exceptions/updateFailedBadRequestException';
import { DeepMocked, createMock } from '@golevelup/ts-jest';
import { UseCase } from 'src/libs/interfaces/use-case.interface';
import {
BoardParticipantsPresenter,
UpdateBoardParticipantsUseCase
} from './update-board-participants.use-case';
import { BoardParticipantsPresenter, UpdateBoardUsersUseCase } from './update-board-users.use-case';
import UpdateBoardUserDto from 'src/modules/boardUsers/dto/update-board-user.dto';
import { CreateBoardUserServiceInterface } from 'src/modules/boardUsers/interfaces/services/create.board.user.service.interface';
import { DeleteBoardUserServiceInterface } from 'src/modules/boardUsers/interfaces/services/delete.board.user.service.interface';
Expand All @@ -24,7 +21,7 @@ const boardUserToRemove = BoardUserFactory.create();
const removedUsers = [boardUserToRemove._id];
const boardUserDto = BoardUserDtoFactory.create({ role: BoardRoles.MEMBER });

describe('UpdateBoardParticipantsUseCase', () => {
describe('UpdateBoardUsersUseCase', () => {
let useCase: UseCase<UpdateBoardUserDto, BoardParticipantsPresenter>;
let updateBoardUserServiceMock: DeepMocked<UpdateBoardUserServiceInterface>;
let createBoardUserServiceMock: DeepMocked<CreateBoardUserServiceInterface>;
Expand All @@ -33,7 +30,7 @@ describe('UpdateBoardParticipantsUseCase', () => {
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UpdateBoardParticipantsUseCase,
UpdateBoardUsersUseCase,
{
provide: CREATE_BOARD_USER_SERVICE,
useValue: createMock<CreateBoardUserServiceInterface>()
Expand All @@ -49,7 +46,7 @@ describe('UpdateBoardParticipantsUseCase', () => {
]
}).compile();

useCase = module.get(UpdateBoardParticipantsUseCase);
useCase = module.get(UpdateBoardUsersUseCase);
updateBoardUserServiceMock = module.get(UPDATE_BOARD_USER_SERVICE);
createBoardUserServiceMock = module.get(CREATE_BOARD_USER_SERVICE);
deleteBoardUserServiceMock = module.get(DELETE_BOARD_USER_SERVICE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import User from 'src/modules/users/entities/user.schema';
export type BoardParticipantsPresenter = BoardUser | BoardUser[];

@Injectable()
export class UpdateBoardParticipantsUseCase
export class UpdateBoardUsersUseCase
implements UseCase<UpdateBoardUserDto, BoardParticipantsPresenter>
{
constructor(
Expand Down
17 changes: 13 additions & 4 deletions backend/src/modules/boardUsers/boardusers.module.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import { Module } from '@nestjs/common';
import { Module, forwardRef } from '@nestjs/common';
import {
boardUserRepository,
createBoardUserService,
deleteBoardUserService,
getBoardUserService,
updateBoardUserService
updateBoardUserService,
updateBoardUsersUseCase
} from './boardusers.providers';
import { mongooseBoardUserModule } from 'src/infrastructure/database/mongoose.module';
import BoardUsersController from './controller/board-user.controller';
import BoardsModule from '../boards/boards.module';
import TeamUsersModule from '../teamUsers/teamusers.module';

@Module({
imports: [mongooseBoardUserModule],
imports: [
mongooseBoardUserModule,
forwardRef(() => BoardsModule),
forwardRef(() => TeamUsersModule)
],
providers: [
updateBoardUsersUseCase,
createBoardUserService,
getBoardUserService,
updateBoardUserService,
deleteBoardUserService,
boardUserRepository
],
controllers: [],
controllers: [BoardUsersController],
exports: [
createBoardUserService,
getBoardUserService,
Expand Down
9 changes: 9 additions & 0 deletions backend/src/modules/boardUsers/boardusers.providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,20 @@ import {
CREATE_BOARD_USER_SERVICE,
DELETE_BOARD_USER_SERVICE,
GET_BOARD_USER_SERVICE,
UPDATE_BOARD_USERS_USE_CASE,
UPDATE_BOARD_USER_SERVICE
} from './constants';
import DeleteBoardUserService from './services/delete.board.user.service';
import GetBoardUserService from './services/get.board.user.service';
import UpdateBoardUserService from './services/update.board.user.service';
import { UpdateBoardUsersUseCase } from './applications/update-board-users.use-case';

/* USE CASES */

export const updateBoardUsersUseCase = {
provide: UPDATE_BOARD_USERS_USE_CASE,
useClass: UpdateBoardUsersUseCase
};

/* SERVICE */

Expand Down
4 changes: 4 additions & 0 deletions backend/src/modules/boardUsers/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/* USE CASES */

export const UPDATE_BOARD_USERS_USE_CASE = 'UpdateBoardUsersUseCase';

/* SERVICES */

export const CREATE_BOARD_USER_SERVICE = 'CreateBoardUserService';
Expand Down
79 changes: 79 additions & 0 deletions backend/src/modules/boardUsers/controller/board-user.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Body, Controller, Inject, Put, SetMetadata, UseGuards } from '@nestjs/common';
import {
ApiBadRequestResponse,
ApiBearerAuth,
ApiBody,
ApiForbiddenResponse,
ApiInternalServerErrorResponse,
ApiNotFoundResponse,
ApiOkResponse,
ApiOperation,
ApiParam,
ApiTags,
ApiUnauthorizedResponse
} from '@nestjs/swagger';
import { TeamRoles } from 'src/libs/enum/team.roles';
import { BoardUserGuard } from 'src/libs/guards/boardRoles.guard';
import JwtAuthenticationGuard from 'src/libs/guards/jwtAuth.guard';
import { UpdateBoardPermissionsGuard } from 'src/libs/guards/updateBoardPermissions.guard';
import { UseCase } from 'src/libs/interfaces/use-case.interface';
import { BadRequestResponse } from 'src/libs/swagger/errors/bad-request.swagger';
import { ForbiddenResponse } from 'src/libs/swagger/errors/forbidden.swagger';
import { InternalServerErrorResponse } from 'src/libs/swagger/errors/internal-server-error.swagger';
import { NotFoundResponse } from 'src/libs/swagger/errors/not-found.swagger';
import { UnauthorizedResponse } from 'src/libs/swagger/errors/unauthorized.swagger';
import UpdateBoardUserDto from 'src/modules/boardUsers/dto/update-board-user.dto';
import { BoardRoles } from 'src/modules/communication/dto/types';
import { BoardParticipantsPresenter } from 'src/modules/boardUsers/applications/update-board-users.use-case';
import BoardDto from 'src/modules/boards/dto/board.dto';
import { UPDATE_BOARD_USERS_USE_CASE } from '../constants';

const BoardUser = (permissions: string[]) => SetMetadata('permissions', permissions);

@ApiBearerAuth('access-token')
@ApiTags('Board Users')
@UseGuards(JwtAuthenticationGuard)
@Controller('boardUsers')
export default class BoardUsersController {
constructor(
@Inject(UPDATE_BOARD_USERS_USE_CASE)
private readonly updateBoardUsersUseCase: UseCase<
UpdateBoardUserDto,
BoardParticipantsPresenter
>
) {}

@ApiOperation({ summary: 'Update participants of a specific board' })
@ApiParam({ type: String, name: 'boardId', required: true })
@ApiBody({ type: UpdateBoardUserDto })
@ApiOkResponse({
type: BoardDto,
description: 'Board participants updated successfully!'
})
@ApiBadRequestResponse({
description: 'Bad Request',
type: BadRequestResponse
})
@ApiUnauthorizedResponse({
description: 'Unauthorized',
type: UnauthorizedResponse
})
@ApiNotFoundResponse({
type: NotFoundResponse,
description: 'Not found!'
})
@ApiForbiddenResponse({
description: 'Forbidden',
type: ForbiddenResponse
})
@ApiInternalServerErrorResponse({
description: 'Internal Server Error',
type: InternalServerErrorResponse
})
@BoardUser([BoardRoles.RESPONSIBLE, TeamRoles.ADMIN, TeamRoles.STAKEHOLDER])
@UseGuards(UpdateBoardPermissionsGuard, BoardUserGuard)
@Put(':boardId/participants')
updateBoardParticipants(@Body() boardData: UpdateBoardUserDto) {
return this.updateBoardUsersUseCase.execute(boardData);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { faker } from '@faker-js/faker';
import { BadRequestException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { DeepMocked, createMock } from '@golevelup/ts-jest';
import { CreateBoardUserServiceInterface } from '../interfaces/services/create.board.user.service.interface';
import CreateBoardUserService from './create.board.user.service';
import { BOARD_USER_REPOSITORY } from '../constants';
import { BoardUserRepositoryInterface } from '../interfaces/repositories/board-user.repository.interface';
import { BoardUserDtoFactory } from 'src/libs/test-utils/mocks/factories/dto/boardUserDto-factory.mock';
import BoardUserDto from '../dto/board.user.dto';
import { BoardUserFactory } from 'src/libs/test-utils/mocks/factories/boardUser-factory.mock';
import BoardUser from '../entities/board.user.schema';
import { BoardRoles } from 'src/libs/enum/board.roles';

const createBoardUserDtos: BoardUserDto[] = BoardUserDtoFactory.createMany(4);

const createdBoardUsers: BoardUser[] = BoardUserFactory.createMany(4, [
{ ...createBoardUserDtos[0] },
{ ...createBoardUserDtos[1] },
{ ...createBoardUserDtos[2] },
{ ...createBoardUserDtos[3] }
]);

describe('CreateBoardUserService', () => {
let boardUserService: CreateBoardUserServiceInterface;
let boardUserRepositoryMock: DeepMocked<BoardUserRepositoryInterface>;

beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
CreateBoardUserService,
{
provide: BOARD_USER_REPOSITORY,
useValue: createMock<BoardUserRepositoryInterface>()
}
]
}).compile();

boardUserService = module.get(CreateBoardUserService);
boardUserRepositoryMock = module.get(BOARD_USER_REPOSITORY);
});

beforeEach(() => {
jest.restoreAllMocks();
jest.clearAllMocks();

boardUserRepositoryMock.findOneByField.mockResolvedValue(null);
boardUserRepositoryMock.create.mockResolvedValue(null);
boardUserRepositoryMock.findOneByField.mockResolvedValue(null);
});

it('should be defined', () => {
expect(boardUserService).toBeDefined();
});

describe('saveBoardUsers', () => {
it('should create board users', async () => {
boardUserRepositoryMock.createBoardUsers.mockResolvedValue(createdBoardUsers);
await expect(
boardUserService.saveBoardUsers(createBoardUserDtos, faker.datatype.uuid())
).resolves.toStrictEqual(createdBoardUsers);
});

it('should throw Bad Request when team users are not created', async () => {
boardUserRepositoryMock.createBoardUsers.mockResolvedValue([]);
await expect(boardUserService.saveBoardUsers(createBoardUserDtos)).rejects.toThrow(
BadRequestException
);
});
});

describe('createBoardUser', () => {
it('should create board user', async () => {
const user = faker.datatype.uuid();
const boardUser: BoardUser = BoardUserFactory.create({ user });
boardUser.role = BoardRoles.MEMBER;

boardUserRepositoryMock.findOneByField.mockResolvedValue(null);
boardUserRepositoryMock.create.mockResolvedValue(boardUser);

await expect(
boardUserService.createBoardUser(boardUser.board as string, user)
).resolves.toStrictEqual(boardUser);
});

it('should throw Bad Request when board user already exists', async () => {
boardUserRepositoryMock.findOneByField.mockResolvedValue(createdBoardUsers[0]);
await expect(
boardUserService.createBoardUser(
createdBoardUsers[0].board as string,
faker.datatype.uuid()
)
).rejects.toThrowError(BadRequestException);
});

it('should throw Bad Request when board user is not created', async () => {
boardUserRepositoryMock.findOneByField.mockResolvedValue(null);
boardUserRepositoryMock.create.mockResolvedValue(null);
await expect(
boardUserService.createBoardUser(createBoardUserDtos[0].board, faker.datatype.uuid())
).rejects.toThrowError(BadRequestException);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { BoardRoles } from 'src/libs/enum/board.roles';
import { BOARD_USER_EXISTS, INSERT_FAILED } from 'src/libs/exceptions/messages';
import { BOARD_USER_EXISTS, CREATE_FAILED, INSERT_FAILED } from 'src/libs/exceptions/messages';
import { CreateBoardUserServiceInterface } from '../interfaces/services/create.board.user.service.interface';
import BoardUserDto from '../dto/board.user.dto';
import { BoardUserRepositoryInterface } from '../interfaces/repositories/board-user.repository.interface';
import { BOARD_USER_REPOSITORY } from '../constants';
import BoardUser from '../entities/board.user.schema';

@Injectable()
export default class CreateBoardUserService implements CreateBoardUserServiceInterface {
Expand All @@ -13,7 +14,7 @@ export default class CreateBoardUserService implements CreateBoardUserServiceInt
private readonly boardUserRepository: BoardUserRepositoryInterface
) {}

saveBoardUsers(newUsers: BoardUserDto[], newBoardId?: string, withSession?: boolean) {
async saveBoardUsers(newUsers: BoardUserDto[], newBoardId?: string, withSession?: boolean) {
let boardUsersToInsert: BoardUserDto[] = newUsers;

if (newBoardId) {
Expand All @@ -22,8 +23,15 @@ export default class CreateBoardUserService implements CreateBoardUserServiceInt
board: newBoardId
}));
}
const createdBoardUsers: BoardUser[] = await this.boardUserRepository.createBoardUsers(
boardUsersToInsert,
withSession
);

return this.boardUserRepository.createBoardUsers(boardUsersToInsert, withSession);
if (createdBoardUsers.length < boardUsersToInsert.length)
throw new BadRequestException(CREATE_FAILED);

return createdBoardUsers;
}

async createBoardUser(board: string, user: string) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Test, TestingModule } from '@nestjs/testing';
import { createMock } from '@golevelup/ts-jest';
import { BOARD_USER_REPOSITORY } from '../constants';
import { BoardUserRepositoryInterface } from '../interfaces/repositories/board-user.repository.interface';
import { DeleteBoardUserServiceInterface } from '../interfaces/services/delete.board.user.service.interface';
import DeleteBoardUserService from './delete.board.user.service';

describe('DeleteBoardUserService', () => {
let boardUserService: DeleteBoardUserServiceInterface;

beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
DeleteBoardUserService,
{
provide: BOARD_USER_REPOSITORY,
useValue: createMock<BoardUserRepositoryInterface>()
}
]
}).compile();

boardUserService = module.get(DeleteBoardUserService);
});

beforeEach(() => {
jest.restoreAllMocks();
jest.clearAllMocks();
});

it('should be defined', () => {
expect(boardUserService).toBeDefined();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default class DeleteBoardUserService implements DeleteBoardUserServiceInt
private readonly boardUserRepository: BoardUserRepositoryInterface
) {}

// these functions are direct queries to the database so they won't be tested
deleteDividedBoardUsers(
dividedBoards: Board[] | Schema.Types.ObjectId[] | string[],
withSession: boolean,
Expand Down
Loading

0 comments on commit 50511b4

Please sign in to comment.