Skip to content

Commit

Permalink
Implement tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nik3212 committed Jan 17, 2024
1 parent faa4dc7 commit a8f4ca3
Show file tree
Hide file tree
Showing 12 changed files with 432 additions and 22 deletions.
2 changes: 2 additions & 0 deletions Sources/Flare/Classes/Models/IAPError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ extension IAPError: Equatable {
return lhs == rhs
case (.unknown, .unknown):
return true
case let (.failedToDecodeSignature(lhs), .failedToDecodeSignature(rhs)):
return lhs == rhs
default:
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ struct SK1StoreProductDiscount: IStoreProductDiscount {
self.productDiscount = productDiscount

offerIdentifier = productDiscount.identifier
currencyCode = ""
currencyCode = productDiscount.priceLocale.currencyCodeID
price = productDiscount.price as Decimal
self.paymentMode = paymentMode
self.subscriptionPeriod = subscriptionPeriod
Expand Down
13 changes: 13 additions & 0 deletions Sources/Flare/Classes/Models/SubscriptionPeriod.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ public final class SubscriptionPeriod: NSObject, Sendable {
self.value = value
self.unit = unit
}

override public func isEqual(_ object: Any?) -> Bool {
guard let other = object as? SubscriptionPeriod else { return false }
return value == other.value && unit == other.unit
}

Check warning on line 48 in Sources/Flare/Classes/Models/SubscriptionPeriod.swift

View check run for this annotation

Codecov / codecov/patch

Sources/Flare/Classes/Models/SubscriptionPeriod.swift#L45-L48

Added lines #L45 - L48 were not covered by tests

override public var hash: Int {
var hasher = Hasher()
hasher.combine(value)
hasher.combine(unit)

return hasher.finalize()
}

Check warning on line 56 in Sources/Flare/Classes/Models/SubscriptionPeriod.swift

View check run for this annotation

Codecov / codecov/patch

Sources/Flare/Classes/Models/SubscriptionPeriod.swift#L50-L56

Added lines #L50 - L56 were not covered by tests
}

// MARK: - Helpers
Expand Down
56 changes: 56 additions & 0 deletions Tests/FlareTests/UnitTests/Models/PromotionalOfferTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// Flare
// Copyright © 2024 Space Code. All rights reserved.
//

@testable import Flare
import StoreKit
import XCTest

// MARK: - PromotionalOfferTests

final class PromotionalOfferTests: XCTestCase {
@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
func test_purchaseOptions() throws {
let option = try PromotionalOffer.SignedData.randomOffer.promotionalOffer
let expected: Product.PurchaseOption = .promotionalOffer(
offerID: PromotionalOffer.SignedData.randomOffer.identifier,
keyID: PromotionalOffer.SignedData.randomOffer.keyIdentifier,
nonce: PromotionalOffer.SignedData.randomOffer.nonce,
signature: Data(),
timestamp: PromotionalOffer.SignedData.randomOffer.timestamp
)

XCTAssertEqual(expected, option)
}

@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
func test_purchaseOptionWithInvalidSignatureThrows() throws {
do {
_ = try PromotionalOffer.SignedData.invalidOffer.promotionalOffer
} catch {
let error = try XCTUnwrap(error as? IAPError)
XCTAssertEqual(error, IAPError.failedToDecodeSignature(signature: PromotionalOffer.SignedData.invalidOffer.signature))
}
}
}

// MARK: - Constants

private extension PromotionalOffer.SignedData {
static let randomOffer: PromotionalOffer.SignedData = .init(
identifier: "identifier \(Int.random(in: 0 ..< 1000))",
keyIdentifier: "key identifier \(Int.random(in: 0 ..< 1000))",
nonce: .init(),
signature: "signature \(Int.random(in: 0 ..< 1000))".asData.base64EncodedString(),
timestamp: Int.random(in: 0 ..< 1000)
)

static let invalidOffer: PromotionalOffer.SignedData = .init(
identifier: "identifier \(Int.random(in: 0 ..< 1000))",
keyIdentifier: "key identifier \(Int.random(in: 0 ..< 1000))",
nonce: .init(),
signature: "signature \(Int.random(in: 0 ..< 1000))",
timestamp: Int.random(in: 0 ..< 1000)
)
}
8 changes: 8 additions & 0 deletions Tests/FlareTests/UnitTests/Models/StoreProductTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//
// Flare
// Copyright © 2024 Space Code. All rights reserved.
//

import XCTest

final class StoreProductTests: XCTestCase {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// Flare
// Copyright © 2024 Space Code. All rights reserved.
//

import Foundation

extension String {
var asData: Data {
Data(utf8)
}
}
106 changes: 102 additions & 4 deletions Tests/IntegrationTests/Flare.storekit
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,15 @@
"codeOffers" : [

],
"displayPrice" : "0.99",
"displayPrice" : "1.99",
"familyShareable" : false,
"groupNumber" : 1,
"internalID" : "138CEE33",
"introductoryOffer" : {
"displayPrice" : "0.99",
"internalID" : "970CA16D",
"paymentMode" : "free",
"numberOfPeriods" : 1,
"paymentMode" : "payAsYouGo",
"subscriptionPeriod" : "P1M"
},
"localizations" : [
Expand All @@ -103,7 +105,7 @@
"locale" : "en_US"
}
],
"productID" : "subscription_1",
"productID" : "com.flare.monthly_1.99_week_intro",
"recurringSubscriptionPeriod" : "P1M",
"referenceName" : "Subscription with Introductory Offer",
"subscriptionGroupID" : "C3C61FEC",
Expand All @@ -128,11 +130,107 @@
"locale" : "en_US"
}
],
"productID" : "subscription_2",
"productID" : "com.flare.monthly_0.99",
"recurringSubscriptionPeriod" : "P1M",
"referenceName" : "Subscription Without Offers",
"subscriptionGroupID" : "C3C61FEC",
"type" : "RecurringSubscription"
},
{
"adHocOffers" : [
{
"internalID" : "479390B8",
"offerID" : "subscription_3_offer",
"paymentMode" : "free",
"referenceName" : "subscription_3_offer",
"subscriptionPeriod" : "P2W"
}
],
"codeOffers" : [

],
"displayPrice" : "0.99",
"familyShareable" : false,
"groupNumber" : 1,
"internalID" : "8D29D6BD",
"introductoryOffer" : null,
"localizations" : [
{
"description" : "",
"displayName" : "",
"locale" : "en_US"
}
],
"productID" : "com.flare.monthly_1.99_two_weeks_offer.free",
"recurringSubscriptionPeriod" : "P1M",
"referenceName" : "Subscription with Promotional Offer",
"subscriptionGroupID" : "C3C61FEC",
"type" : "RecurringSubscription"
},
{
"adHocOffers" : [
{
"displayPrice" : "0.99",
"internalID" : "A05B39A3",
"numberOfPeriods" : 1,
"offerID" : "com.flare.monthly_0.99.1_week_intro",
"paymentMode" : "payAsYouGo",
"referenceName" : "com.flare.monthly_0.99.1_week_intro",
"subscriptionPeriod" : "P1M"
},
{
"displayPrice" : "1.99",
"internalID" : "79BD229A",
"offerID" : "com.flare.monthly_0.99.1_week_intro",
"paymentMode" : "payUpFront",
"referenceName" : "com.flare.monthly_0.99.1_week_intro",
"subscriptionPeriod" : "P1M"
},
{
"internalID" : "C181C3BF",
"offerID" : "com.flare.monthly_0.99.1_week_intro",
"paymentMode" : "free",
"referenceName" : "com.flare.monthly_0.99.1_week_intro",
"subscriptionPeriod" : "P1W"
}
],
"codeOffers" : [
{
"displayPrice" : "0.99",
"eligibility" : [
"existing",
"expired",
"new"
],
"internalID" : "A9D00827",
"isStackable" : true,
"paymentMode" : "payUpFront",
"referenceName" : "offer",
"subscriptionPeriod" : "P1M"
}
],
"displayPrice" : "0.99",
"familyShareable" : false,
"groupNumber" : 1,
"internalID" : "7867CD16",
"introductoryOffer" : {
"displayPrice" : "0.99",
"internalID" : "0A94C45A",
"paymentMode" : "payUpFront",
"subscriptionPeriod" : "P1M"
},
"localizations" : [
{
"description" : "Subscription",
"displayName" : "Subscription",
"locale" : "en_US"
}
],
"productID" : "com.flare.monthly_0.99.1_week_intro",
"recurringSubscriptionPeriod" : "P1M",
"referenceName" : "Subscription Full",
"subscriptionGroupID" : "C3C61FEC",
"type" : "RecurringSubscription"
}
]
}
Expand Down
16 changes: 16 additions & 0 deletions Tests/IntegrationTests/Helpers/Extensions/AsyncSequence+.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Flare
// Copyright © 2024 Space Code. All rights reserved.
//

import Foundation

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.2, *)
extension AsyncSequence {
/// Returns the elements of the asynchronous sequence.
func extractValues() async rethrows -> [Element] {
try await reduce(into: []) {
$0.append($1)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ enum ProductProviderHelper {

static var subscriptions: [StoreKit.Product] {
get async throws {
try await subscriptionsWithOffers + subscriptionsWithoutOffers
try await subscriptionsWithIntroductoryOffer + subscriptionsWithoutOffers + subscriptonsWithOffers
}
}

static var subscriptionsWithOffers: [StoreKit.Product] {
static var subscriptionsWithIntroductoryOffer: [StoreKit.Product] {
get async throws {
try await StoreKit.Product.products(for: [.subscription1ID])
}
Expand All @@ -33,25 +33,24 @@ enum ProductProviderHelper {
}
}

//
// static var all: [StoreKit.Product] {
// get async throws {
// let purchases = try await self.purchases
// let subscriptions = try await self.subscriptions
//
// return purchases + subscriptions
// }
// }
static var subscriptonsWithOffers: [StoreKit.Product] {
get async throws {
try await StoreKit.Product.products(for: [.subscription3ID])
}
}
}

// MARK: - Constants

private extension String {
static let testNonConsumableID = "com.flare.test_non_consumable_purchase_1"

/// The subscription's id with introductionary offer
static let subscription1ID = "subscription_1"
/// The subscription's id with introductory offer
static let subscription1ID = "com.flare.monthly_1.99_week_intro"

/// The subscription's id without introductory offer
static let subscription2ID = "com.flare.monthly_0.99"

/// The subscription's id without introductionary offer
static let subscription2ID = "subscription_2"
/// The subscription's id with promotional offer
static let subscription3ID = "com.flare.monthly_1.99_two_weeks_offer.free"
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
//
// Flare
// Copyright © 2023 Space Code. All rights reserved.
// Copyright © 2024 Space Code. All rights reserved.
//

import Flare
import StoreKitTest
import XCTest

// MARK: - StoreSessionTestCase

@available(iOS 14.0, tvOS 14.0, macOS 11.0, watchOS 7.0, *)
class StoreSessionTestCase: XCTestCase {
// MARK: Properties
Expand All @@ -29,3 +32,33 @@ class StoreSessionTestCase: XCTestCase {
super.tearDown()
}
}

@available(iOS 14.0, tvOS 14.0, macOS 11.0, watchOS 7.0, *)
extension StoreSessionTestCase {
func expireSubscription(product: StoreProduct) {
do {
try session?.expireSubscription(productIdentifier: product.productIdentifier)
} catch {
debugPrint(error.localizedDescription)
}
}

@available(iOS 15.2, tvOS 15.2, macOS 12.1, watchOS 8.3, *)
func findTransaction(for productIdentifier: String) async throws -> Transaction {
let transactions: [Transaction] = await Transaction.currentEntitlements
.compactMap { result in
switch result {
case let .verified(transaction):
return transaction
case .unverified:
return nil
}
}
.filter { (transaction: Transaction) in
transaction.productID == productIdentifier
}
.extractValues()

return try XCTUnwrap(transactions.first)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ final class EligibilityProviderTests: StoreSessionTestCase {

func test_thatProviderReturnsEligible_whenProductHasIntroductoryOffer() async throws {
// given
let product = try await ProductProviderHelper.subscriptionsWithOffers.randomElement()!
let product = try await ProductProviderHelper.subscriptionsWithIntroductoryOffer.randomElement()!

// when
let result = try await sut.checkEligibility(products: [StoreProduct(product: product)])
Expand Down
Loading

0 comments on commit a8f4ca3

Please sign in to comment.