From 1c319ac0fad13c4ee51a211f3e5e9bb99f35021b Mon Sep 17 00:00:00 2001 From: LouiseHsu Date: Tue, 23 Jul 2024 17:38:14 -0700 Subject: [PATCH] [in_app_purchase_storekit] convert paymentQueueTests to swift (#7204) Part of https://github.com/flutter/flutter/issues/151624 --- .../ios/Runner.xcodeproj/project.pbxproj | 8 +- .../ios/RunnerTests/PaymentQueueTests.m | 1 - .../ios/RunnerTests/PaymentQueueTests.swift | 1 + .../macos/Runner.xcodeproj/project.pbxproj | 8 +- .../macos/RunnerTests/PaymentQueueTests.m | 1 - .../macos/RunnerTests/PaymentQueueTests.swift | 1 + .../shared/RunnerTests/PaymentQueueTests.m | 515 ------------------ .../RunnerTests/PaymentQueueTests.swift | 496 +++++++++++++++++ 8 files changed, 506 insertions(+), 525 deletions(-) delete mode 120000 packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.m create mode 120000 packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.swift delete mode 120000 packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/PaymentQueueTests.m create mode 120000 packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/PaymentQueueTests.swift delete mode 100644 packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/PaymentQueueTests.m create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/PaymentQueueTests.swift diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj index ce090286dd23..3c0fb772c8f0 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj @@ -23,8 +23,8 @@ F27694112C49BF6F00277144 /* FIAPPaymentQueueDeleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F27694102C49BF6F00277144 /* FIAPPaymentQueueDeleteTests.swift */; }; F27694172C49DBCA00277144 /* FIATransactionCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F27694162C49DBCA00277144 /* FIATransactionCacheTests.swift */; }; F295AD3A2C1256DD0067C78A /* Stubs.m in Sources */ = {isa = PBXBuildFile; fileRef = F295AD392C1256DD0067C78A /* Stubs.m */; }; - F295AD452C1256F50067C78A /* PaymentQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F295AD3F2C1256F50067C78A /* PaymentQueueTests.m */; }; F295AD462C1256F50067C78A /* TranslatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F295AD402C1256F50067C78A /* TranslatorTests.m */; }; + F2D5271A2C50627500C137C7 /* PaymentQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D527192C50627500C137C7 /* PaymentQueueTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -81,8 +81,8 @@ F27694162C49DBCA00277144 /* FIATransactionCacheTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FIATransactionCacheTests.swift; path = ../../shared/RunnerTests/FIATransactionCacheTests.swift; sourceTree = ""; }; F295AD362C1251300067C78A /* Stubs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Stubs.h; path = ../../shared/RunnerTests/Stubs.h; sourceTree = ""; }; F295AD392C1256DD0067C78A /* Stubs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Stubs.m; path = ../../shared/RunnerTests/Stubs.m; sourceTree = ""; }; - F295AD3F2C1256F50067C78A /* PaymentQueueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PaymentQueueTests.m; path = ../../shared/RunnerTests/PaymentQueueTests.m; sourceTree = ""; }; F295AD402C1256F50067C78A /* TranslatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TranslatorTests.m; path = ../../shared/RunnerTests/TranslatorTests.m; sourceTree = ""; }; + F2D527192C50627500C137C7 /* PaymentQueueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PaymentQueueTests.swift; path = ../../shared/RunnerTests/PaymentQueueTests.swift; sourceTree = ""; }; F6E5D5F926131C4800C68BED /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = ""; }; /* End PBXFileReference section */ @@ -186,11 +186,11 @@ A59001A521E69658004A3E5E /* RunnerTests */ = { isa = PBXGroup; children = ( + F2D527192C50627500C137C7 /* PaymentQueueTests.swift */, F27694162C49DBCA00277144 /* FIATransactionCacheTests.swift */, F27694102C49BF6F00277144 /* FIAPPaymentQueueDeleteTests.swift */, F24C45E12C409D41000C6C72 /* InAppPurchasePluginTests.swift */, F276940A2C47268700277144 /* ProductRequestHandlerTests.swift */, - F295AD3F2C1256F50067C78A /* PaymentQueueTests.m */, F295AD402C1256F50067C78A /* TranslatorTests.m */, F295AD392C1256DD0067C78A /* Stubs.m */, F295AD362C1251300067C78A /* Stubs.h */, @@ -433,9 +433,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F2D5271A2C50627500C137C7 /* PaymentQueueTests.swift in Sources */, F24C45E22C409D42000C6C72 /* InAppPurchasePluginTests.swift in Sources */, F22BF91C2BC9B40B00713878 /* SwiftStubs.swift in Sources */, - F295AD452C1256F50067C78A /* PaymentQueueTests.m in Sources */, F276940B2C47268700277144 /* ProductRequestHandlerTests.swift in Sources */, F295AD462C1256F50067C78A /* TranslatorTests.m in Sources */, F295AD3A2C1256DD0067C78A /* Stubs.m in Sources */, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.m deleted file mode 120000 index f207cda68945..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.m +++ /dev/null @@ -1 +0,0 @@ -../../shared/RunnerTests/PaymentQueueTests.m \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.swift new file mode 120000 index 000000000000..781896937403 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.swift @@ -0,0 +1 @@ +../../shared/RunnerTests/PaymentQueueTests.swift \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj index 69be43bd74c6..8e29bf1bb935 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj @@ -32,7 +32,7 @@ F27694132C49BF7B00277144 /* FIAPPaymentQueueDeleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F27694122C49BF7B00277144 /* FIAPPaymentQueueDeleteTests.swift */; }; F27694192C49DBE800277144 /* FIATransactionCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F27694182C49DBE800277144 /* FIATransactionCacheTests.swift */; }; F2C3A7412BD9D33D000D35F2 /* Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C3A7402BD9D33D000D35F2 /* Stubs.swift */; }; - F79BDC182905FC1800E3999D /* PaymentQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC172905FC1800E3999D /* PaymentQueueTests.m */; }; + F2D5271E2C50645600C137C7 /* PaymentQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D5271D2C50645600C137C7 /* PaymentQueueTests.swift */; }; F79BDC1C2905FC3200E3999D /* Stubs.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC1B2905FC3200E3999D /* Stubs.m */; }; F79BDC1E2905FC3900E3999D /* TranslatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC1D2905FC3900E3999D /* TranslatorTests.m */; }; F8270DE1AEF80A8CF2C45688 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 045C0F7D19875EDA98DF0B7F /* Pods_RunnerTests.framework */; }; @@ -97,9 +97,9 @@ F27694182C49DBE800277144 /* FIATransactionCacheTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FIATransactionCacheTests.swift; path = ../../shared/RunnerTests/FIATransactionCacheTests.swift; sourceTree = ""; }; F2C3A73F2BD9D33D000D35F2 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; F2C3A7402BD9D33D000D35F2 /* Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stubs.swift; sourceTree = ""; }; + F2D5271D2C50645600C137C7 /* PaymentQueueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PaymentQueueTests.swift; path = ../../shared/RunnerTests/PaymentQueueTests.swift; sourceTree = ""; }; F700DD0228E652A10004836B /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F79BDC152905FC0500E3999D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../../shared/RunnerTests/Info.plist; sourceTree = ""; }; - F79BDC172905FC1800E3999D /* PaymentQueueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PaymentQueueTests.m; path = ../../shared/RunnerTests/PaymentQueueTests.m; sourceTree = ""; }; F79BDC1B2905FC3200E3999D /* Stubs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Stubs.m; path = ../../shared/RunnerTests/Stubs.m; sourceTree = ""; }; F79BDC1D2905FC3900E3999D /* TranslatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TranslatorTests.m; path = ../../shared/RunnerTests/TranslatorTests.m; sourceTree = ""; }; F79BDC1F2906023C00E3999D /* Stubs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Stubs.h; path = ../../shared/RunnerTests/Stubs.h; sourceTree = ""; }; @@ -217,11 +217,11 @@ F700DD0328E652A10004836B /* RunnerTests */ = { isa = PBXGroup; children = ( + F2D5271D2C50645600C137C7 /* PaymentQueueTests.swift */, F27694182C49DBE800277144 /* FIATransactionCacheTests.swift */, F27694122C49BF7B00277144 /* FIAPPaymentQueueDeleteTests.swift */, F24C45E32C409D87000C6C72 /* InAppPurchasePluginTests.swift */, F27694082C4724B200277144 /* ProductRequestHandlerTests.swift */, - F79BDC172905FC1800E3999D /* PaymentQueueTests.m */, F79BDC1F2906023C00E3999D /* Stubs.h */, F79BDC152905FC0500E3999D /* Info.plist */, F79BDC1B2905FC3200E3999D /* Stubs.m */, @@ -463,9 +463,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F2D5271E2C50645600C137C7 /* PaymentQueueTests.swift in Sources */, F24C45E42C409D87000C6C72 /* InAppPurchasePluginTests.swift in Sources */, F79BDC1E2905FC3900E3999D /* TranslatorTests.m in Sources */, - F79BDC182905FC1800E3999D /* PaymentQueueTests.m in Sources */, F79BDC1C2905FC3200E3999D /* Stubs.m in Sources */, F27694092C4724B200277144 /* ProductRequestHandlerTests.swift in Sources */, F2C3A7412BD9D33D000D35F2 /* Stubs.swift in Sources */, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/PaymentQueueTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/PaymentQueueTests.m deleted file mode 120000 index f207cda68945..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/PaymentQueueTests.m +++ /dev/null @@ -1 +0,0 @@ -../../shared/RunnerTests/PaymentQueueTests.m \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/PaymentQueueTests.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/PaymentQueueTests.swift new file mode 120000 index 000000000000..781896937403 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/PaymentQueueTests.swift @@ -0,0 +1 @@ +../../shared/RunnerTests/PaymentQueueTests.swift \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/PaymentQueueTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/PaymentQueueTests.m deleted file mode 100644 index 0c6d51a3b75d..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/PaymentQueueTests.m +++ /dev/null @@ -1,515 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import "Stubs.h" - -@import in_app_purchase_storekit; - -@interface PaymentQueueTest : XCTestCase - -@property(nonatomic, strong) NSDictionary *periodMap; -@property(nonatomic, strong) NSDictionary *discountMap; -@property(nonatomic, strong) NSDictionary *productMap; -@property(nonatomic, strong) NSDictionary *productResponseMap; - -@end - -@implementation PaymentQueueTest - -- (void)setUp { - self.periodMap = @{@"numberOfUnits" : @(0), @"unit" : @(0)}; - self.discountMap = @{ - @"price" : @1.0, - @"currencyCode" : @"USD", - @"numberOfPeriods" : @1, - @"subscriptionPeriod" : self.periodMap, - @"paymentMode" : @1 - }; - self.productMap = @{ - @"price" : @1.0, - @"currencyCode" : @"USD", - @"productIdentifier" : @"123", - @"localizedTitle" : @"title", - @"localizedDescription" : @"des", - @"subscriptionPeriod" : self.periodMap, - @"introductoryPrice" : self.discountMap, - @"subscriptionGroupIdentifier" : @"com.group" - }; - self.productResponseMap = - @{@"products" : @[ self.productMap ], @"invalidProductIdentifiers" : [NSNull null]}; -} - -- (void)testTransactionPurchased { - XCTestExpectation *expectation = - [self expectationWithDescription:@"expect to get purchased transcation."]; - PaymentQueueStub *queue = [[PaymentQueueStub alloc] init]; - queue.testState = SKPaymentTransactionStatePurchased; - __block SKPaymentTransactionStub *tran; - FIAPaymentQueueHandler *handler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - tran = (SKPaymentTransactionStub *)transaction; - [expectation fulfill]; - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil - transactionCache:[[TransactionCacheStub alloc] init]]; - SKPayment *payment = - [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; - [handler startObservingPaymentQueue]; - [handler addPayment:payment]; - [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqual(tran.transactionState, SKPaymentTransactionStatePurchased); - XCTAssertEqualObjects(tran.transactionIdentifier, @"fakeID"); -} - -- (void)testTransactionFailed { - XCTestExpectation *expectation = - [self expectationWithDescription:@"expect to get failed transcation."]; - PaymentQueueStub *queue = [[PaymentQueueStub alloc] init]; - queue.testState = SKPaymentTransactionStateFailed; - __block SKPaymentTransactionStub *tran; - FIAPaymentQueueHandler *handler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - tran = (SKPaymentTransactionStub *)transaction; - [expectation fulfill]; - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil - transactionCache:[[TransactionCacheStub alloc] init]]; - - SKPayment *payment = - [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; - [handler startObservingPaymentQueue]; - [handler addPayment:payment]; - [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqual(tran.transactionState, SKPaymentTransactionStateFailed); - XCTAssertEqual(tran.transactionIdentifier, nil); -} - -- (void)testTransactionRestored { - XCTestExpectation *expectation = - [self expectationWithDescription:@"expect to get restored transcation."]; - PaymentQueueStub *queue = [[PaymentQueueStub alloc] init]; - queue.testState = SKPaymentTransactionStateRestored; - __block SKPaymentTransactionStub *tran; - FIAPaymentQueueHandler *handler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - tran = (SKPaymentTransactionStub *)transaction; - [expectation fulfill]; - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil - transactionCache:[[TransactionCacheStub alloc] init]]; - - SKPayment *payment = - [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; - [handler startObservingPaymentQueue]; - [handler addPayment:payment]; - [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqual(tran.transactionState, SKPaymentTransactionStateRestored); - XCTAssertEqualObjects(tran.transactionIdentifier, @"fakeID"); -} - -- (void)testTransactionPurchasing { - XCTestExpectation *expectation = - [self expectationWithDescription:@"expect to get purchasing transcation."]; - PaymentQueueStub *queue = [[PaymentQueueStub alloc] init]; - queue.testState = SKPaymentTransactionStatePurchasing; - __block SKPaymentTransactionStub *tran; - FIAPaymentQueueHandler *handler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - tran = (SKPaymentTransactionStub *)transaction; - [expectation fulfill]; - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil - transactionCache:[[TransactionCacheStub alloc] init]]; - - SKPayment *payment = - [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; - [handler startObservingPaymentQueue]; - [handler addPayment:payment]; - [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqual(tran.transactionState, SKPaymentTransactionStatePurchasing); - XCTAssertEqual(tran.transactionIdentifier, nil); -} - -- (void)testTransactionDeferred { - XCTestExpectation *expectation = - [self expectationWithDescription:@"expect to get deffered transcation."]; - PaymentQueueStub *queue = [[PaymentQueueStub alloc] init]; - queue.testState = SKPaymentTransactionStateDeferred; - __block SKPaymentTransactionStub *tran; - FIAPaymentQueueHandler *handler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - tran = (SKPaymentTransactionStub *)transaction; - [expectation fulfill]; - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil - transactionCache:[[TransactionCacheStub alloc] init]]; - SKPayment *payment = - [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; - [handler startObservingPaymentQueue]; - [handler addPayment:payment]; - [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqual(tran.transactionState, SKPaymentTransactionStateDeferred); - XCTAssertEqual(tran.transactionIdentifier, nil); -} - -- (void)testFinishTransaction { - XCTestExpectation *expectation = - [self expectationWithDescription:@"handler.transactions should be empty."]; - PaymentQueueStub *queue = [[PaymentQueueStub alloc] init]; - queue.testState = SKPaymentTransactionStateDeferred; - __block FIAPaymentQueueHandler *handler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - XCTAssertEqual(transactions.count, 1); - SKPaymentTransaction *transaction = transactions[0]; - [handler finishTransaction:transaction]; - } - transactionRemoved:^(NSArray *_Nonnull transactions) { - XCTAssertEqual(transactions.count, 1); - [expectation fulfill]; - } - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil - transactionCache:[[TransactionCacheStub alloc] init]]; - SKPayment *payment = - [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; - [handler startObservingPaymentQueue]; - [handler addPayment:payment]; - [self waitForExpectations:@[ expectation ] timeout:5]; -} - -- (void)testStartObservingPaymentQueueShouldNotProcessTransactionsWhenCacheIsEmpty { - TransactionCacheStub *cacheStub = [[TransactionCacheStub alloc] init]; - FIAPaymentQueueHandler *handler = - [[FIAPaymentQueueHandler alloc] initWithQueue:[[PaymentQueueStub alloc] init] - transactionsUpdated:^(NSArray *_Nonnull transactions) { - XCTFail("transactionsUpdated callback should not be called when cache is empty."); - } - transactionRemoved:^(NSArray *_Nonnull transactions) { - XCTFail("transactionRemoved callback should not be called when cache is empty."); - } - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:^(NSArray *_Nonnull downloads) { - XCTFail("updatedDownloads callback should not be called when cache is empty."); - } - transactionCache:cacheStub]; - - __block NSInteger TransactionCacheKeyUpdatedTransactionsInvokedCount = 0; - __block NSInteger TransactionCacheKeyUpdatedDownloadsInvokedCount = 0; - __block NSInteger TransactionCacheKeyRemovedTransactionsInvokedCount = 0; - - cacheStub.getObjectsForKeyStub = ^NSArray *_Nonnull(TransactionCacheKey key) { - switch (key) { - case TransactionCacheKeyUpdatedTransactions: - TransactionCacheKeyUpdatedTransactionsInvokedCount++; - break; - case TransactionCacheKeyUpdatedDownloads: - TransactionCacheKeyUpdatedDownloadsInvokedCount++; - break; - case TransactionCacheKeyRemovedTransactions: - TransactionCacheKeyRemovedTransactionsInvokedCount++; - break; - default: - XCTFail("Invalid transaction state was invoked."); - } - return nil; - }; - - [handler startObservingPaymentQueue]; - - XCTAssertEqual(1, TransactionCacheKeyUpdatedTransactionsInvokedCount); - XCTAssertEqual(1, TransactionCacheKeyUpdatedDownloadsInvokedCount); - XCTAssertEqual(1, TransactionCacheKeyRemovedTransactionsInvokedCount); -} - -- (void) - testStartObservingPaymentQueueShouldNotProcessTransactionsWhenCacheContainsEmptyTransactionArrays { - TransactionCacheStub *cacheStub = [[TransactionCacheStub alloc] init]; - FIAPaymentQueueHandler *handler = - [[FIAPaymentQueueHandler alloc] initWithQueue:[[PaymentQueueStub alloc] init] - transactionsUpdated:^(NSArray *_Nonnull transactions) { - XCTFail("transactionsUpdated callback should not be called when cache is empty."); - } - transactionRemoved:^(NSArray *_Nonnull transactions) { - XCTFail("transactionRemoved callback should not be called when cache is empty."); - } - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:^(NSArray *_Nonnull downloads) { - XCTFail("updatedDownloads callback should not be called when cache is empty."); - } - transactionCache:cacheStub]; - - __block NSInteger TransactionCacheKeyUpdatedTransactionsInvokedCount = 0; - __block NSInteger TransactionCacheKeyUpdatedDownloadsInvokedCount = 0; - __block NSInteger TransactionCacheKeyRemovedTransactionsInvokedCount = 0; - - cacheStub.getObjectsForKeyStub = ^NSArray *_Nonnull(TransactionCacheKey key) { - switch (key) { - case TransactionCacheKeyUpdatedTransactions: - TransactionCacheKeyUpdatedTransactionsInvokedCount++; - return @[]; - break; - case TransactionCacheKeyUpdatedDownloads: - TransactionCacheKeyUpdatedDownloadsInvokedCount++; - return @[]; - break; - case TransactionCacheKeyRemovedTransactions: - TransactionCacheKeyRemovedTransactionsInvokedCount++; - return @[]; - break; - default: - XCTFail("Invalid transaction state was invoked."); - } - }; - - [handler startObservingPaymentQueue]; - - XCTAssertEqual(1, TransactionCacheKeyUpdatedTransactionsInvokedCount); - XCTAssertEqual(1, TransactionCacheKeyUpdatedDownloadsInvokedCount); - XCTAssertEqual(1, TransactionCacheKeyRemovedTransactionsInvokedCount); -} - -- (void)testStartObservingPaymentQueueShouldProcessTransactionsForItemsInCache { - XCTestExpectation *updateTransactionsExpectation = - [self expectationWithDescription: - @"transactionsUpdated callback should be called with one transaction."]; - XCTestExpectation *removeTransactionsExpectation = - [self expectationWithDescription: - @"transactionsRemoved callback should be called with one transaction."]; - XCTestExpectation *updateDownloadsExpectation = - [self expectationWithDescription: - @"downloadsUpdated callback should be called with one transaction."]; - SKPaymentTransaction *transactionStub = [[SKPaymentTransactionStub alloc] init]; - SKDownload *downloadStub = [[SKDownload alloc] init]; - TransactionCacheStub *cacheStub = [[TransactionCacheStub alloc] init]; - FIAPaymentQueueHandler *handler = - [[FIAPaymentQueueHandler alloc] initWithQueue:[[PaymentQueueStub alloc] init] - transactionsUpdated:^(NSArray *_Nonnull transactions) { - XCTAssertEqualObjects(transactions, @[ transactionStub ]); - [updateTransactionsExpectation fulfill]; - } - transactionRemoved:^(NSArray *_Nonnull transactions) { - XCTAssertEqualObjects(transactions, @[ transactionStub ]); - [removeTransactionsExpectation fulfill]; - } - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:^(NSArray *_Nonnull downloads) { - XCTAssertEqualObjects(downloads, @[ downloadStub ]); - [updateDownloadsExpectation fulfill]; - } - transactionCache:cacheStub]; - - __block NSInteger TransactionCacheKeyUpdatedTransactionsInvokedCount = 0; - __block NSInteger TransactionCacheKeyUpdatedDownloadsInvokedCount = 0; - __block NSInteger TransactionCacheKeyRemovedTransactionsInvokedCount = 0; - - cacheStub.getObjectsForKeyStub = ^NSArray *_Nonnull(TransactionCacheKey key) { - switch (key) { - case TransactionCacheKeyUpdatedTransactions: - TransactionCacheKeyUpdatedTransactionsInvokedCount++; - return @[ transactionStub ]; - break; - case TransactionCacheKeyUpdatedDownloads: - TransactionCacheKeyUpdatedDownloadsInvokedCount++; - return @[ downloadStub ]; - break; - case TransactionCacheKeyRemovedTransactions: - TransactionCacheKeyRemovedTransactionsInvokedCount++; - return @[ transactionStub ]; - break; - default: - XCTFail("Invalid transaction state was invoked."); - } - }; - - __block NSInteger clearInvokedCount = 0; - cacheStub.clearStub = ^{ - clearInvokedCount++; - }; - - [handler startObservingPaymentQueue]; - - [self waitForExpectations:@[ - updateTransactionsExpectation, removeTransactionsExpectation, updateDownloadsExpectation - ] - timeout:5]; - - XCTAssertEqual(1, TransactionCacheKeyUpdatedTransactionsInvokedCount); - XCTAssertEqual(1, TransactionCacheKeyUpdatedDownloadsInvokedCount); - XCTAssertEqual(1, TransactionCacheKeyRemovedTransactionsInvokedCount); - XCTAssertEqual(1, clearInvokedCount); -} - -- (void)testTransactionsShouldBeCachedWhenNotObserving { - PaymentQueueStub *queue = [[PaymentQueueStub alloc] init]; - TransactionCacheStub *cacheStub = [[TransactionCacheStub alloc] init]; - FIAPaymentQueueHandler *handler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - XCTFail("transactionsUpdated callback should not be called when cache is empty."); - } - transactionRemoved:^(NSArray *_Nonnull transactions) { - XCTFail("transactionRemoved callback should not be called when cache is empty."); - } - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:^(NSArray *_Nonnull downloads) { - XCTFail("updatedDownloads callback should not be called when cache is empty."); - } - transactionCache:cacheStub]; - - SKPayment *payment = - [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; - - __block NSInteger TransactionCacheKeyUpdatedTransactionsInvokedCount = 0; - __block NSInteger TransactionCacheKeyUpdatedDownloadsInvokedCount = 0; - __block NSInteger TransactionCacheKeyRemovedTransactionsInvokedCount = 0; - - cacheStub.addObjectsStub = ^(NSArray *_Nonnull objects, TransactionCacheKey key) { - switch (key) { - case TransactionCacheKeyUpdatedTransactions: - TransactionCacheKeyUpdatedTransactionsInvokedCount++; - break; - case TransactionCacheKeyUpdatedDownloads: - TransactionCacheKeyUpdatedDownloadsInvokedCount++; - break; - case TransactionCacheKeyRemovedTransactions: - TransactionCacheKeyRemovedTransactionsInvokedCount++; - break; - default: - XCTFail("Invalid transaction state was invoked."); - } - }; - - [handler addPayment:payment]; - - XCTAssertEqual(1, TransactionCacheKeyUpdatedTransactionsInvokedCount); - XCTAssertEqual(0, TransactionCacheKeyUpdatedDownloadsInvokedCount); - XCTAssertEqual(0, TransactionCacheKeyRemovedTransactionsInvokedCount); -} - -- (void)testTransactionsShouldNotBeCachedWhenObserving { - XCTestExpectation *updateTransactionsExpectation = - [self expectationWithDescription: - @"transactionsUpdated callback should be called with one transaction."]; - XCTestExpectation *removeTransactionsExpectation = - [self expectationWithDescription: - @"transactionsRemoved callback should be called with one transaction."]; - XCTestExpectation *updateDownloadsExpectation = - [self expectationWithDescription: - @"downloadsUpdated callback should be called with one transaction."]; - SKPaymentTransaction *transactionStub = [[SKPaymentTransactionStub alloc] init]; - SKDownload *downloadStub = [[SKDownload alloc] init]; - PaymentQueueStub *queue = [[PaymentQueueStub alloc] init]; - queue.testState = SKPaymentTransactionStatePurchased; - TransactionCacheStub *cacheStub = [[TransactionCacheStub alloc] init]; - FIAPaymentQueueHandler *handler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - XCTAssertEqualObjects(transactions, @[ transactionStub ]); - [updateTransactionsExpectation fulfill]; - } - transactionRemoved:^(NSArray *_Nonnull transactions) { - XCTAssertEqualObjects(transactions, @[ transactionStub ]); - [removeTransactionsExpectation fulfill]; - } - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:^(NSArray *_Nonnull downloads) { - XCTAssertEqualObjects(downloads, @[ downloadStub ]); - [updateDownloadsExpectation fulfill]; - } - transactionCache:cacheStub]; - - SKPaymentQueueStub *paymentQueueStub = [[SKPaymentQueueStub alloc] init]; - - [handler startObservingPaymentQueue]; - [handler paymentQueue:paymentQueueStub updatedTransactions:@[ transactionStub ]]; - [handler paymentQueue:paymentQueueStub removedTransactions:@[ transactionStub ]]; - [handler paymentQueue:paymentQueueStub updatedDownloads:@[ downloadStub ]]; - - [self waitForExpectations:@[ - updateTransactionsExpectation, removeTransactionsExpectation, updateDownloadsExpectation - ] - timeout:5]; - - __block NSInteger TransactionCacheKeyUpdatedTransactionsInvokedCount = 0; - __block NSInteger TransactionCacheKeyUpdatedDownloadsInvokedCount = 0; - __block NSInteger TransactionCacheKeyRemovedTransactionsInvokedCount = 0; - - cacheStub.addObjectsStub = ^(NSArray *_Nonnull objects, TransactionCacheKey key) { - switch (key) { - case TransactionCacheKeyUpdatedTransactions: - TransactionCacheKeyUpdatedTransactionsInvokedCount++; - break; - case TransactionCacheKeyUpdatedDownloads: - TransactionCacheKeyUpdatedDownloadsInvokedCount++; - break; - case TransactionCacheKeyRemovedTransactions: - TransactionCacheKeyRemovedTransactionsInvokedCount++; - break; - default: - XCTFail("Invalid transaction state was invoked."); - } - }; - XCTAssertEqual(0, TransactionCacheKeyUpdatedTransactionsInvokedCount); - XCTAssertEqual(0, TransactionCacheKeyUpdatedDownloadsInvokedCount); - XCTAssertEqual(0, TransactionCacheKeyRemovedTransactionsInvokedCount); -} -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/PaymentQueueTests.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/PaymentQueueTests.swift new file mode 100644 index 000000000000..9f3a7e9d9cd5 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/PaymentQueueTests.swift @@ -0,0 +1,496 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import StoreKit +import XCTest + +@testable import in_app_purchase_storekit + +final class PaymentQueueTest: XCTestCase { + private var periodMap: [String: Any] { + return ["numberOfUnits": 0, "unit": 0] + } + private var discountMap: [String: Any] { + return [ + "price": 1.0, + "currencyCode": "USD", + "numberOfPeriods": 1, + "subscriptionPeriod": periodMap, + "paymentMode": 1, + ] + } + private var productMap: [String: Any] { + return [ + "price": 1.0, + "currencyCode": "USD", + "productIdentifier": "123", + "localizedTitle": "title", + "localizedDescription": "des", + "subscriptionPeriod": periodMap, + "introductoryPrice": discountMap, + "subscriptionGroupIdentifier": "com.group", + ] + } + private var productResponseMap: [String: Any] { + return ["products": [productMap], "invalidProductIdentifiers": []] + } + + func testTransactionPurchased() throws { + let expectation = self.expectation(description: "expect to get purchased transaction.") + let queue = PaymentQueueStub() + queue.testState = .purchased + var transactionStub: SKPaymentTransactionStub? + let handler = FIAPaymentQueueHandler( + queue: queue, + transactionsUpdated: { transactions in + let transaction = transactions[0] + transactionStub = transaction as? SKPaymentTransactionStub + expectation.fulfill() + }, + transactionRemoved: nil, + restoreTransactionFailed: nil, + restoreCompletedTransactionsFinished: nil, + shouldAddStorePayment: { _, _ in + return true + }, + updatedDownloads: nil, + transactionCache: TransactionCacheStub()) + let payment = SKPayment(product: SKProductStub(map: productResponseMap)) + + handler.startObservingPaymentQueue() + handler.add(payment) + waitForExpectations(timeout: 5) + + let unwrappedTransaction = try XCTUnwrap(transactionStub) + XCTAssertEqual(unwrappedTransaction.transactionState, .purchased) + XCTAssertEqual(unwrappedTransaction.transactionIdentifier, "fakeID") + } + + func testTransactionFailed() throws { + let expectation = self.expectation(description: "expect to get failed transaction.") + let queue = PaymentQueueStub() + queue.testState = .failed + var transactionStub: SKPaymentTransactionStub? + let handler = FIAPaymentQueueHandler( + queue: queue, + transactionsUpdated: { transactions in + let transaction = transactions[0] + transactionStub = transaction as? SKPaymentTransactionStub + expectation.fulfill() + }, + transactionRemoved: nil, + restoreTransactionFailed: nil, + restoreCompletedTransactionsFinished: nil, + shouldAddStorePayment: { _, _ in + return true + }, + updatedDownloads: nil, + transactionCache: TransactionCacheStub()) + let payment = SKPayment(product: SKProductStub(map: productResponseMap)) + + handler.startObservingPaymentQueue() + handler.add(payment) + waitForExpectations(timeout: 5) + + let unwrappedTransaction = try XCTUnwrap(transactionStub) + XCTAssertEqual(unwrappedTransaction.transactionState, .failed) + XCTAssertEqual(unwrappedTransaction.transactionIdentifier, nil) + } + + func testTransactionRestored() throws { + let expectation = self.expectation(description: "expect to get restored transaction.") + let queue = PaymentQueueStub() + queue.testState = .restored + var transactionStub: SKPaymentTransactionStub? + let handler = FIAPaymentQueueHandler( + queue: queue, + transactionsUpdated: { transactions in + let transaction = transactions[0] + transactionStub = transaction as? SKPaymentTransactionStub + expectation.fulfill() + }, + transactionRemoved: nil, + restoreTransactionFailed: nil, + restoreCompletedTransactionsFinished: nil, + shouldAddStorePayment: { _, _ in + return true + }, + updatedDownloads: nil, + transactionCache: TransactionCacheStub()) + let payment = SKPayment(product: SKProductStub(map: productResponseMap)) + + handler.startObservingPaymentQueue() + handler.add(payment) + waitForExpectations(timeout: 5) + + let unwrappedTransaction = try XCTUnwrap(transactionStub) + XCTAssertEqual(unwrappedTransaction.transactionState, .restored) + XCTAssertEqual(unwrappedTransaction.transactionIdentifier, "fakeID") + } + + func testTransactionPurchasing() throws { + let expectation = self.expectation(description: "expect to get purchasing transaction.") + let queue = PaymentQueueStub() + queue.testState = .purchasing + var transactionStub: SKPaymentTransactionStub? + let handler = FIAPaymentQueueHandler( + queue: queue, + transactionsUpdated: { transactions in + let transaction = transactions[0] + transactionStub = transaction as? SKPaymentTransactionStub + expectation.fulfill() + }, + transactionRemoved: nil, + restoreTransactionFailed: nil, + restoreCompletedTransactionsFinished: nil, + shouldAddStorePayment: { _, _ in + return true + }, + updatedDownloads: nil, + transactionCache: TransactionCacheStub()) + let payment = SKPayment(product: SKProductStub(map: productResponseMap)) + + handler.startObservingPaymentQueue() + handler.add(payment) + waitForExpectations(timeout: 5) + + let unwrappedTransaction = try XCTUnwrap(transactionStub) + XCTAssertEqual(unwrappedTransaction.transactionState, .purchasing) + XCTAssertEqual(unwrappedTransaction.transactionIdentifier, nil) + } + + func testTransactionDeferred() throws { + let expectation = self.expectation(description: "expect to get deferred transaction.") + let queue = PaymentQueueStub() + queue.testState = .deferred + var transactionStub: SKPaymentTransactionStub? + let handler = FIAPaymentQueueHandler( + queue: queue, + transactionsUpdated: { transactions in + let transaction = transactions[0] + transactionStub = transaction as? SKPaymentTransactionStub + expectation.fulfill() + }, + transactionRemoved: nil, + restoreTransactionFailed: nil, + restoreCompletedTransactionsFinished: nil, + shouldAddStorePayment: { _, _ in + return true + }, + updatedDownloads: nil, + transactionCache: TransactionCacheStub()) + let payment = SKPayment(product: SKProductStub(map: productResponseMap)) + + handler.startObservingPaymentQueue() + handler.add(payment) + waitForExpectations(timeout: 5) + + let unwrappedTransaction = try XCTUnwrap(transactionStub) + XCTAssertEqual(unwrappedTransaction.transactionState, .deferred) + XCTAssertEqual(unwrappedTransaction.transactionIdentifier, nil) + } + + func testFinishTransaction() { + let expectation = self.expectation(description: "handler.transactions should be empty.") + let queue = PaymentQueueStub() + queue.testState = .deferred + var handler: FIAPaymentQueueHandler! + handler = FIAPaymentQueueHandler( + queue: queue, + transactionsUpdated: { transactions in + XCTAssertEqual(transactions.count, 1) + let transaction = transactions[0] + handler.finish(transaction) + }, + transactionRemoved: { transactions in + XCTAssertEqual(transactions.count, 1) + expectation.fulfill() + }, + restoreTransactionFailed: nil, + restoreCompletedTransactionsFinished: nil, + shouldAddStorePayment: { _, _ in + return true + }, + updatedDownloads: nil, + transactionCache: TransactionCacheStub()) + let payment = SKPayment(product: SKProductStub(map: productResponseMap)) + + handler.startObservingPaymentQueue() + handler.add(payment) + + waitForExpectations(timeout: 5) + } + + func testStartObservingPaymentQueueShouldNotProcessTransactionsWhenCacheIsEmpty() { + let cacheStub = TransactionCacheStub() + let handler = FIAPaymentQueueHandler( + queue: PaymentQueueStub(), + transactionsUpdated: { _ in + XCTFail("transactionsUpdated callback should not be called when cache is empty.") + }, + transactionRemoved: { _ in + XCTFail("transactionRemoved callback should not be called when cache is empty.") + }, + restoreTransactionFailed: nil, + restoreCompletedTransactionsFinished: nil, + shouldAddStorePayment: { _, _ in + return true + }, + updatedDownloads: { _ in + XCTFail("updatedDownloads callback should not be called when cache is empty.") + }, + transactionCache: cacheStub) + + var transactionCacheKeyUpdatedTransactionsInvokedCount = 0 + var transactionCacheKeyUpdatedDownloadsInvokedCount = 0 + var transactionCacheKeyRemovedTransactionsInvokedCount = 0 + + cacheStub.getObjectsForKeyStub = { key in + switch key { + case .updatedTransactions: + transactionCacheKeyUpdatedTransactionsInvokedCount += 1 + case .updatedDownloads: + transactionCacheKeyUpdatedDownloadsInvokedCount += 1 + case .removedTransactions: + transactionCacheKeyRemovedTransactionsInvokedCount += 1 + default: + XCTFail("Invalid transaction state was invoked.") + } + return [] + } + + handler.startObservingPaymentQueue() + + XCTAssertEqual(transactionCacheKeyUpdatedTransactionsInvokedCount, 1) + XCTAssertEqual(transactionCacheKeyUpdatedDownloadsInvokedCount, 1) + XCTAssertEqual(transactionCacheKeyRemovedTransactionsInvokedCount, 1) + } + + func + testStartObservingPaymentQueueShouldNotProcessTransactionsWhenCacheContainsEmptyTransactionArrays() + { + let cacheStub = TransactionCacheStub() + let handler = FIAPaymentQueueHandler( + queue: PaymentQueueStub(), + transactionsUpdated: { _ in + XCTFail("transactionsUpdated callback should not be called when cache is empty.") + }, + transactionRemoved: { _ in + XCTFail("transactionRemoved callback should not be called when cache is empty.") + }, + restoreTransactionFailed: nil, + restoreCompletedTransactionsFinished: nil, + shouldAddStorePayment: { _, _ in + return true + }, + updatedDownloads: { _ in + XCTFail("updatedDownloads callback should not be called when cache is empty.") + }, + transactionCache: cacheStub) + + var transactionCacheKeyUpdatedTransactionsInvokedCount = 0 + var transactionCacheKeyUpdatedDownloadsInvokedCount = 0 + var transactionCacheKeyRemovedTransactionsInvokedCount = 0 + + cacheStub.getObjectsForKeyStub = { key in + switch key { + case .updatedTransactions: + transactionCacheKeyUpdatedTransactionsInvokedCount += 1 + return [] + case .updatedDownloads: + transactionCacheKeyUpdatedDownloadsInvokedCount += 1 + return [] + case .removedTransactions: + transactionCacheKeyRemovedTransactionsInvokedCount += 1 + return [] + default: + XCTFail("Invalid transaction state was invoked.") + } + return [] + } + + handler.startObservingPaymentQueue() + + XCTAssertEqual(transactionCacheKeyUpdatedTransactionsInvokedCount, 1) + XCTAssertEqual(transactionCacheKeyUpdatedDownloadsInvokedCount, 1) + XCTAssertEqual(transactionCacheKeyRemovedTransactionsInvokedCount, 1) + } + + func testStartObservingPaymentQueueShouldProcessTransactionsForItemsInCache() { + let updateTransactionsExpectation = expectation( + description: "transactionsUpdated callback should be called with one transaction.") + let removeTransactionsExpectation = expectation( + description: "transactionsRemoved callback should be called with one transaction.") + let updateDownloadsExpectation = expectation( + description: "downloadsUpdated callback should be called with one transaction.") + let transactionStub = SKPaymentTransactionStub() + let downloadStub = SKDownload() + let cacheStub = TransactionCacheStub() + let handler = FIAPaymentQueueHandler( + queue: PaymentQueueStub(), + transactionsUpdated: { transactions in + XCTAssertEqual(transactions as? [SKPaymentTransactionStub], [transactionStub]) + updateTransactionsExpectation.fulfill() + }, + transactionRemoved: { transactions in + XCTAssertEqual(transactions as? [SKPaymentTransactionStub], [transactionStub]) + removeTransactionsExpectation.fulfill() + }, + restoreTransactionFailed: nil, + restoreCompletedTransactionsFinished: nil, + shouldAddStorePayment: { _, _ in + return true + }, + updatedDownloads: { downloads in + XCTAssertEqual(downloads as NSArray, [downloadStub] as NSArray) + updateDownloadsExpectation.fulfill() + }, + transactionCache: cacheStub) + + var transactionCacheKeyUpdatedTransactionsInvokedCount = 0 + var transactionCacheKeyUpdatedDownloadsInvokedCount = 0 + var transactionCacheKeyRemovedTransactionsInvokedCount = 0 + + cacheStub.getObjectsForKeyStub = { key in + switch key { + case .updatedTransactions: + transactionCacheKeyUpdatedTransactionsInvokedCount += 1 + return [transactionStub] + case .updatedDownloads: + transactionCacheKeyUpdatedDownloadsInvokedCount += 1 + return [downloadStub] + case .removedTransactions: + transactionCacheKeyRemovedTransactionsInvokedCount += 1 + return [transactionStub] + default: + XCTFail("Invalid transaction state was invoked.") + } + return [] + } + + var clearInvokedCount = 0 + cacheStub.clearStub = { + clearInvokedCount += 1 + } + + handler.startObservingPaymentQueue() + + waitForExpectations(timeout: 5) + XCTAssertEqual(transactionCacheKeyUpdatedTransactionsInvokedCount, 1) + XCTAssertEqual(transactionCacheKeyUpdatedDownloadsInvokedCount, 1) + XCTAssertEqual(transactionCacheKeyRemovedTransactionsInvokedCount, 1) + XCTAssertEqual(clearInvokedCount, 1) + } + + func testTransactionsShouldBeCachedWhenNotObserving() { + let queue = PaymentQueueStub() + let cacheStub = TransactionCacheStub() + let handler = FIAPaymentQueueHandler( + queue: queue, + transactionsUpdated: { _ in + XCTFail("transactionsUpdated callback should not be called when cache is empty.") + }, + transactionRemoved: { _ in + XCTFail("transactionRemoved callback should not be called when cache is empty.") + }, + restoreTransactionFailed: nil, + restoreCompletedTransactionsFinished: nil, + shouldAddStorePayment: { _, _ in + return true + }, + updatedDownloads: { _ in + XCTFail("updatedDownloads callback should not be called when cache is empty.") + }, + transactionCache: cacheStub) + + let payment = SKPayment(product: SKProductStub(map: productResponseMap)) + + var transactionCacheKeyUpdatedTransactionsInvokedCount = 0 + var transactionCacheKeyUpdatedDownloadsInvokedCount = 0 + var transactionCacheKeyRemovedTransactionsInvokedCount = 0 + + cacheStub.addObjectsStub = { objects, key in + switch key { + case .updatedTransactions: + transactionCacheKeyUpdatedTransactionsInvokedCount += 1 + case .updatedDownloads: + transactionCacheKeyUpdatedDownloadsInvokedCount += 1 + case .removedTransactions: + transactionCacheKeyRemovedTransactionsInvokedCount += 1 + default: + XCTFail("Invalid transaction state was invoked.") + } + } + + handler.add(payment) + + XCTAssertEqual(transactionCacheKeyUpdatedTransactionsInvokedCount, 1) + XCTAssertEqual(transactionCacheKeyUpdatedDownloadsInvokedCount, 0) + XCTAssertEqual(transactionCacheKeyRemovedTransactionsInvokedCount, 0) + } + + func testTransactionsShouldNotBeCachedWhenObserving() { + let updateTransactionsExpectation = expectation( + description: "transactionsUpdated callback should be called with one transaction.") + let removeTransactionsExpectation = expectation( + description: "transactionsRemoved callback should be called with one transaction.") + let updateDownloadsExpectation = expectation( + description: "downloadsUpdated callback should be called with one transaction.") + let transactionStub = SKPaymentTransactionStub() + let downloadStub = SKDownload() + let queue = PaymentQueueStub() + queue.testState = .purchased + let cacheStub = TransactionCacheStub() + let handler = FIAPaymentQueueHandler( + queue: queue, + transactionsUpdated: { transactions in + XCTAssertEqual(transactions as? [SKPaymentTransactionStub], [transactionStub]) + updateTransactionsExpectation.fulfill() + }, + transactionRemoved: { transactions in + XCTAssertEqual(transactions as? [SKPaymentTransactionStub], [transactionStub]) + removeTransactionsExpectation.fulfill() + }, + restoreTransactionFailed: nil, + restoreCompletedTransactionsFinished: nil, + shouldAddStorePayment: { _, _ in + return true + }, + updatedDownloads: { downloads in + XCTAssertEqual(downloads as NSArray, [downloadStub] as NSArray) + updateDownloadsExpectation.fulfill() + }, + transactionCache: cacheStub) + + let paymentQueueStub = SKPaymentQueueStub() + + handler.startObservingPaymentQueue() + handler.paymentQueue(paymentQueueStub, updatedTransactions: [transactionStub]) + handler.paymentQueue(paymentQueueStub, removedTransactions: [transactionStub]) + handler.paymentQueue(paymentQueueStub, updatedDownloads: [downloadStub]) + + waitForExpectations(timeout: 5) + + var transactionCacheKeyUpdatedTransactionsInvokedCount = 0 + var transactionCacheKeyUpdatedDownloadsInvokedCount = 0 + var transactionCacheKeyRemovedTransactionsInvokedCount = 0 + + cacheStub.addObjectsStub = { objects, key in + switch key { + case .updatedTransactions: + transactionCacheKeyUpdatedTransactionsInvokedCount += 1 + case .updatedDownloads: + transactionCacheKeyUpdatedDownloadsInvokedCount += 1 + case .removedTransactions: + transactionCacheKeyRemovedTransactionsInvokedCount += 1 + default: + XCTFail("Invalid transaction state was invoked.") + } + } + + XCTAssertEqual(transactionCacheKeyUpdatedTransactionsInvokedCount, 0) + XCTAssertEqual(transactionCacheKeyUpdatedDownloadsInvokedCount, 0) + XCTAssertEqual(transactionCacheKeyRemovedTransactionsInvokedCount, 0) + } +}