From 6ba90d0ff1682d3399c3d8192ddbb686134184c8 Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Tue, 8 Nov 2022 16:09:27 -0800 Subject: [PATCH] `ReceiptRefreshPolicy.retryUntilProductIsFound`: default to returning "invalid" receipt (#2024) See #1945. To make this retry mechanism less risky when eventually enabled, this change makes the "worst case scenario" (where retrying didn't help, or `AppleReceipt.containsActivePurchase` has issues) equivalent to `ReceiptFetcher.onlyIfEmpty`. _Note: this is currently not enabled in production, only in Integration Tests._ --- Sources/Purchasing/ReceiptFetcher.swift | 5 ++-- Tests/UnitTests/Mocks/MockReceiptParser.swift | 2 +- .../Purchasing/ReceiptFetcherTests.swift | 29 +++++++++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Sources/Purchasing/ReceiptFetcher.swift b/Sources/Purchasing/ReceiptFetcher.swift index bd3ad3be33..cf79d308d8 100644 --- a/Sources/Purchasing/ReceiptFetcher.swift +++ b/Sources/Purchasing/ReceiptFetcher.swift @@ -151,10 +151,11 @@ private extension ReceiptFetcher { sleepDuration: DispatchTimeInterval ) async -> Data { var retries = 0 + var data: Data = .init() repeat { retries += 1 - let data = await self.refreshReceipt() + data = await self.refreshReceipt() if !data.isEmpty { do { @@ -176,7 +177,7 @@ private extension ReceiptFetcher { try? await Task.sleep(nanoseconds: UInt64(sleepDuration.nanoseconds)) } while retries <= maximumRetries && !Task.isCancelled - return Data() + return data } } diff --git a/Tests/UnitTests/Mocks/MockReceiptParser.swift b/Tests/UnitTests/Mocks/MockReceiptParser.swift index 4088c87230..156d5bb611 100644 --- a/Tests/UnitTests/Mocks/MockReceiptParser.swift +++ b/Tests/UnitTests/Mocks/MockReceiptParser.swift @@ -54,7 +54,7 @@ class MockReceiptParser: ReceiptParser { // This is used to mock changing receipts over time. return try self.stubbedParseResults[self.invokedParseCount - 1].get() } else { - return try XCTUnwrap(self.stubbedParseResults.first?.get()) + return try XCTUnwrap(self.stubbedParseResults.first).get() } } diff --git a/Tests/UnitTests/Purchasing/ReceiptFetcherTests.swift b/Tests/UnitTests/Purchasing/ReceiptFetcherTests.swift index d8f9790289..cbc53f506f 100644 --- a/Tests/UnitTests/Purchasing/ReceiptFetcherTests.swift +++ b/Tests/UnitTests/Purchasing/ReceiptFetcherTests.swift @@ -190,7 +190,7 @@ final class RetryingReceiptFetcherTests: BaseReceiptFetcherTests { self.mock(receipt: Self.receiptWithoutPurchases) let data = await self.fetch(productIdentifier: Self.productID, retries: 0) - expect(data) == Data() + expect(data) == Self.receiptWithoutPurchases.asData expect(self.mockReceiptParser.invokedParseParametersList) == [ Self.receiptWithoutPurchases.asData @@ -203,7 +203,7 @@ final class RetryingReceiptFetcherTests: BaseReceiptFetcherTests { let invalidData = Self.receiptWithoutPurchases.asData let data = await self.fetch(productIdentifier: Self.productID, retries: 2) - expect(data) == Data() + expect(data) == invalidData expect(self.mockReceiptParser.invokedParseParametersList) == [ invalidData, @@ -240,6 +240,19 @@ final class RetryingReceiptFetcherTests: BaseReceiptFetcherTests { ] } + func testStopsRetryingEvenIfParsingReceiptKeepsThrowingError() async { + let invalidData = self.mockReceiptWithInvalidData() + + let data = await self.fetch(productIdentifier: Self.productID, retries: 1) + expect(data) == invalidData + + expect(self.mockRequestFetcher.refreshReceiptCalledCount) == 2 + expect(self.mockReceiptParser.invokedParseParametersList) == [ + invalidData, + invalidData + ] + } + func testStopsRetryingIfFindsValidReceipt() async { self.mock(receipts: [Self.receiptWithoutPurchases, Self.validReceipt]) @@ -283,6 +296,18 @@ final class RetryingReceiptFetcherTests: BaseReceiptFetcherTests { self.mockReceiptParser.stubbedParseResults = receipts } + private func mockReceiptWithInvalidData() -> Data { + let invalidData = Data(repeating: 0, count: 10) + + self.mockBundle.receiptURLResult = .receiptWithData + self.mockFileReader.mockedURLContents[self.mockBundle.appStoreReceiptURL!] = [invalidData] + self.mockReceiptParser.stubbedParseResults = [ + .failure(ErrorUtils.missingReceiptFileError()) + ] + + return invalidData + } + private static let productID = "com.revenuecat.test_product" private static let validReceipt = AppleReceipt(