import { TestingModule, Test } from '@nestjs/testing' import { date, datatype } from 'faker' import MockDate from 'mockdate' import { factory } from '../../../../../test/factories' import { MockedModule } from '../../../../../test/types' import { PlanRepository } from '../../../../repositories' import { GoogleInAppSubscription, GoogleInAppSubscriptionService, SubscriptionService } from '../../../../services' import { CancellationsReasons, Patient, PaymentProviders, Plan, Subscription } from './../../../../models' import { GoogleCheckSubscription } from './google.check-subscription' describe('GoogleCheckSubscription', () => { let subject: GoogleCheckSubscription let subscriptionService: MockedModule let planRepository: MockedModule let googleInAppSubscription: MockedModule let now: Date beforeAll(async () => { subscriptionService = { cancelSubscription: jest.fn(), uncancel: jest.fn(), getToken: jest.fn(), updateExpiresDate: jest.fn(), } planRepository = { findByAddonCode: jest.fn(), get: jest.fn(), } googleInAppSubscription = { get: jest.fn(), } const app: TestingModule = await Test.createTestingModule({ controllers: [], providers: [ { provide: SubscriptionService, useValue: subscriptionService, }, { provide: PlanRepository, useValue: planRepository, }, { provide: GoogleInAppSubscriptionService, useValue: googleInAppSubscription, }, GoogleCheckSubscription, ], }).compile() subject = app.get(GoogleCheckSubscription) now = new Date() globalThis.secret = { app_package_ids: { default: { apple: datatype.uuid(), google: datatype.uuid(), }, }, } as any MockDate.set(now) }) afterEach(() => { jest.resetAllMocks() }) afterAll(() => { jest.restoreAllMocks() }) describe('#check()', () => { let patient: Patient let plan: Plan let googleInfo: GoogleInAppSubscription beforeAll(async () => { googleInfo = await factory.build('google_in_app_subscription') plan = await factory.build('plan', { payment_providers: [ await factory.build('plan_payment_provider', { provider: PaymentProviders.GOOGLE, }), ], }) }) beforeEach(() => { googleInAppSubscription.get.mockResolvedValue(googleInfo) planRepository.get.mockResolvedValue(plan) }) describe('when patient does not have subscription', () => { beforeAll(async () => { patient = await factory.build('patient') }) it('returns false', async () => { await expect(subject.check(patient)).resolves.toBeFalsy() }) }) describe('when patient is not subscriber', () => { beforeEach(async () => { const subscription = await factory.build('subscription:google') patient = await factory.build('patient', { subscription }) patient.subscription.cancellationAt = new Date() patient.subscription.planId = datatype.uuid() }) it('does not cancel subscription', async () => { await subject.check(patient) expect(subscriptionService.cancelSubscription).not.toBeCalled() }) describe('when has google subscription', () => { describe('when is valid subscription', () => { beforeEach(() => { subscriptionService.uncancel.mockImplementation((subscription: Subscription) => { subscription.uncancel() }) }) it('uncancel subscription', async () => { await subject.check(patient) expect(subscriptionService.uncancel).toBeCalledWith(patient.subscription) }) it('returns true', async () => { await expect(subject.check(patient)).resolves.toBeTruthy() }) }) describe('when invalid subscription', () => { beforeAll(() => { googleInfo.expiryTimeMillis = date .recent() .getTime() .toString() }) it('does not uncancel subscription', async () => { await subject.check(patient) expect(subscriptionService.uncancel).not.toBeCalled() }) it('returns false', async () => { await expect(subject.check(patient)).resolves.toBeFalsy() }) }) }) describe('when does not have google subscription', () => { beforeAll(() => { googleInAppSubscription.get.mockResolvedValue(null) }) it('does not uncancel subscription', async () => { await subject.check(patient) expect(subscriptionService.uncancel).not.toBeCalled() }) it('returns false', async () => { await expect(subject.check(patient)).resolves.toBeFalsy() }) }) }) describe('when patient is subscriber', () => { beforeEach(async () => { const subscription = await factory.build('subscription:google') patient = await factory.build('patient', { subscription }) patient.subscription.planId = datatype.uuid() }) it('does not uncancel subscription', async () => { await subject.check(patient) expect(subscriptionService.uncancel).not.toBeCalled() }) describe('when has google subscription', () => { describe('when subscription does not have expiresAt data', () => { beforeAll(() => { patient.subscription.expiresAt = undefined }) it('saves expiresAt', async () => { await subject.check(patient) expect(subscriptionService.updateExpiresDate).toBeCalledWith( patient.subscription, new Date(+googleInfo.expiryTimeMillis), ) }) }) describe('when is valid subscription', () => { beforeAll(() => { googleInfo.expiryTimeMillis = date .future() .getTime() .toString() }) it('does not cancel subscription', async () => { await subject.check(patient) expect(subscriptionService.cancelSubscription).not.toBeCalled() }) it('returns true', async () => { await expect(subject.check(patient)).resolves.toBeTruthy() }) }) describe('when invalid subscription', () => { beforeAll(() => { googleInfo.expiryTimeMillis = date .recent() .getTime() .toString() }) beforeEach(() => { subscriptionService.cancelSubscription.mockImplementation(({ patient }: { patient: Patient }) => { patient.subscription.cancellationRequestAt = new Date() patient.subscription.cancellationAt = new Date() }) }) it('cancels subscription', async () => { await subject.check(patient) expect(subscriptionService.cancelSubscription).toBeCalledWith({ patient, cancellationReason: { id: CancellationsReasons.BY_PROVIDER }, cancellationBy: { id: patient.id }, expiresAt: new Date(+googleInfo.expiryTimeMillis), }) }) it('returns false', async () => { await expect(subject.check(patient)).resolves.toBeFalsy() }) }) }) describe('when does not have google subscription', () => { beforeEach(() => { googleInAppSubscription.get.mockResolvedValue(null) subscriptionService.cancelSubscription.mockImplementation(({ patient }: { patient: Patient }) => { patient.subscription.cancellationRequestAt = new Date() patient.subscription.cancellationAt = new Date() }) }) it('cancels subscription', async () => { await subject.check(patient) expect(subscriptionService.cancelSubscription).toBeCalledWith({ patient, cancellationReason: { id: CancellationsReasons.BY_PROVIDER }, cancellationBy: { id: patient.id }, expiresAt: patient.subscription.expiresAt, }) }) it('returns false', async () => { await expect(subject.check(patient)).resolves.toBeFalsy() }) }) }) }) })