diff --git a/apps/api/src/campaign/campaign.service.ts b/apps/api/src/campaign/campaign.service.ts index a1290b346..526c0e3f3 100644 --- a/apps/api/src/campaign/campaign.service.ts +++ b/apps/api/src/campaign/campaign.service.ts @@ -41,7 +41,6 @@ import { NotificationService, donationNotificationSelect, } from '../sockets/notifications/notification.service' -import { DonationMetadata } from '../donations/dontation-metadata.interface' import { Expense } from '@prisma/client' import { SendGridParams } from '../notifications/providers/notifications.sendgrid.types' import * as NotificationData from '../notifications/notification-data.json' diff --git a/apps/api/src/withdrawal/withdrawal.controller.spec.ts b/apps/api/src/withdrawal/withdrawal.controller.spec.ts index c16bd6731..fc9297bf3 100644 --- a/apps/api/src/withdrawal/withdrawal.controller.spec.ts +++ b/apps/api/src/withdrawal/withdrawal.controller.spec.ts @@ -1,6 +1,6 @@ -import { NotFoundException, ForbiddenException } from '@nestjs/common' +import { NotFoundException, BadRequestException } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' -import { WithdrawStatus, Currency } from '@prisma/client' +import { WithdrawStatus, Currency, Withdrawal } from '@prisma/client' import { mockReset } from 'jest-mock-extended' import { MockPrismaService, prismaMock } from '../prisma/prisma-client.mock' import { CreateWithdrawalDto } from './dto/create-withdrawal.dto' @@ -9,7 +9,7 @@ import { WithdrawalController } from './withdrawal.controller' import { WithdrawalService } from './withdrawal.service' import { MarketingNotificationsModule } from '../notifications/notifications.module' -const mockData = [ +const mockData: Withdrawal[] = [ { id: '00000000-0000-0000-0000-000000000001', status: WithdrawStatus.initial, @@ -362,9 +362,98 @@ describe('WithdrawalController', () => { }) describe('removeData', () => { - it('should not remove withdrawals', async () => { + it("should decrement vault's blockedAmount on initial withdrawn deletion", async () => { const withdrawal = mockData[0] - await expect(controller.remove(withdrawal.id)).rejects.toThrow(new ForbiddenException()) + prismaMock.withdrawal.delete.mockResolvedValue(withdrawal) + await expect(controller.remove(withdrawal.id)).toResolve() + expect(prismaMock.withdrawal.delete).toHaveBeenCalledWith({ + where: { id: withdrawal.id }, + }) + expect(prismaMock.vault.update).toHaveBeenCalledWith({ + where: { id: withdrawal.sourceVaultId }, + data: { + amount: { + increment: 0, + }, + blockedAmount: { + decrement: withdrawal.amount, + }, + }, + }) + }) + + it("should increment vault's amount on succeeded withdrawn deletion ", async () => { + const withdrawal = { ...mockData[0], status: WithdrawStatus.succeeded } + prismaMock.withdrawal.delete.mockResolvedValue(withdrawal) + await expect(controller.remove(withdrawal.id)).toResolve() + expect(prismaMock.withdrawal.delete).toHaveBeenCalledWith({ + where: { id: withdrawal.id }, + }) + expect(prismaMock.vault.update).toHaveBeenCalledWith({ + where: { id: withdrawal.sourceVaultId }, + data: { + amount: { + increment: withdrawal.amount, + }, + blockedAmount: { + decrement: 0, + }, + }, + }) + }) + + it("shouldn't update vault's amount and blockedAmount on cancelled withdrawn deletion", async () => { + const withdrawal = { ...mockData[0], status: WithdrawStatus.cancelled } + prismaMock.withdrawal.delete.mockResolvedValue(withdrawal) + await expect(controller.remove(withdrawal.id)).toResolve() + expect(prismaMock.withdrawal.delete).toHaveBeenCalledWith({ + where: { id: withdrawal.id }, + }) + expect(prismaMock.vault.update).toHaveBeenCalledWith({ + where: { id: withdrawal.sourceVaultId }, + data: { + amount: { + increment: 0, + }, + blockedAmount: { + decrement: 0, + }, + }, + }) + }) + + it("shouldn't update vault's amount and blockedAmount on declined withdrawn deletion", async () => { + const withdrawal = { ...mockData[0], status: WithdrawStatus.declined } + prismaMock.withdrawal.delete.mockResolvedValue(withdrawal) + await expect(controller.remove(withdrawal.id)).toResolve() + expect(prismaMock.withdrawal.delete).toHaveBeenCalledWith({ + where: { id: withdrawal.id }, + }) + expect(prismaMock.vault.update).toHaveBeenCalledWith({ + where: { id: withdrawal.sourceVaultId }, + data: { + amount: { + increment: 0, + }, + blockedAmount: { + decrement: 0, + }, + }, + }) + }) + + it('Should throw an error if delete query rejects', async () => { + const withdrawal = mockData[1] + prismaMock.withdrawal.delete.mockRejectedValue(withdrawal) + await expect(controller.remove(withdrawal.id)).rejects.toThrow( + new BadRequestException("Withdrawal record couldn't be deleted"), + ) + + expect(prismaMock.withdrawal.delete).toHaveBeenCalledWith({ + where: { id: withdrawal.id }, + }) + + expect(prismaMock.vault.update).not.toHaveBeenCalled() }) }) }) diff --git a/apps/api/src/withdrawal/withdrawal.service.ts b/apps/api/src/withdrawal/withdrawal.service.ts index 05c579953..4dcfb15fa 100644 --- a/apps/api/src/withdrawal/withdrawal.service.ts +++ b/apps/api/src/withdrawal/withdrawal.service.ts @@ -1,10 +1,4 @@ -import { - Injectable, - Logger, - NotFoundException, - BadRequestException, - ForbiddenException, -} from '@nestjs/common' +import { Injectable, Logger, NotFoundException, BadRequestException } from '@nestjs/common' import { Withdrawal, WithdrawStatus } from '@prisma/client' import { PrismaService } from '../prisma/prisma.service' import { CreateWithdrawalDto } from './dto/create-withdrawal.dto' @@ -124,11 +118,29 @@ export class WithdrawalService { return result } - // Functionality will be reworked soon async remove(id: string): Promise { - throw new ForbiddenException() - const result = await this.prisma.withdrawal.delete({ where: { id: id } }) - if (!result) throw new NotFoundException('Not found') + const result = await this.prisma.withdrawal + .delete({ + where: { id: id }, + }) + .catch(() => { + throw new BadRequestException("Withdrawal record couldn't be deleted") + }) + + const isSucceeded = result.status === WithdrawStatus.succeeded + const isCancelled = + result.status === WithdrawStatus.cancelled || result.status === WithdrawStatus.declined + + await this.prisma.vault.update({ + where: { id: result.sourceVaultId }, + data: { + amount: { increment: isSucceeded ? result.amount : 0 }, + blockedAmount: { + decrement: isCancelled || isSucceeded ? 0 : result.amount, + }, + }, + }) + return result } }