import { TestingModule, Test } from '@nestjs/testing' import { datatype } from 'faker' import { factory } from '../../../../test/factories' import { MockedModule } from '../../../../test/types' import { PatientRepository, PlanRepository, SubscriptionRepository } from '../../../repositories' import { TeleconsultationsCreditService } from '../../../services' import { BusinessExceptionError } from '../../../errors' import { Patient, Plan, Subscription } from '../../../models' import { TeleconsultationsCredit } from './teleconsultations_credit' describe('TeleconsultationsCredit', () => { let subject: TeleconsultationsCredit let teleconsultationsCreditService: MockedModule let planRepository: MockedModule let subscriptionRepository: MockedModule let patientRepository: MockedModule beforeAll(async () => { teleconsultationsCreditService = { getCount: jest.fn(), getCompleteBalance: jest.fn(), createTransaction: jest.fn(), hasByDateAndReasons: jest.fn(), } planRepository = { findEnabledWithTeleconsultations: jest.fn(), } subscriptionRepository = { findByTeleconsultationRenew: jest.fn(), updateRenewsAt: jest.fn(), } patientRepository = { findCollaboratorsByCreationAnniversary: jest.fn(), } const app: TestingModule = await Test.createTestingModule({ controllers: [], providers: [ { provide: TeleconsultationsCreditService, useValue: teleconsultationsCreditService, }, { provide: SubscriptionRepository, useValue: subscriptionRepository, }, { provide: PlanRepository, useValue: planRepository, }, { provide: PatientRepository, useValue: patientRepository, }, TeleconsultationsCredit, ], }).compile() subject = app.get(TeleconsultationsCredit) }) afterEach(() => { jest.resetAllMocks() }) afterAll(() => { jest.restoreAllMocks() }) describe('#renewSubscriptionsTeleconsultationsCreditsByPlans', () => { const date = new Date() describe('when has plans with teleconsultations', () => { let plans: Array beforeAll(async () => { const planFeature = await factory.build('plan_feature', { feature: 'teleconsultation' }) plans = [await factory.build('plan', { interval: 'monthly', features: [planFeature] })] }) beforeEach(() => { planRepository.findEnabledWithTeleconsultations.mockResolvedValue(plans) }) describe('when a plan has subscriptions with renews on the day of the date', () => { let subscriptions: Array let balance: number let currentBalanceSingleBuysCount: number beforeAll(async () => { subscriptions = [await factory.build('subscription')] }) beforeEach(() => { subscriptionRepository.findByTeleconsultationRenew.mockResolvedValue(subscriptions) teleconsultationsCreditService.getCompleteBalance.mockResolvedValue({ balance, currentBalanceSingleBuysCount, }) jest.spyOn(console, 'error').mockImplementation(() => null) }) describe('when has negative balance', () => { beforeAll(() => { balance = -1 }) it('log an error', async () => { await subject.renewSubscriptionsTeleconsultationsCreditsByPlans(date) expect(console.error).toHaveBeenCalledWith( new BusinessExceptionError( `Teleconsultations credid balance can't be negative! Patient ${subscriptions[0].patientId}`, ), subscriptions[0], ) }) }) describe('when has a positive balance', () => { beforeAll(() => { balance = 2 currentBalanceSingleBuysCount = 1 }) it('removes credits that are expirable', async () => { await subject.renewSubscriptionsTeleconsultationsCreditsByPlans(date) expect(teleconsultationsCreditService.createTransaction).toHaveBeenCalledWith( expect.objectContaining({ value: currentBalanceSingleBuysCount - balance, reason: 'end_of_period_expiration', }), ) }) it('adds the plan credits', async () => { await subject.renewSubscriptionsTeleconsultationsCreditsByPlans(date) expect(teleconsultationsCreditService.createTransaction).toHaveBeenCalledWith( expect.objectContaining({ value: plans[0].getFeature('teleconsultation').quantity, reason: 'period_renewal', }), ) }) }) describe('when subscription is cancelled', () => { beforeAll(async () => { subscriptions = [await factory.build('subscription', { cancellationAt: new Date() })] }) afterAll(async () => { subscriptions = [await factory.build('subscription')] }) it('doesn`t adds the plan credits', async () => { await subject.renewSubscriptionsTeleconsultationsCreditsByPlans(date) expect(teleconsultationsCreditService.createTransaction).not.toHaveBeenCalledWith( expect.objectContaining({ reason: 'period_renewal', }), ) }) }) }) }) }) describe('#renewCollaboratorsTeleconsultationsCredits', () => { const criationAnniversaryDate = new Date() describe('when there are not collaborators', () => { beforeEach(() => { patientRepository.findCollaboratorsByCreationAnniversary.mockResolvedValue([]) }) it('does not add any teleconsultations credit', async () => { await subject.renewCollaboratorsTeleconsultationsCredits(criationAnniversaryDate) expect(teleconsultationsCreditService.createTransaction).not.toBeCalled() }) }) describe('when there are collaborators', () => { let collaborators: Patient[] let balance: number let currentBalanceSingleBuysCount: number beforeEach(async () => { collaborators = await factory.buildMany('patient:collaborator', datatype.number({ min: 1, max: 10 })) patientRepository.findCollaboratorsByCreationAnniversary.mockResolvedValue(collaborators) teleconsultationsCreditService.getCompleteBalance.mockResolvedValue({ balance, currentBalanceSingleBuysCount, }) jest.spyOn(console, 'error').mockImplementation(() => null) }) afterEach(() => { teleconsultationsCreditService.createTransaction.mockReset() }) describe('when has negative balance', () => { beforeAll(() => { balance = -1 }) it('log an error', async () => { await subject.renewCollaboratorsTeleconsultationsCredits(criationAnniversaryDate) expect(console.error).toHaveBeenCalledTimes(collaborators.length) }) }) describe('when has a positive balance', () => { beforeAll(() => { balance = 3 }) it('adds the plan credits', async () => { await subject.renewCollaboratorsTeleconsultationsCredits(criationAnniversaryDate) expect(teleconsultationsCreditService.createTransaction).toHaveBeenCalledWith( expect.objectContaining({ reason: 'period_renewal' }), ) }) describe('when has experiable credits', () => { beforeAll(() => { balance = 2 currentBalanceSingleBuysCount = 1 }) it('removes credits that are expirable', async () => { await subject.renewCollaboratorsTeleconsultationsCredits(criationAnniversaryDate) expect(teleconsultationsCreditService.createTransaction).toHaveBeenCalledWith( expect.objectContaining({ value: currentBalanceSingleBuysCount - balance, reason: 'end_of_period_expiration', }), ) }) }) }) }) }) })