import { Test, TestingModule } from '@nestjs/testing' import { date } from 'faker' import { factory } from '../../../../test/factories' import { MockedModule } from '../../../../test/types' import { CancellationsReasons, Patient, Subscription } from '../../../models' import { NotFoundError } from '../../../errors' import { SubscriptionRepository } from '../../../repositories' import { SubscriptionService, GoogleInAppSubscriptionService, GoogleInAppSubscription } from '../../../services' import { GoogleInApp } from './google-in-app' import { GoogleInAppStatus, GoogleInAppNotification } from './types' describe('GoogleInApp', () => { let subject: GoogleInApp let subscriptionRepository: MockedModule let subscriptionService: MockedModule let googleInAppSubscriptionService: MockedModule beforeAll(async () => { subscriptionRepository = { getByTokenAndPaymentProvider: jest.fn(), } subscriptionService = { cancelSubscription: jest.fn(), } googleInAppSubscriptionService = { get: jest.fn(), } const app: TestingModule = await Test.createTestingModule({ providers: [ { provide: SubscriptionRepository, useValue: subscriptionRepository, }, { provide: SubscriptionService, useValue: subscriptionService, }, { provide: GoogleInAppSubscriptionService, useValue: googleInAppSubscriptionService, }, GoogleInApp, ], }).compile() subject = app.get(GoogleInApp) }) afterEach(() => { jest.clearAllMocks() }) afterAll(() => { jest.restoreAllMocks() }) describe('#handle()', () => { let subscription: Subscription let data: GoogleInAppNotification describe('when hasn`t a subscription with given token', () => { beforeAll(async () => { data = await factory.build('google_in_app_notification') subscriptionRepository.getByTokenAndPaymentProvider.mockResolvedValue(null) }) it('throws an error', async () => { await expect(subject.handle(data)).rejects.toThrowError(NotFoundError) }) }) describe('when has a subscription with given token', () => { let patient: Patient let googleSubscription: GoogleInAppSubscription beforeEach(async () => { subscription = await factory.build('subscription') subscriptionRepository.getByTokenAndPaymentProvider.mockResolvedValue(subscription) googleInAppSubscriptionService.get.mockResolvedValue(googleSubscription) }) describe.each` type | status ${'3'} | ${GoogleInAppStatus[3]} ${'12'} | ${GoogleInAppStatus[12]} ${'13'} | ${GoogleInAppStatus[13]} `('and notification is "$status"', ({ type }) => { beforeAll(async () => { data = await factory.build('google_in_app_notification') data.subscriptionNotification.notificationType = type }) beforeEach(() => { patient = new Patient({ id: subscription.patientId, subscription }) subscription.cancellationRequestAt = new Date() }) describe('and Google doesn`t returns the subscription data', () => { beforeAll(() => { googleSubscription = null }) it('throws an error', async () => { await expect(subject.handle(data)).rejects.toThrowError(NotFoundError) }) }) describe('and Google returns the subscription data', () => { beforeAll(async () => { googleSubscription = await factory.build('google_in_app_subscription') }) describe('and subscription hasn`t the expiration date', () => { beforeEach(() => { subscription.expiresAt = null }) it('adds the expiresAt property', async () => { await subject.handle(data) expect(subscriptionService.cancelSubscription).toHaveBeenCalledWith({ patient: new Patient({ id: subscription.patientId, subscription: new Subscription({ ...subscription, cancellationAt: type === '12' || type === '13' ? new Date(+googleSubscription.expiryTimeMillis) : undefined, expiresAt: new Date(+googleSubscription.expiryTimeMillis), cancellationRequestAt: new Date(+googleSubscription.userCancellationTimeMillis), }), }), cancellationReason: { id: CancellationsReasons.IN_APP_CANCELLED, }, cancellationBy: { id: subscription.patientId }, expiresAt: subscription.expiresAt, }) }) }) describe('and subscription already has the expiration date', () => { beforeEach(() => { subscription.expiresAt = date.future() }) it(`cancels the subscription`, async () => { await subject.handle(data) expect(subscriptionService.cancelSubscription).toHaveBeenCalledWith({ patient, cancellationReason: { id: CancellationsReasons.IN_APP_CANCELLED, }, cancellationBy: { id: patient.id }, expiresAt: subscription.expiresAt, }) }) }) }) if (type === GoogleInAppStatus[13]) { describe('and subscription is not in cancellation', () => { beforeEach(() => { subscription.cancellationRequestAt = null }) it('does not cancel the subscription', async () => { await subject.handle(data) expect(subscriptionService.cancelSubscription).not.toBeCalled() }) }) } }) }) }) })