import { TestingModule, Test } from '@nestjs/testing' import { datatype } from 'faker' import { BusinessExceptionError } from '../../errors' import { Credit, CreditPending, CreditReason, Patient, User } from '../../models' import { CreditRepository, CreditPendingRepository } from '../../repositories' import { factory } from '../../../test/factories' import { MockedModule } from '../../../test/types' import { CreditService } from './credit.service' describe('CreditService', () => { let subject: CreditService> let creditRepository: MockedModule> let creditPendingRepository: MockedModule beforeAll(async () => { creditRepository = { getCount: jest.fn(), getSimpleBalance: jest.fn(), getComplexBalance: jest.fn(), create: jest.fn(), findPatientCreditsByDateAndReasons: jest.fn(), } creditPendingRepository = { create: jest.fn(), save: jest.fn(), } const app: TestingModule = await Test.createTestingModule({ controllers: [], providers: [ { provide: CreditRepository, useValue: creditRepository, }, { provide: CreditPendingRepository, useValue: creditPendingRepository, }, { provide: CreditService, useFactory: (creditRepository: CreditRepository, creditPendingRepository: CreditPendingRepository) => new CreditService(Credit, creditRepository, creditPendingRepository), inject: [CreditRepository, CreditPendingRepository], }, ], }).compile() subject = app.get>>(CreditService) }) afterEach(() => { jest.resetAllMocks() }) afterAll(() => { jest.restoreAllMocks() }) describe('#getBalance', () => { const patientId = datatype.uuid() it('gets from repository', async () => { await subject.getBalance(patientId) expect(creditRepository.getSimpleBalance).toHaveBeenCalledWith(patientId) }) }) describe('#getCompleteBalance', () => { const patientId = datatype.uuid() let nodes: Array<{ reason: CreditReason | string; value: number }> let balance: number beforeEach(() => { creditRepository.getComplexBalance.mockResolvedValue({ balance, nodes }) }) describe('when balance is zero', () => { beforeAll(() => { balance = 0 }) describe('when has single buys but isn`t in current balance', () => { beforeAll(() => { nodes = [ { reason: 'single_buy', value: 1 }, { reason: 'appointment_use', value: -1 }, { reason: 'period_renewal', value: 1 }, { reason: 'end_of_period_expiration', value: -1 }, ].reverse() }) it('retuns the current balance single buys count', async () => { expect(await subject.getCompleteBalance(patientId)).toEqual( expect.objectContaining({ currentBalanceSingleBuysCount: 0 }), ) }) }) }) describe('when balance is positive', () => { beforeAll(() => { balance = 2 }) describe('when has single buys', () => { beforeAll(() => { nodes = [ { reason: 'single_buy', value: 1 }, { reason: 'appointment_use', value: -1 }, { reason: 'period_renewal', value: 1 }, { reason: 'end_of_period_expiration', value: -1 }, { reason: 'period_renewal', value: 1 }, { reason: 'single_buy', value: 1 }, ].reverse() }) it('retuns the balance and single buys count', async () => { expect(await subject.getCompleteBalance(patientId)).toEqual({ balance: 2, currentBalanceSingleBuysCount: 1 }) }) }) }) }) describe('#getCount', () => { const patientId = datatype.uuid() it('gets from repository', async () => { await subject.getCount(patientId) expect(creditRepository.getCount).toHaveBeenCalledWith(patientId) }) }) describe('#createTransaction', () => { const patientId = datatype.uuid() const id = datatype.uuid() const value = 1 const campaignValue = datatype.uuid() let reason: CreditReason let patient: Patient let user: User beforeEach(() => { creditRepository.create.mockResolvedValue(id) }) describe('when has an author', () => { beforeAll(() => { reason = 'other' }) it('creates and return the credit transaction with system author', async () => { expect( await subject.createTransaction({ patientId, reason, value, author: { type: 'system' }, campaignValue, }), ).toEqual({ idTransaction: id, status: 'created', }) }) }) describe('when is a user created transaction', () => { beforeAll(async () => { reason = 'other' user = await factory.build('user') }) it('creates and return the credit transaction with system author', async () => { expect( await subject.createTransaction({ patientId, reason, value, author: user, campaignValue, }), ).toEqual({ idTransaction: id, status: 'created', }) }) }) describe('when is a system transaction', () => { beforeAll(async () => { reason = 'other' patient = await factory.build('patient') }) it('creates and return the credit transaction with system author', async () => { expect( await subject.createTransaction({ patientId, reason, value, author: patient, campaignValue, }), ).toEqual({ idTransaction: id, status: 'created', }) }) }) describe('when is not given the patient id', () => { describe('when is not given an identifier data', () => { it('throws an error', async () => { await expect( subject.createTransaction({ reason, value, author: patient, campaignValue, }), ).rejects.toThrowError(BusinessExceptionError) }) }) describe('when is given an identifier data', () => { it('creates a pending credit with author id', async () => { await subject.createTransaction({ reason, value, author: patient, identifier: { documentNumber: datatype.uuid() }, campaignValue, }) expect(creditPendingRepository.create).toBeCalledWith(expect.objectContaining({ created_by_id: patient.id })) }) }) }) describe('when has not an author', () => { describe('when is not given the patient id', () => { describe('when is given an identifier data', () => { let externalId: string beforeAll(() => { externalId = datatype.uuid() }) it('creates a pending credit with identifier external id', async () => { await subject.createTransaction({ reason, value, author: { type: 'system' }, identifier: { documentNumber: datatype.uuid(), externalId }, campaignValue, }) expect(creditPendingRepository.create).toBeCalledWith( expect.objectContaining({ created_by_id: externalId }), ) }) }) }) }) }) describe('#hasByDateAndReasons', () => { describe('when found patient credits', () => { beforeEach(() => { creditRepository.findPatientCreditsByDateAndReasons.mockResolvedValue( factory.buildMany('bioimpedance_credit', datatype.number({ min: 1, max: 10 })), ) }) it('returns true', async () => { expect(await subject.hasByDateAndReasons(datatype.uuid(), new Date(), ['single_buy'])).toEqual(true) }) }) describe('when not found patient credits', () => { beforeEach(() => { creditRepository.findPatientCreditsByDateAndReasons.mockResolvedValue([]) }) it('returns true', async () => { expect(await subject.hasByDateAndReasons(datatype.uuid(), new Date(), ['single_buy'])).toEqual(false) }) }) }) describe('#freePendingCredit', () => { let creditPending: CreditPending let patientId: string let creditId: string beforeAll(() => { patientId = datatype.uuid() creditId = datatype.uuid() }) beforeEach(async () => { creditPending = await factory.build('credit_pending') creditRepository.create.mockResolvedValue(creditId) }) it('creates a credit', async () => { await subject.freePendingCredit(creditPending, patientId) expect(creditRepository.create).toBeCalled() }) it('saves credit pending with new credit id', async () => { await subject.freePendingCredit(creditPending, patientId) expect(creditPendingRepository.save).toBeCalledWith(expect.objectContaining({ credit_id: creditId }), [ 'credit_id', ]) }) }) })