Skip to content

Commit

Permalink
ReceiptRefreshPolicy.retryUntilProductIsFound: default to returning…
Browse files Browse the repository at this point in the history
… "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._
  • Loading branch information
NachoSoto authored Nov 9, 2022
1 parent 6c0b026 commit 6ba90d0
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 5 deletions.
5 changes: 3 additions & 2 deletions Sources/Purchasing/ReceiptFetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -176,7 +177,7 @@ private extension ReceiptFetcher {
try? await Task.sleep(nanoseconds: UInt64(sleepDuration.nanoseconds))
} while retries <= maximumRetries && !Task.isCancelled

return Data()
return data
}

}
2 changes: 1 addition & 1 deletion Tests/UnitTests/Mocks/MockReceiptParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Expand Down
29 changes: 27 additions & 2 deletions Tests/UnitTests/Purchasing/ReceiptFetcherTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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])

Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit 6ba90d0

Please sign in to comment.