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 } from '../../../services' import { AppleInApp } from './apple-in-app' import { AppleEventType, ReceiptInfo } from './types' describe('AppleInApp', () => { let subject: AppleInApp let subscriptionRepository: MockedModule let subscriptionService: MockedModule beforeAll(async () => { subscriptionRepository = { getByOriginalTransactionAndPaymentProvider: jest.fn(), } subscriptionService = { cancelSubscription: jest.fn(), uncancel: jest.fn(), } const app: TestingModule = await Test.createTestingModule({ providers: [ { provide: SubscriptionRepository, useValue: subscriptionRepository, }, { provide: SubscriptionService, useValue: subscriptionService, }, AppleInApp, ], }).compile() subject = app.get(AppleInApp) }) afterEach(() => { jest.clearAllMocks() }) afterAll(() => { jest.restoreAllMocks() }) describe('#handle()', () => { let subscription: Subscription let receiptInfo: ReceiptInfo describe('when hasn`t a subscription with given token', () => { beforeAll(async () => { receiptInfo = await factory.build('apple:receipt') subscriptionRepository.getByOriginalTransactionAndPaymentProvider.mockResolvedValue(null) }) it('throws an error', async () => { await expect(subject.handle('DID_CHANGE_RENEWAL_STATUS', receiptInfo)).rejects.toThrowError(NotFoundError) }) }) describe('when has a subscription with given token', () => { beforeEach(async () => { subscription = await factory.build('subscription') subscriptionRepository.getByOriginalTransactionAndPaymentProvider.mockResolvedValue(subscription) }) describe.each` type ${'CANCEL'} ${'DID_CHANGE_RENEWAL_STATUS'} ${'DID_FAIL_TO_RENEW'} `('and notification is "$type"', ({ type }) => { let expiresAt: Date beforeEach(() => { expiresAt = new Date(+receiptInfo.expires_date_ms) }) describe('and subscription is already expired', () => { beforeAll(() => { receiptInfo.expires_date_ms = date .past() .valueOf() .toString() }) it('cancels with cancellationAt property defined', async () => { await subject.handle(type as AppleEventType, receiptInfo) expect(subscriptionService.cancelSubscription).toHaveBeenCalledWith({ patient: new Patient({ id: subscription.patientId, subscription: new Subscription({ ...subscription, cancellationAt: expiresAt, cancellationRequestAt: new Date(+receiptInfo.cancellation_date_ms), }), }), cancellationReason: { id: CancellationsReasons.IN_APP_CANCELLED, }, cancellationBy: { id: subscription.patientId }, expiresAt, }) }) }) describe('and subscription isn`t already expired', () => { beforeAll(() => { receiptInfo.expires_date_ms = date .future() .valueOf() .toString() }) it(`cancels the subscription`, async () => { await subject.handle(type as AppleEventType, receiptInfo) expect(subscriptionService.cancelSubscription).toHaveBeenCalledWith({ patient: new Patient({ id: subscription.patientId, subscription: new Subscription({ ...subscription, cancellationAt: undefined }), }), cancellationReason: { id: CancellationsReasons.IN_APP_CANCELLED, }, cancellationBy: { id: subscription.patientId }, expiresAt, }) }) }) }) describe('and notification is DID_RECOVER', () => { it('uncancels the subscription', async () => { await subject.handle('DID_RECOVER', receiptInfo) expect(subscriptionService.uncancel).toHaveBeenCalledWith({ ...subscription, expiresAt: new Date(+receiptInfo.expires_date_ms), }) }) }) }) }) })