-
Notifications
You must be signed in to change notification settings - Fork 316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Offline Entitlements
: integration tests
#2501
Conversation
8e823a7
to
860d17b
Compare
8417156
to
c83d3d0
Compare
860d17b
to
6cf0d99
Compare
31dd833
to
0f54435
Compare
6cf0d99
to
6cd570e
Compare
0f54435
to
f052941
Compare
0edce74
to
3719956
Compare
f052941
to
58f528c
Compare
@@ -100,3 +97,16 @@ import Foundation | |||
} | |||
|
|||
extension DangerousSettings: Sendable {} | |||
|
|||
/// Dangerous settings not exposed outside of the SDK. | |||
internal protocol InternalDangerousSettingsType: Sendable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allows having a custom type in integration tests so we can swap the return value, without having to make the actual DangerousSettings.Internal
struct
mutable.
@@ -13,14 +13,11 @@ import Foundation | |||
*/ | |||
@objc(RCDangerousSettings) public final class DangerousSettings: NSObject { | |||
|
|||
/// Dangerous settings not exposed outside of the SDK. | |||
internal struct InternalSettings { | |||
internal struct Internal: InternalDangerousSettingsType { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed this from DangerousSettings.InternalSettings
to DangerousSettings.Internal
.
Sources/Networking/Operations/Handling/CustomerInfoResponseHandler.swift
Outdated
Show resolved
Hide resolved
58f528c
to
6fcca74
Compare
Tests/BackendIntegrationTests/BaseBackendIntegrationTests.swift
Outdated
Show resolved
Hide resolved
private var dangerousSettings: DangerousSettings { | ||
return .init(autoSyncPurchases: true, | ||
internalSettings: .init(enableReceiptFetchRetry: true)) | ||
internalSettings: self) | ||
} | ||
|
||
} | ||
|
||
extension BaseBackendIntegrationTests: InternalDangerousSettingsType { | ||
|
||
var enableReceiptFetchRetry: Bool { return true } | ||
var forceServerErrors: Bool { return false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think composition over inheritance is the way to go here.
It feels unnatural for the test class itself, which is fairly large and complex, to conform to this protocol and being what gets "dependency-injected" into the DangerousSettings
initializer.
Why not have BaseBackendIntegrationTests
instances hold a member variable of a new type, say a MutableInternalDangerousSettings
class, that conforms to this protocol?
The serverUp()
and serverDown()
methods can then just mutate forceServerErrors
that variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think composition over inheritance is the way to go
Normally I agree, but I fail to see how that would improve this particular case. That would semantically be the same.
This set up is much simpler this way. The way these tests are set up is such that the tests themselves become the configuration, which allows us to override flags like this.
What benefit does your suggestion provide?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really love these tests! I think I should be able to copy part of it for the Android tests 👍. Though we don't support coroutines there yet so they won't look nearly as nice 😬
ee1d6bc
to
82bda68
Compare
14d252b
to
777318f
Compare
d6d52c7
to
a44e3d1
Compare
Codecov Report
@@ Coverage Diff @@
## nacho/sdk-2991-write-flow-to-use-locally-computed #2501 +/- ##
=====================================================================================
+ Coverage 87.83% 87.89% +0.05%
=====================================================================================
Files 198 198
Lines 13583 13617 +34
=====================================================================================
+ Hits 11931 11968 +37
+ Misses 1652 1649 -3
|
3bd13cb
to
f72a183
Compare
6115e26
to
8459cb7
Compare
f72a183
to
d76b395
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! This brought a great point in favor of supporting coroutines in Android 🙏
} | ||
|
||
@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) | ||
func testPurchaseAgainAfterServerRecovers() async throws { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe apple/Google shouldn't allow you to purchase the same subscription a second time right? So not sure if this is a valid scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can call the method multiple times, it will just reuse the transaction if it's not finished. It's not very important, but I was thinking "what if for some reason the purchase fails the first time while the server is down, will the SDK get in a weird state"?
So that's why I wrote this.
} | ||
|
||
@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) | ||
func testReopeningAppWithOfflineEntitlementsDoesNotReturnStaleCache() async throws { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add a test case for foregrounding the app sends unsynced purchases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's done in testPurchaseWhileServerIsDownPostsReceiptAfterServerComesBack
.
Tests/BackendIntegrationTests/OfflineStoreKitIntegrationTests.swift
Outdated
Show resolved
Hide resolved
…er is down (#2368) ### Changes: - Created `PurchasedProductsFetcherType` to be able to mock and hold fetchers, even on older devices - Created `ProductEntitlementMappingFetcher` to abstract injecting `ProductEntitlementMapping` - Added logic to `CustomerInfoResponseHandler` to compute offline `CustomerInfo` after a server error (see #2367). **This is used by all `CustomerInfo` requests as well as `PostReceiptDataOperation`**. - Wrote comprehensive `CustomerInfoResponseHandler` tests ### TODO: - [x] Don't cache offline computed `CustomerInfo`: #2378 - [x] Integration tests: #2501.
09ad41b
to
fd03123
Compare
**This is an automatic release.** ### New Features * `Offline Entitlements`: use offline-computed `CustomerInfo` when server is down (#2368) via NachoSoto (@NachoSoto) ### Bugfixes * `AppleReceipt.debugDescription`: don't pretty-print JSON (#2564) via NachoSoto (@NachoSoto) * `SK2StoreProduct`: fix crash on iOS 12 (#2565) via NachoSoto (@NachoSoto) * `GetCustomerInfo` posts receipts if there are pending transactions (#2533) via NachoSoto (@NachoSoto) ### Performance Improvements * `PurchasedProductsFetcher`: cache current entitlements (#2507) via NachoSoto (@NachoSoto) * Performance: new check to ensure serialization / deserialization is done from background thread (#2496) via NachoSoto (@NachoSoto) ### Dependency Updates * Bump fastlane from 2.212.2 to 2.213.0 (#2544) via dependabot[bot] (@dependabot[bot]) ### Other Changes * `CustomerInfoManager`: post all unfinished transactions (#2563) via NachoSoto (@NachoSoto) * `PostReceiptOperation`: added ability to also post `AdServices` token (#2566) via NachoSoto (@NachoSoto) * `Offline Entitlements`: improved computation log (#2562) via NachoSoto (@NachoSoto) * Added `TransactionPoster` tests (#2557) via NachoSoto (@NachoSoto) * Refactored `TransactionPoster`: removed 2 dependencies and abstracted parameters (#2542) via NachoSoto (@NachoSoto) * `CustomerInfoManagerTests`: wait for `getAndCacheCustomerInfo` to finish (#2555) via NachoSoto (@NachoSoto) * `StoreTransaction`: implemented `description` (#2556) via NachoSoto (@NachoSoto) * `Backend.ResponseHandler` is now `@Sendable` (#2541) via NachoSoto (@NachoSoto) * Extracted `TransactionPoster` from `PurchasesOrchestrator` (#2540) via NachoSoto (@NachoSoto) * `enableAdServicesAttributionTokenCollection`: added integration test (#2551) via NachoSoto (@NachoSoto) * `AttributionPoster`: replaced hardcoded strings with constants (#2548) via NachoSoto (@NachoSoto) * `DefaultDecodable`: moved to `Misc/Codable/DefaultDecodable.swift` (#2528) via NachoSoto (@NachoSoto) * `CircleCI`: specify device to run `backend_integration_tests` (#2547) via NachoSoto (@NachoSoto) * Created `StoreKit2TransactionFetcher` (#2539) via NachoSoto (@NachoSoto) * Fix load shedder integration tests (#2546) via Josh Holtz (@joshdholtz) * Fix doc on `Offering.getMetadataValue` (#2545) via Josh Holtz (@joshdholtz) * Extracted and tested `AsyncSequence.extractValues` (#2538) via NachoSoto (@NachoSoto) * `Offline Entitlements`: don't compute offline `CustomerInfo` when purchasing a consumable products (#2522) via NachoSoto (@NachoSoto) * `OfflineEntitlementsManager`: disable offline `CustomerInfo` in observer mode (#2520) via NachoSoto (@NachoSoto) * `BasePurchasesTests`: fixed leak detection (#2534) via NachoSoto (@NachoSoto) * `PurchaseTesterSwiftUI`: added `ProxyView` to `iOS` (#2531) via NachoSoto (@NachoSoto) * `PurchasedProductsFetcher`: removed `AppStore.sync` call (#2521) via NachoSoto (@NachoSoto) * `PurchaseTesterSwiftUI`: added new window on Mac to manage proxy (#2518) via NachoSoto (@NachoSoto) * `PurchasedProductsFetcher`: added log if fetching purchased products is slow (#2515) via NachoSoto (@NachoSoto) * `Offline Entitlements`: disable for custom entitlements mode (#2509) via NachoSoto (@NachoSoto) * `Offline Entitlements`: fixed iOS 12 tests (#2514) via NachoSoto (@NachoSoto) * `PurchasedProductsFetcher`: don't throw errors if purchased products were found (#2506) via NachoSoto (@NachoSoto) * `Offline Entitlements`: allow creating offline `CustomerInfo` with empty `ProductEntitlementMapping` (#2504) via NachoSoto (@NachoSoto) * `Offline Entitlements`: integration tests (#2501) via NachoSoto (@NachoSoto) * `CustomerInfoManager`: don't cache offline `CustomerInfo` (#2378) via NachoSoto (@NachoSoto) * `DangerousSettings`: debug-only `forceServerErrors` (#2486) via NachoSoto (@NachoSoto) * `CocoapodsInstallation`: fixed `Xcode 14.3.0` issue (#2489) via NachoSoto (@NachoSoto) * `CarthageInstallation`: removed workaround (#2488) via NachoSoto (@NachoSoto) --------- Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
Example tests: