/* eslint-disable import/first */ let myFetchMock import { date, datatype } from 'faker' import * as fetchMock from 'fetch-mock-jest' import { factory } from '../../../test/factories' import { NotFoundError } from '../../errors' import { Subscription } from '../../models/subscription.model' import { HttpService } from '../http' import { AppleInAppSubscriptionService } from './apple-in-app-subscription.service' import { ReceiptInfo } from './types' jest.mock('node-fetch', () => (myFetchMock = fetchMock.sandbox())) describe('AppleInAppSubscriptionService', () => { let subject: AppleInAppSubscriptionService const url = 'https://buy.itunes.apple.com/verifyReceipt' beforeAll(() => { subject = new AppleInAppSubscriptionService(new HttpService()) globalThis.secret = { apple: { sharedsecret: datatype.uuid(), }, } as any }) afterEach(() => { jest.resetAllMocks() myFetchMock.mockReset() }) afterAll(() => { jest.restoreAllMocks() }) describe('#verify()', () => { let subscription: Subscription beforeAll(async () => { subscription = await factory.build('subscription:apple') }) describe('when subscription hasn`t a receipt', () => { beforeAll(() => { subscription.token = null }) it('throws an error', async () => { await expect(subject.verify(subscription)).rejects.toThrowError(NotFoundError) }) }) describe('when subscription has a receipt', () => { let receipts: Array beforeAll(() => { subscription.token = datatype.uuid() }) beforeEach(() => { myFetchMock.post(url, { latest_receipt_info: receipts }) }) describe('with receipt existing on Apple', () => { let randomIndex: number beforeAll(async () => { const newReceipts = await factory.buildMany('apple:receipt', datatype.number({ min: 1, max: 10 })) const randomNumberOfYears = datatype.number({ min: 1, max: 10 }) receipts = newReceipts.map(receipt => ({ ...receipt, purchase_date_ms: date .between(date.past(randomNumberOfYears), date.future(randomNumberOfYears)) .valueOf() .toString(), })) randomIndex = datatype.number({ min: 0, max: receipts.length - 1 }) }) describe('with at least one not expired', () => { beforeAll(() => { receipts[randomIndex].expires_date_ms = date .future(1) .getTime() .toString() }) it('returns true', async () => { expect(await subject.verify(subscription)).toEqual(true) }) }) describe('without at least one not expired', () => { beforeAll(() => { receipts = receipts.map(receipt => ({ ...receipt, expires_date_ms: date .past(1) .valueOf() .toString(), })) }) it('returns false', async () => { expect(await subject.verify(subscription)).toEqual(false) }) }) }) describe('with receipt not existing on Apple', () => { beforeAll(() => { receipts = null }) it('throws an error', async () => { await expect(subject.verify(subscription)).rejects.toThrowError(NotFoundError) }) }) }) }) describe('#getExternalPlan()', () => { let subscription: Subscription beforeAll(async () => { subscription = await factory.build('subscription:apple') }) describe('when subscription hasn`t a receipt', () => { beforeAll(() => { subscription.token = null }) it('throws an error', async () => { await expect(subject.getExternalPlan(subscription)).rejects.toThrowError(NotFoundError) }) }) describe('when subscription has a receipt', () => { let receipts: Array beforeAll(() => { subscription.token = datatype.uuid() }) beforeEach(() => { myFetchMock.post('', { latest_receipt_info: receipts }) }) describe('with receipt existing on Apple', () => { beforeAll(async () => { receipts = [await factory.build('apple:receipt')] }) it('returns external plan', async () => { expect(await subject.getExternalPlan(subscription)).toEqual(receipts[0].product_id) }) }) describe('with receipt not existing on Apple', () => { beforeAll(() => { receipts = null }) it('throws an error', async () => { await expect(subject.getExternalPlan(subscription)).rejects.toThrowError(NotFoundError) }) }) }) }) describe('#getExternalSubscriptionIdentifier()', () => { let subscription: Subscription beforeAll(async () => { subscription = await factory.build('subscription:apple') }) describe('when subscription hasn`t a receipt', () => { beforeAll(() => { subscription.token = null }) it('throws an error', async () => { await expect(subject.getExternalSubscriptionIdentifier(subscription)).rejects.toThrowError(NotFoundError) }) }) describe('when subscription has a receipt', () => { let receipts: Array beforeAll(() => { subscription.token = datatype.uuid() }) beforeEach(() => { myFetchMock.post('', { latest_receipt_info: receipts }) }) describe('with receipt existing on Apple', () => { beforeAll(async () => { receipts = [await factory.build('apple:receipt')] }) it('returns subscription identifier', async () => { expect(await subject.getExternalSubscriptionIdentifier(subscription)).toEqual( receipts[0].original_transaction_id, ) }) }) describe('with receipt not existing on Apple', () => { beforeAll(() => { receipts = null }) it('throws an error', async () => { await expect(subject.getExternalSubscriptionIdentifier(subscription)).rejects.toThrowError(NotFoundError) }) }) }) }) describe('#getTrialExpiration()', () => { let subscription: Subscription beforeAll(async () => { subscription = await factory.build('subscription:apple') }) describe('when subscription hasn`t a receipt', () => { beforeAll(() => { subscription.token = null }) it('throws an error', async () => { await expect(subject.getTrialExpiration(subscription)).rejects.toThrowError(NotFoundError) }) }) describe('when subscription has an Apple receipt', () => { let receipts: Array beforeAll(() => { subscription.token = datatype.uuid() }) beforeEach(() => { myFetchMock.post('', { latest_receipt_info: receipts }) }) describe('with a trial period', () => { describe('by intro offer', () => { beforeAll(async () => { receipts = [await factory.build('apple:receipt:trial:intro')] }) it('returns the date', async () => { expect(await subject.getTrialExpiration(subscription)).toEqual(new Date(receipts[0].expires_date_ms)) }) }) describe('by trial', () => { beforeAll(async () => { receipts = [await factory.build('apple:receipt:trial')] }) it('returns the date', async () => { expect(await subject.getTrialExpiration(subscription)).toEqual(new Date(receipts[0].expires_date_ms)) }) }) }) describe('without a trial period', () => { beforeAll(async () => { receipts = [await factory.build('apple:receipt')] }) it('returns null', async () => { expect(await subject.getTrialExpiration(subscription)).toBeNull() }) }) }) }) describe('#getExpiration()', () => { let subscription: Subscription beforeAll(async () => { subscription = await factory.build('subscription:apple') }) describe('when subscription hasn`t a receipt', () => { beforeAll(() => { subscription.token = null }) it('throws an error', async () => { await expect(subject.getExpiration(subscription)).rejects.toThrowError(NotFoundError) }) }) describe('when subscription has an Apple receipt', () => { let receipts: Array beforeAll(async () => { subscription.token = datatype.uuid() receipts = [await factory.build('apple:receipt')] }) beforeEach(() => { myFetchMock.post('', { latest_receipt_info: receipts }) }) it('returns the date', async () => { expect(await subject.getExpiration(subscription)).toEqual(new Date(receipts[0].expires_date_ms)) }) }) }) })