import { TestingModule, Test } from '@nestjs/testing' import { addDays, subDays } from 'date-fns' import { datatype } from 'faker' import { cloneDeep } from 'lodash' import { factory } from '../../../../../test/factories' import { Addon, Person, SmartFitPlan } from '../../../../services/smartfit' import { MockedModule } from '../../../../../test/types' import { PatientRepository, SubscriptionRepository, PlanRepository } from '../../../../repositories' import { COLLABORATOR_TELECONSULTATIONS_CREDIT, SubscriptionService, TeleconsultationsCreditService, } from '../../../../services' import { CancellationsReasons, CommonOtherReasons, Patient, PaymentProviders, Plan } from './../../../../models' import { SmartfitCheckSubscription } from './smartfit.check-subscription' describe('SmartfitCheckSubscription', () => { let subject: SmartfitCheckSubscription let patientRepository: MockedModule let subscriptionService: MockedModule let subscriptionRepository: MockedModule let planRepository: MockedModule let teleconsultationsCreditService: MockedModule beforeAll(async () => { patientRepository = { save: jest.fn(), } subscriptionService = { getPerson: jest.fn(), getActiveAddon: jest.fn(), addSubscription: jest.fn(), cancelSubscription: jest.fn(), } subscriptionRepository = { updateSubscribedAt: jest.fn(), cancel: jest.fn(), get: jest.fn(), } planRepository = { findByAddonId: jest.fn(), getWithFeatures: jest.fn(), } teleconsultationsCreditService = { createTransaction: jest.fn(), } const app: TestingModule = await Test.createTestingModule({ controllers: [], providers: [ { provide: PatientRepository, useValue: patientRepository, }, { provide: SubscriptionRepository, useValue: subscriptionRepository, }, { provide: SubscriptionService, useValue: subscriptionService, }, { provide: PlanRepository, useValue: planRepository, }, { provide: TeleconsultationsCreditService, useValue: teleconsultationsCreditService, }, SmartfitCheckSubscription, ], }).compile() subject = app.get(SmartfitCheckSubscription) }) afterEach(() => { jest.resetAllMocks() }) afterAll(() => { jest.restoreAllMocks() }) describe('#check()', () => { let patient: Patient let addon: Addon let result: boolean let person: Person let plan: Plan let planByAddonId: Plan beforeAll(async () => { plan = await factory.build('plan:smartfit') patient = await factory.build('patient', { collaborator: false }) person = await factory.build('smartfit:person', { plan: SmartFitPlan.BLACK }) }) beforeEach(async () => { subscriptionService.getActiveAddon.mockResolvedValue(addon) planRepository.getWithFeatures.mockResolvedValue(plan) planRepository.findByAddonId.mockResolvedValue(planByAddonId) subscriptionRepository.get.mockResolvedValue(cloneDeep(patient.subscription)) subscriptionService.getPerson.mockResolvedValue(person) result = undefined try { result = await subject.check(cloneDeep(patient)) } catch (err) {} }) describe('when person not exists', () => { beforeEach(() => { subscriptionService.getPerson.mockResolvedValue(null) }) it('returns false', () => { expect(result).toEqual(false) }) }) describe('when person exists', () => { describe(`when patient is a person but isn't flagged as it`, () => { beforeAll(() => { patient.is_smartfit = undefined }) afterAll(() => { patient.is_smartfit = true }) it('save the patient with the new data', async () => { expect(patientRepository.save).toBeCalledWith( { id: patient.id, smartfit_token: person.authToken, is_smartfit: !!person.authToken, smartfit_unit_acronym: person.locationAcronym, }, ['smartfit_token', 'is_smartfit', 'smartfit_unit_acronym'], ) }) }) describe('when patient is not a collaborator', () => { it("do not update patient's collaborator flag", () => { expect(patientRepository.save).not.toBeCalled() }) describe('when patient is a student', () => { describe('when has addons', () => { beforeAll(async () => { addon = await factory.build('addon') person = await factory.build('smartfit:person') }) describe('when patient is subscriber', () => { beforeAll(async () => { patient = await factory.build('patient:subscriber', { collaborator: false }) plan = planByAddonId = await factory.build('plan:smartfit') planByAddonId.payment_providers[0].smartfit_id = addon.addonId }) describe('when subscription doesn`t has subscribedAt property', () => { describe('when addon has createdAt property', () => { beforeAll(() => { patient.subscription.subscribedAt = undefined }) it('update subscription with subscribedAt as addon creation date', () => { expect(subscriptionRepository.updateSubscribedAt).toBeCalledWith({ ...patient.subscription, subscribedAt: addon.createdAt, }) }) describe('when addonId is not same that the subscription plan smartfit id', () => { beforeAll(() => { plan.payment_providers[0].smartfit_id = datatype.uuid() patient.subscription.subscribedAt = undefined }) afterAll(() => { plan.payment_providers[0].smartfit_id = addon.addonId }) it('update subscription with subscribedAt as subscription creation date', () => { expect(subscriptionRepository.updateSubscribedAt).toBeCalledWith({ ...patient.subscription, subscribedAt: patient.subscription.createdAt, }) }) }) }) describe('when addon hasn`t createdAt property', () => { beforeAll(() => { patient.subscription.subscribedAt = undefined addon.createdAt = null }) it('update subscription with subscribedAt as subscription creation date', () => { expect(subscriptionRepository.updateSubscribedAt).toBeCalledWith({ ...patient.subscription, subscribedAt: patient.subscription.createdAt, }) }) }) }) describe('when subscription has subscribedAt property', () => { beforeAll(() => { patient.subscription.subscribedAt = new Date() }) it('do not update the subscription', () => { expect(subscriptionRepository.updateSubscribedAt).not.toBeCalled() }) }) describe('when subscription is in_cancellation', () => { beforeAll(() => { patient.subscription.cancellationRequestAt = new Date() }) describe('when is expired', () => { beforeAll(() => { patient.subscription.expiresAt = subDays(new Date(), 1) }) afterAll(() => { patient.subscription.cancellationAt = undefined }) it('cancelledAt attribute is updated', () => { expect(subscriptionRepository.cancel).toBeCalledWith( expect.objectContaining({ cancellationAt: expect.any(Date) }), expect.objectContaining({ id: patient.subscription.cancellationReasonId }), patient.subscription.cancellationById, ) }) it('try create new subscription', () => { expect(subscriptionService.addSubscription).toBeCalled() }) it('returns true', () => { expect(result).toBeTruthy() }) }) describe('when is not expired', () => { beforeAll(() => { patient.subscription.expiresAt = addDays(new Date(), 1) }) it('cancelledAt attribute is not updated', () => { expect(subscriptionRepository.cancel).not.toBeCalled() }) it('not try create new subscription', () => { expect(subscriptionService.addSubscription).not.toBeCalled() }) it('returns true', () => { expect(result).toBeTruthy() }) }) describe('when subscription dont have expiration date', () => { beforeAll(() => { patient.subscription.expiresAt = undefined }) afterAll(() => { patient.subscription.expiresAt = addDays(new Date(), 1) }) it('cancelledAt attribute is not updated', () => { expect(subscriptionRepository.cancel).not.toBeCalled() }) it('not try create new subscription', () => { expect(subscriptionService.addSubscription).not.toBeCalled() }) it('returns true', () => { expect(result).toBeTruthy() }) }) }) describe('when subscription has same addonId', () => { it('does not cancel the subscription', () => { expect(subscriptionRepository.cancel).not.toBeCalled() }) it('returns true', () => { expect(result).toBeTruthy() }) }) describe('when subscription has different addonId', () => { beforeAll(async () => { addon = await factory.build('addon:active') plan = await factory.build('plan:smartfit') planByAddonId = await factory.build('plan:smartfit') planByAddonId.payment_providers[0].smartfit_id = addon.addonId }) it('does cancel the subscription', () => { expect(subscriptionService.cancelSubscription).toBeCalled() }) it('does add the subscription with new addonId', () => { expect(subscriptionService.addSubscription).toBeCalledWith({ patient: { ...patient, subscription: { ...patient.subscription, expiresAt: expect.any(Date) } }, plan: expect.objectContaining({ payment_providers: expect.arrayContaining([ expect.objectContaining({ smartfit_id: addon.addonId }), ]), }), paymentProvider: { provider: PaymentProviders.SMARTFIT }, options: { subscribedAt: addon.createdAt }, data: {}, }) }) it('returns true', () => { expect(result).toBeTruthy() }) }) }) }) describe('when does not have addons', () => { beforeAll(async () => { addon = undefined person = await factory.build('smartfit:person') }) describe('when patient is not subscriber', () => { beforeAll(async () => { patient = await factory.build('patient') }) it('do not cancel current subscription', () => { expect(subscriptionRepository.cancel).not.toBeCalled() }) it('returns false', () => { expect(result).toBeFalsy() }) }) describe('when patient is subscriber', () => { beforeAll(async () => { addon = undefined patient = await factory.build('patient:subscriber') }) it('cancel current subscription', () => { expect(subscriptionRepository.cancel).toBeCalledWith( { ...patient.subscription, expiresAt: expect.any(Date), cancellationRequestAt: expect.any(Date), cancellationAt: expect.any(Date), }, { id: CancellationsReasons.BY_PROVIDER }, patient.id, ) }) it('returns false', () => { expect(result).toBeFalsy() }) }) }) }) }) }) describe('when patient is a collaborator', () => { beforeAll(async () => { person = await factory.build('smartfit:person', { plan: SmartFitPlan.VIP_COLLABORATOR }) }) describe('and already is flagged as collaborator', () => { beforeAll(async () => { patient = await factory.build('patient:collaborator') }) it('does nothing', () => { expect(patientRepository.save).not.toBeCalled() expect(subscriptionService.cancelSubscription).not.toBeCalled() expect(teleconsultationsCreditService.createTransaction).not.toBeCalled() }) }) describe(`and already isn't flagged as collaborator`, () => { beforeAll(async () => { patient = await factory.build('patient') }) describe('when patient is subscriber', () => { beforeAll(async () => { patient = await factory.build('patient:subscriber') }) it('cancel current subscription', () => { expect(subscriptionService.cancelSubscription).toBeCalledWith({ patient: { ...patient, collaborator: true }, cancellationReason: { id: CancellationsReasons.OTHER, otherReason: CommonOtherReasons.BECAME_COLLABORATOR, }, cancellationBy: { id: patient.id }, }) }) }) it("update patient's collaborator flag", () => { expect(patientRepository.save).toBeCalledWith( expect.objectContaining({ collaborator: true }), expect.arrayContaining(['collaborator']), ) }) it('adds teleconsultation credit as collaborator to patient', () => { expect(teleconsultationsCreditService.createTransaction).toBeCalledWith({ patientId: patient.id, value: COLLABORATOR_TELECONSULTATIONS_CREDIT, reason: 'collaborator_signup', author: expect.objectContaining({ id: patient.id }), }) }) it('returns true', () => { expect(result).toEqual(true) }) }) }) }) })