Skip to content

Commit

Permalink
OfflineEntitlementsManager: disable offline CustomerInfo in obser…
Browse files Browse the repository at this point in the history
…ver mode (#2520)
  • Loading branch information
NachoSoto authored May 24, 2023
1 parent 7fe13aa commit c48b080
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 12 deletions.
4 changes: 4 additions & 0 deletions RevenueCat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@
37E35C8515C5E2D01B0AF5C1 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E3507939634ED5A9280544 /* Strings.swift */; };
42F1DF385E3C1F9903A07FBF /* ProductsFetcherSK1.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFB3CBAA73855779FE828CE2 /* ProductsFetcherSK1.swift */; };
4F0201C42A13C85500091612 /* Assertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F0201C32A13C85500091612 /* Assertions.swift */; };
4F0BBAAC2A1D253D000E75AB /* OfflineCustomerInfoCreatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F0BBAAB2A1D253D000E75AB /* OfflineCustomerInfoCreatorTests.swift */; };
4F2017D52A15587F0061F6EF /* OfflineStoreKitIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F2017D42A15587F0061F6EF /* OfflineStoreKitIntegrationTests.swift */; };
4F2018732A15797D0061F6EF /* TestLogHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57057FF728B0048900995F21 /* TestLogHandler.swift */; };
4F69EB092A14406E00ED6D4B /* Matchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F69EB082A14406E00ED6D4B /* Matchers.swift */; };
Expand Down Expand Up @@ -875,6 +876,7 @@
37E35F783903362B65FB7AF3 /* MockProductsRequestFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockProductsRequestFactory.swift; sourceTree = "<group>"; };
37E35FDA0A44EA03EA12DAA2 /* DateFormatter+ExtensionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DateFormatter+ExtensionsTests.swift"; sourceTree = "<group>"; };
4F0201C32A13C85500091612 /* Assertions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assertions.swift; sourceTree = "<group>"; };
4F0BBAAB2A1D253D000E75AB /* OfflineCustomerInfoCreatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineCustomerInfoCreatorTests.swift; sourceTree = "<group>"; };
4F2017D42A15587F0061F6EF /* OfflineStoreKitIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineStoreKitIntegrationTests.swift; sourceTree = "<group>"; };
4F69EB082A14406E00ED6D4B /* Matchers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Matchers.swift; sourceTree = "<group>"; };
4F8A58162A16EE3500EF97AD /* MockOfflineCustomerInfoCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockOfflineCustomerInfoCreator.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2075,6 +2077,7 @@
isa = PBXGroup;
children = (
57488B8A29CB756A0000EE7E /* ProductEntitlementMappingTests.swift */,
4F0BBAAB2A1D253D000E75AB /* OfflineCustomerInfoCreatorTests.swift */,
57488C2229CB89CC0000EE7E /* OfflineEntitlementsManagerTests.swift */,
57CB2A7929CCC61600C91439 /* CustomerInfoResponseHandlerTests.swift */,
);
Expand Down Expand Up @@ -3237,6 +3240,7 @@
351B51B926D450E800BD2BD7 /* TransactionsFactoryTests.swift in Sources */,
351B51BB26D450E800BD2BD7 /* ProductRequestDataInitializationTests.swift in Sources */,
351B515426D44B0A00BD2BD7 /* MockStoreKit1Wrapper.swift in Sources */,
4F0BBAAC2A1D253D000E75AB /* OfflineCustomerInfoCreatorTests.swift in Sources */,
35F8B8FA26E02AE1003C3363 /* MockTrialOrIntroPriceEligibilityChecker.swift in Sources */,
35D83300262FAD8000E60AC5 /* ETagManagerTests.swift in Sources */,
57DDA7B329CBEFB30098B89D /* MockPurchasedProductsFetcher.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion Sources/Logging/Strings/OfflineEntitlementsStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ extension OfflineEntitlementsStrings: CustomStringConvertible {
var description: String {
switch self {
case .offline_entitlements_not_available:
return "OS version does not support offline entitlements."
return "Offline entitlements not available."

case .product_entitlement_mapping_stale_updating:
return "ProductEntitlementMapping cache is stale, updating from network."
Expand Down
19 changes: 14 additions & 5 deletions Sources/Networking/Backend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,6 @@ extension Backend {

extension Backend {

// For testing
var networkTimeout: TimeInterval {
return self.config.httpClient.timeout
}

@objc var signatureVerificationEnabled: Bool {
return self.config.httpClient.signatureVerificationEnabled
}
Expand All @@ -190,3 +185,17 @@ extension Backend {
}

}

// MARK: - Testing extensions

extension Backend {

var networkTimeout: TimeInterval {
return self.config.httpClient.timeout
}

var offlineCustomerInfoEnabled: Bool {
return self.config.offlineCustomerInfoCreator != nil
}

}
5 changes: 3 additions & 2 deletions Sources/OfflineEntitlements/OfflineCustomerInfoCreator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ class OfflineCustomerInfoCreator {

static func createIfAvailable(
with purchasedProductsFetcher: PurchasedProductsFetcherType?,
productEntitlementMappingFetcher: ProductEntitlementMappingFetcher
productEntitlementMappingFetcher: ProductEntitlementMappingFetcher,
observerMode: Bool
) -> OfflineCustomerInfoCreator? {
guard let fetcher = purchasedProductsFetcher else {
guard let fetcher = purchasedProductsFetcher, !observerMode else {
Logger.debug(Strings.offlineEntitlements.offline_entitlements_not_available)
return nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class OfflineEntitlementsManager {
completion: (@MainActor @Sendable (Result<(), Error>) -> Void)?
) {
guard #available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *),
!self.systemInfo.observerMode,
!self.systemInfo.dangerousSettings.customEntitlementComputation else {
Logger.debug(Strings.offlineEntitlements.product_entitlement_mapping_unavailable)

Expand Down
7 changes: 6 additions & 1 deletion Sources/Purchasing/Purchases/Purchases.swift
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,8 @@ public typealias StartPurchaseBlock = (@escaping PurchaseCompletedBlock) -> Void
attributionFetcher: attributionFetcher,
offlineCustomerInfoCreator: .createIfAvailable(
with: purchasedProductsFetcher,
productEntitlementMappingFetcher: deviceCache
productEntitlementMappingFetcher: deviceCache,
observerMode: observerMode
)
)

Expand Down Expand Up @@ -1406,6 +1407,10 @@ internal extension Purchases {
return self.userDefaults
}

var offlineCustomerInfoEnabled: Bool {
return self.backend.offlineCustomerInfoEnabled
}

var publicKey: Signing.PublicKey? {
return self.systemInfo.responseVerificationMode.publicKey
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// OfflineCustomerInfoCreatorTests.swift
//
// Created by Nacho Soto on 5/23/23.

import Nimble
@testable import RevenueCat
import StoreKit
import XCTest

class OfflineCustomerInfoCreatorTests: TestCase {

func testCreateIfAvailableWithNoFetcherReturnsNil() {
expect(
OfflineCustomerInfoCreator.createIfAvailable(
with: nil,
productEntitlementMappingFetcher: MockProductEntitlementMappingFetcher(),
observerMode: false
)
).to(beNil())
}

func testCreateIfAvailableInObserverMode() {
expect(
OfflineCustomerInfoCreator.createIfAvailable(
with: MockPurchasedProductsFetcher(),
productEntitlementMappingFetcher: MockProductEntitlementMappingFetcher(),
observerMode: true
)
).to(beNil())
}

func testCreateIfAvailable() {
expect(
OfflineCustomerInfoCreator.createIfAvailable(
with: MockPurchasedProductsFetcher(),
productEntitlementMappingFetcher: MockProductEntitlementMappingFetcher(),
observerMode: false
)
).toNot(beNil())
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class BaseOfflineEntitlementsManagerTests: TestCase {
override func setUpWithError() throws {
try super.setUpWithError()

self.mockSystemInfo = MockSystemInfo(finishTransactions: false)
self.mockSystemInfo = MockSystemInfo(finishTransactions: true)
self.mockOfflineEntitlements = try XCTUnwrap(
self.mockBackend.offlineEntitlements as? MockOfflineEntitlementsAPI
)
Expand All @@ -58,7 +58,21 @@ class OfflineEntitlementsManagerAvailableTests: BaseOfflineEntitlementsManagerTe
}

func testUpdateEntitlementsCacheForCustomEntitlementComputation() {
self.mockSystemInfo = MockSystemInfo(finishTransactions: false, customEntitlementsComputation: true)
self.mockSystemInfo = MockSystemInfo(finishTransactions: true, customEntitlementsComputation: true)
self.manager = self.createManager()

let result = waitUntilValue { completion in
self.manager.updateProductsEntitlementsCacheIfStale(isAppBackgrounded: false) {
completion($0)
}
}

expect(result).to(beFailure())
expect(result?.error).to(matchError(OfflineEntitlementsManager.Error.notAvailable))
}

func testUpdateEntitlementsCacheForObserverMode() {
self.mockSystemInfo = MockSystemInfo(finishTransactions: false)
self.manager = self.createManager()

let result = waitUntilValue { completion in
Expand Down Expand Up @@ -127,8 +141,9 @@ class OfflineEntitlementsManagerAvailableTests: BaseOfflineEntitlementsManagerTe
expect(self.manager.shouldComputeOfflineCustomerInfo(appUserID: "test")) == true
}

func testShouldNotComputeOfflineCustomerInfo() {
func testShouldNotComputeOfflineCustomerInfoIfThereIsACachedCustomerInfo() {
self.mockDeviceCache.cachedCustomerInfo["test"] = Data()

expect(self.manager.shouldComputeOfflineCustomerInfo(appUserID: "test")) == false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,33 @@ class PurchasesConfiguringTests: BasePurchasesTests {

private static let customUserDefaults: UserDefaults = .init(suiteName: "com.revenuecat.testing_user_defaults")!

// MARK: - OfflineCustomerInfoCreator

func testObserverModeDoesNotCreateOfflineCustomerInfoCreator() {
expect(Self.create(observerMode: true).offlineCustomerInfoEnabled) == false
}

func testOlderVersionsDoNoCreateOfflineCustomerInfo() throws {
if #available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) {
throw XCTSkip("Test for older versions")
}

expect(Self.create(observerMode: false).offlineCustomerInfoEnabled) == false
}

func testOfflineCustomerInfoEnabled() throws {
try AvailabilityChecks.iOS15APIAvailableOrSkipTest()

expect(Self.create(observerMode: false).offlineCustomerInfoEnabled) == true
}

private static func create(observerMode: Bool) -> Purchases {
return Purchases.configure(
with: .init(withAPIKey: "")
.with(observerMode: observerMode)
)
}

}

private extension UserDefaults {
Expand Down

0 comments on commit c48b080

Please sign in to comment.