Skip to content

Commit

Permalink
OfferingsManager: don't consider timeouts as configuration errors (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
NachoSoto authored May 16, 2023
1 parent b04d894 commit e4f6c99
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 10 deletions.
26 changes: 19 additions & 7 deletions Sources/Purchasing/OfferingsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,7 @@ private extension OfferingsManager {
let products = result.value ?? []

guard products.isEmpty == false else {
self.handleOfferingsUpdateError(
.configurationError(Strings.offering.configuration_error_products_not_found.description,
underlyingError: result.error as NSError?),
completion: completion
)
self.handleOfferingsUpdateError(Self.createErrorForEmptyResult(result.error), completion: completion)
return
}

Expand Down Expand Up @@ -168,6 +164,16 @@ private extension OfferingsManager {
}
}

private static func createErrorForEmptyResult(_ error: PurchasesError?) -> OfferingsManager.Error {
if let purchasesError = error,
case ErrorCode.productRequestTimedOut = purchasesError.error {
return .timeout(purchasesError)
} else {
return .configurationError(Strings.offering.configuration_error_products_not_found.description,
underlyingError: error?.asPublicError)
}
}

func handleOfferingsUpdateError(
_ error: Error,
completion: (@MainActor @Sendable (Result<Offerings, Error>) -> Void)?
Expand Down Expand Up @@ -213,10 +219,11 @@ extension OfferingsManager: @unchecked Sendable {}

extension OfferingsManager {

enum Error: Swift.Error, Equatable {
enum Error: Swift.Error {

case backendError(BackendError)
case configurationError(String, NSError?, ErrorSource)
case configurationError(String, PublicError?, ErrorSource)
case timeout(PurchasesError)
case noOfferingsFound(ErrorSource)
case missingProducts(identifiers: Set<String>, ErrorSource)

Expand All @@ -231,6 +238,9 @@ extension OfferingsManager.Error: PurchasesErrorConvertible {
case let .backendError(backendError):
return backendError.asPurchasesError

case let .timeout(underlyingError):
return underlyingError

case let .configurationError(errorMessage, underlyingError, source):
return ErrorUtils.configurationError(message: errorMessage,
underlyingError: underlyingError,
Expand Down Expand Up @@ -293,6 +303,7 @@ extension OfferingsManager.Error: CustomNSError {
var errorDescription: String? {
switch self {
case .backendError: return nil
case let .timeout(underlyingError): return underlyingError.error.localizedDescription
case let .configurationError(message, _, _): return message
case .noOfferingsFound: return nil
case .missingProducts: return nil
Expand All @@ -303,6 +314,7 @@ extension OfferingsManager.Error: CustomNSError {
switch self {
case let .backendError(.networkError(error)): return error
case let .backendError(error): return error
case let .timeout(underlyingError): return underlyingError
case let .configurationError(_, error, _): return error
case .noOfferingsFound: return nil
case .missingProducts: return nil
Expand Down
48 changes: 45 additions & 3 deletions Tests/UnitTests/Purchasing/OfferingsManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,13 @@ extension OfferingsManagerTests {

// then
expect(result).to(beFailure())
expect(result?.error) == .backendError(.unexpectedBackendResponse(.customerInfoNil))

switch result?.error {
case .backendError(.unexpectedBackendResponse(.customerInfoNil, _, _)):
break
default:
fail("Unexpected result")
}
}

func testOfferingsForAppUserIDReturnsConfigurationErrorIfBackendReturnsEmpty() throws {
Expand Down Expand Up @@ -180,6 +186,30 @@ extension OfferingsManagerTests {
}
}

func testOfferingsReturnsTimeoutErrorIfProductRequestTimesOut() throws {
// given
let timeoutError = ErrorUtils.productRequestTimedOutError()

self.mockOfferings.stubbedGetOfferingsCompletionResult = .success(
MockData.anyBackendOfferingsResponse
)
self.mockProductsManager.stubbedProductsCompletionResult = .failure(timeoutError)

// when
let result = waitUntilValue { completed in
self.offeringsManager.offerings(appUserID: MockData.anyAppUserID) {
completed($0)
}
}

// then
expect(result).to(beFailure())
expect(result?.error).to(matchError(OfferingsManager.Error.timeout(timeoutError)))

let underlyingError = try XCTUnwrap(result?.error?.errorUserInfo[NSUnderlyingErrorKey] as? NSError)
expect(underlyingError).to(matchError(timeoutError))
}

func testOfferingsLogsErrorInformationIfBackendReturnsEmpty() throws {
let logger = TestLogHandler()

Expand Down Expand Up @@ -274,7 +304,13 @@ extension OfferingsManagerTests {

// then
expect(result).to(beFailure())
expect(result?.error) == .noOfferingsFound()

switch result?.error {
case .noOfferingsFound:
break
default:
fail("Unexpected result")
}
}

func testOfferingsForAppUserIDReturnsUnexpectedBackendErrorIfBadBackendRequest() throws {
Expand All @@ -291,7 +327,13 @@ extension OfferingsManagerTests {

// then
expect(result).to(beFailure())
expect(result?.error) == .backendError(MockData.unexpectedBackendResponseError)

switch result?.error {
case .backendError(MockData.unexpectedBackendResponseError):
break
default:
fail("Unexpected result")
}
}

func testFailBackendDeviceCacheClearsOfferingsCache() {
Expand Down

0 comments on commit e4f6c99

Please sign in to comment.