generated from xgeekshq/oss-template
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: unmergecardservice to usecase and tests (#1312)
- Loading branch information
1 parent
12b648d
commit aa202d9
Showing
13 changed files
with
338 additions
and
186 deletions.
There are no files selected for viewing
164 changes: 164 additions & 0 deletions
164
backend/src/modules/cards/applications/unmerge-card.use-case.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { TYPES } from '../interfaces/types'; | ||
import { DeepMocked, createMock } from '@golevelup/ts-jest'; | ||
import { CardRepositoryInterface } from '../repository/card.repository.interface'; | ||
import { UnmergeCardUseCase } from './unmerge-card.use-case'; | ||
import { GetCardServiceInterface } from '../interfaces/services/get.card.service.interface'; | ||
import UnmergeCardUseCaseDto from '../dto/useCase/unmerge-card.use-case.dto'; | ||
import faker from '@faker-js/faker'; | ||
import { CardFactory } from 'src/libs/test-utils/mocks/factories/card-factory.mock'; | ||
import { BoardFactory } from 'src/libs/test-utils/mocks/factories/board-factory.mock'; | ||
import { BadRequestException } from '@nestjs/common'; | ||
|
||
const unmergeCardDto: UnmergeCardUseCaseDto = { | ||
boardId: faker.datatype.uuid(), | ||
cardGroupId: faker.datatype.uuid(), | ||
draggedCardId: faker.datatype.uuid(), | ||
columnId: faker.datatype.uuid(), | ||
position: faker.datatype.number() | ||
}; | ||
|
||
const updateResultMock = { | ||
acknowledged: true, | ||
matchedCount: 1, | ||
modifiedCount: 1, | ||
upsertedCount: 1, | ||
upsertedId: null | ||
}; | ||
const cardMock = CardFactory.create(); | ||
const boardMock = BoardFactory.create(); | ||
|
||
describe('UnmergeCardUseCase', () => { | ||
let useCase: UnmergeCardUseCase; | ||
let cardRepositoryMock: DeepMocked<CardRepositoryInterface>; | ||
let cardServiceMock: DeepMocked<GetCardServiceInterface>; | ||
|
||
beforeAll(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
UnmergeCardUseCase, | ||
{ | ||
provide: TYPES.services.GetCardService, | ||
useValue: createMock<GetCardServiceInterface>() | ||
}, | ||
{ | ||
provide: TYPES.repository.CardRepository, | ||
useValue: createMock<CardRepositoryInterface>() | ||
} | ||
] | ||
}).compile(); | ||
useCase = module.get<UnmergeCardUseCase>(UnmergeCardUseCase); | ||
cardRepositoryMock = module.get(TYPES.repository.CardRepository); | ||
cardServiceMock = module.get(TYPES.services.GetCardService); | ||
cardServiceMock.getCardItemFromGroup.mockResolvedValue(cardMock); | ||
cardServiceMock.getCardFromBoard.mockResolvedValue(cardMock); | ||
cardRepositoryMock.pullItem.mockResolvedValue(updateResultMock); | ||
cardRepositoryMock.updateCardFromGroupOnUnmerge.mockResolvedValue(boardMock); | ||
}); | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
jest.restoreAllMocks(); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(useCase).toBeDefined(); | ||
}); | ||
|
||
it('should call repository startTransaction', async () => { | ||
await useCase.execute(unmergeCardDto); | ||
expect(cardRepositoryMock.startTransaction).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should call repository commitTransaction', async () => { | ||
await useCase.execute(unmergeCardDto); | ||
expect(cardRepositoryMock.commitTransaction).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should call repository endSession', async () => { | ||
await useCase.execute(unmergeCardDto); | ||
expect(cardRepositoryMock.endSession).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should call cardService.getCardItemFromGroup with boardId and draggedCardId', async () => { | ||
await useCase.execute(unmergeCardDto); | ||
expect(cardServiceMock.getCardItemFromGroup).toHaveBeenNthCalledWith( | ||
1, | ||
unmergeCardDto.boardId, | ||
unmergeCardDto.draggedCardId | ||
); | ||
}); | ||
|
||
it('should throw badRequest if getCardItemFromGroup fails', async () => { | ||
cardServiceMock.getCardItemFromGroup.mockResolvedValueOnce(null); | ||
await expect(useCase.execute(unmergeCardDto)).rejects.toThrow(BadRequestException); | ||
await expect(cardRepositoryMock.endSession).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should call pullItem ', async () => { | ||
await useCase.execute(unmergeCardDto); | ||
expect(cardRepositoryMock.pullItem).toHaveBeenNthCalledWith( | ||
1, | ||
unmergeCardDto.boardId, | ||
unmergeCardDto.draggedCardId, | ||
true | ||
); | ||
}); | ||
|
||
it('should throw badRequest if pullItem fails', async () => { | ||
updateResultMock.modifiedCount = 2; | ||
cardRepositoryMock.pullItem.mockResolvedValueOnce(updateResultMock); | ||
|
||
await expect(useCase.execute(unmergeCardDto)).rejects.toThrow(BadRequestException); | ||
await expect(cardRepositoryMock.abortTransaction).toHaveBeenCalledTimes(1); | ||
await expect(cardRepositoryMock.endSession).toHaveBeenCalledTimes(1); | ||
updateResultMock.modifiedCount = 1; | ||
}); | ||
|
||
it('should call cardService.getCardFromBoard with boardId and cardGroupId', async () => { | ||
await useCase.execute(unmergeCardDto); | ||
expect(cardServiceMock.getCardFromBoard).toHaveBeenNthCalledWith( | ||
1, | ||
unmergeCardDto.boardId, | ||
unmergeCardDto.cardGroupId | ||
); | ||
}); | ||
|
||
it('should throw badRequest if cardGroup not found', async () => { | ||
cardServiceMock.getCardFromBoard.mockResolvedValueOnce(null); | ||
await expect(useCase.execute(unmergeCardDto)).rejects.toThrow(BadRequestException); | ||
await expect(cardRepositoryMock.abortTransaction).toHaveBeenCalledTimes(1); | ||
await expect(cardRepositoryMock.endSession).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should throw badRequest if getCardFromBoard the new card', async () => { | ||
const newCardSavedMock = CardFactory.create(); | ||
newCardSavedMock.items[0]._id = null; | ||
cardServiceMock.getCardFromBoard | ||
.mockResolvedValueOnce(cardMock) | ||
.mockResolvedValueOnce(newCardSavedMock); | ||
|
||
await expect(useCase.execute(unmergeCardDto)).rejects.toThrow(BadRequestException); | ||
await expect(cardRepositoryMock.endSession).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should throw badRequest when cardRep.updateCardFromGroupOnUnmerge fails', async () => { | ||
cardRepositoryMock.updateCardFromGroupOnUnmerge.mockResolvedValueOnce(null); | ||
await expect(useCase.execute(unmergeCardDto)).rejects.toThrow(BadRequestException); | ||
await expect(cardRepositoryMock.abortTransaction).toHaveBeenCalledTimes(1); | ||
await expect(cardRepositoryMock.endSession).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should throw badRequest when pushCard fails', async () => { | ||
cardRepositoryMock.pushCard.mockResolvedValueOnce(null); | ||
await expect(useCase.execute(unmergeCardDto)).rejects.toThrow(BadRequestException); | ||
await expect(cardRepositoryMock.abortTransaction).toHaveBeenCalledTimes(1); | ||
await expect(cardRepositoryMock.endSession).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should throw badRequest when unexpected error occurs', async () => { | ||
cardServiceMock.getCardItemFromGroup.mockRejectedValueOnce(Error); | ||
await expect(useCase.execute(unmergeCardDto)).rejects.toThrow(BadRequestException); | ||
await expect(cardRepositoryMock.endSession).toHaveBeenCalledTimes(1); | ||
}); | ||
}); |
128 changes: 128 additions & 0 deletions
128
backend/src/modules/cards/applications/unmerge-card.use-case.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import { Inject, Injectable } from '@nestjs/common'; | ||
import { TYPES } from '../interfaces/types'; | ||
import { UseCase } from 'src/libs/interfaces/use-case.interface'; | ||
import { CardRepositoryInterface } from '../repository/card.repository.interface'; | ||
import { GetCardServiceInterface } from '../interfaces/services/get.card.service.interface'; | ||
import { | ||
CARD_NOT_FOUND, | ||
CARD_NOT_INSERTED, | ||
CARD_NOT_REMOVED, | ||
UPDATE_FAILED | ||
} from 'src/libs/exceptions/messages'; | ||
import UnmergeCardUseCaseDto from '../dto/useCase/unmerge-card.use-case.dto'; | ||
import { UpdateFailedException } from 'src/libs/exceptions/updateFailedBadRequestException'; | ||
|
||
@Injectable() | ||
export class UnmergeCardUseCase implements UseCase<UnmergeCardUseCaseDto, string> { | ||
constructor( | ||
@Inject(TYPES.services.GetCardService) | ||
private readonly cardService: GetCardServiceInterface, | ||
@Inject(TYPES.repository.CardRepository) | ||
private readonly cardRepository: CardRepositoryInterface | ||
) {} | ||
|
||
async execute(unMergeCardDto: UnmergeCardUseCaseDto) { | ||
const { boardId, draggedCardId } = unMergeCardDto; | ||
await this.cardRepository.startTransaction(); | ||
|
||
try { | ||
const cardItemToMove = await this.cardService.getCardItemFromGroup(boardId, draggedCardId); | ||
|
||
if (!cardItemToMove) throw Error(CARD_NOT_FOUND); | ||
|
||
const cardId = await this.unmergeCard(cardItemToMove, unMergeCardDto); | ||
|
||
await this.cardRepository.commitTransaction(); | ||
|
||
const newCardSaved = await this.cardService.getCardFromBoard(boardId, cardId); | ||
|
||
const itemId = newCardSaved.items[0]._id; | ||
|
||
if (!itemId) throw Error(UPDATE_FAILED); | ||
|
||
return itemId; | ||
} catch (e) { | ||
throw new UpdateFailedException(e.message ? e.message : UPDATE_FAILED); | ||
} finally { | ||
await this.cardRepository.endSession(); | ||
} | ||
} | ||
|
||
private async unmergeCard(cardItemToMove, unMergeCardDto) { | ||
const { boardId, cardGroupId, draggedCardId } = unMergeCardDto; | ||
try { | ||
//Removes card from group | ||
const pullResult = await this.cardRepository.pullItem(boardId, draggedCardId, true); | ||
|
||
if (pullResult.modifiedCount !== 1) throw Error(CARD_NOT_REMOVED); | ||
|
||
const cardGroup = await this.cardService.getCardFromBoard(boardId, cardGroupId); | ||
|
||
if (!cardGroup) throw Error(CARD_NOT_FOUND); | ||
|
||
await this.updateLastCardOnGroup(cardGroup, unMergeCardDto); | ||
|
||
const cardId = await this.createNewCard(cardItemToMove, unMergeCardDto); | ||
|
||
return cardId; | ||
} catch (e) { | ||
await this.cardRepository.abortTransaction(); | ||
throw Error(e.message); | ||
} | ||
} | ||
|
||
//When card group has only one item (!== draggedCardId) move the votes and comments to a new card | ||
private async updateLastCardOnGroup(cardGroup, unMergeCardDto) { | ||
const { boardId, cardGroupId, draggedCardId } = unMergeCardDto; | ||
|
||
const items = cardGroup.items.filter((item) => item._id.toString() !== draggedCardId); | ||
|
||
if (items.length === 1) { | ||
const [{ comments, votes: itemVotes }] = items; | ||
const newComments = cardGroup.comments.concat(comments); | ||
const newVotes = (cardGroup.votes as unknown as string[]).concat( | ||
itemVotes as unknown as string[] | ||
); | ||
|
||
const updateCard = await this.cardRepository.updateCardFromGroupOnUnmerge( | ||
boardId, | ||
cardGroupId, | ||
items[0], | ||
newComments, | ||
newVotes, | ||
true | ||
); | ||
|
||
if (!updateCard) throw Error(UPDATE_FAILED); | ||
} | ||
} | ||
|
||
//Creates new card for the card that was removed from the group | ||
private async createNewCard(cardItemToMove, unMergeCardDto) { | ||
const { boardId, columnId, position } = unMergeCardDto; | ||
|
||
const newCardItem = { ...cardItemToMove }; | ||
const cardId = newCardItem._id; | ||
delete newCardItem._id; | ||
|
||
const newCard = { | ||
_id: cardId, | ||
...cardItemToMove, | ||
comments: [], | ||
votes: [], | ||
items: [newCardItem] | ||
}; | ||
|
||
const pushResult = await this.cardRepository.pushCard( | ||
boardId, | ||
columnId, | ||
position, | ||
newCard, | ||
true | ||
); | ||
|
||
if (!pushResult) throw Error(CARD_NOT_INSERTED); | ||
|
||
return cardId; | ||
} | ||
} |
28 changes: 0 additions & 28 deletions
28
backend/src/modules/cards/applications/unmerge.card.application.ts
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.