Improve some in-app tests (#484)

A few things:

- One feature eligibility test was incorrect, namely
`test_givenFreeVersion_thenIsNotEligibleForAnyFeature`
- Eligibility tests of feature subsets must also assert ineligibility of
excluded features
- App type was not covered

Also add and test a new .fullVersionPlusTV app type including the
.appleTV feature.
This commit is contained in:
Davide De Rosa 2024-01-20 09:50:23 +01:00 committed by GitHub
parent 7b30d48ea4
commit ef0ac50c62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 150 additions and 97 deletions

View File

@ -34,6 +34,8 @@ public enum AppType: Int {
case fullVersion = 2 case fullVersion = 2
case fullVersionPlusTV = 3
public var isRestricted: Bool { public var isRestricted: Bool {
switch self { switch self {
case .undefined, .beta: case .undefined, .beta:

View File

@ -59,11 +59,11 @@ public final class ProductManager: NSObject, ObservableObject {
public init(inApp: any LocalInApp, public init(inApp: any LocalInApp,
receiptReader: ReceiptReader, receiptReader: ReceiptReader,
overriddenAppType: AppType? = nil, overriddenAppType: AppType? = nil,
buildProducts: BuildProducts) { buildProducts: BuildProducts? = nil) {
self.overriddenAppType = overriddenAppType self.overriddenAppType = overriddenAppType
self.receiptReader = receiptReader self.receiptReader = receiptReader
self.buildProducts = buildProducts self.buildProducts = buildProducts ?? BuildProducts { _ in [] }
appType = .undefined appType = overriddenAppType ?? .undefined
products = [] products = []
self.inApp = inApp self.inApp = inApp
@ -224,11 +224,17 @@ extension ProductManager {
} }
func isIncludedInFullVersion(_ feature: LocalProduct) -> Bool { func isIncludedInFullVersion(_ feature: LocalProduct) -> Bool {
!feature.isLegacyPlatformVersion && feature != .appleTV switch appType {
case .fullVersionPlusTV:
return !feature.isLegacyPlatformVersion
default:
return !feature.isLegacyPlatformVersion && feature != .appleTV
}
} }
public func isFullVersion() -> Bool { public func isFullVersion() -> Bool {
if appType == .fullVersion { if appType == .fullVersion || appType == .fullVersionPlusTV {
pp_log.verbose("Eligibility: appType = .fullVersion") pp_log.verbose("Eligibility: appType = .fullVersion")
return true return true
} }

View File

@ -31,15 +31,19 @@ import XCTest
final class ProductManagerTests: XCTestCase { final class ProductManagerTests: XCTestCase {
private let inApp = MockInApp() private let inApp = MockInApp()
private let noBuildProducts = BuildProducts { _ in [] } private let olderBuildNumber = 500
private let defaultBuildNumber = 1000
private let newerBuildNumber = 1500
// MARK: Build products // MARK: Build products
func test_givenBuildProducts_whenOlder_thenFullVersion() { func test_givenBuildProducts_whenOlder_thenFullVersion() {
let reader = MockReceiptReader() let reader = MockReceiptReader()
reader.setReceipt(withBuild: 500, products: []) reader.setReceipt(withBuild: olderBuildNumber, products: [])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: BuildProducts { build in let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: BuildProducts { build in
if build <= 1000 { if build <= self.defaultBuildNumber {
return [.fullVersion] return [.fullVersion]
} }
return [] return []
@ -49,9 +53,9 @@ final class ProductManagerTests: XCTestCase {
func test_givenBuildProducts_whenNewer_thenFreeVersion() { func test_givenBuildProducts_whenNewer_thenFreeVersion() {
let reader = MockReceiptReader() let reader = MockReceiptReader()
reader.setReceipt(withBuild: 1500, products: []) reader.setReceipt(withBuild: newerBuildNumber, products: [])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: BuildProducts { build in let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: BuildProducts { build in
if build <= 1000 { if build <= self.defaultBuildNumber {
return [.fullVersion] return [.fullVersion]
} }
return [] return []
@ -61,35 +65,22 @@ final class ProductManagerTests: XCTestCase {
// MARK: Eligibility // MARK: Eligibility
func test_givenPurchase_whenReload_thenCredited() { func test_givenPurchasedFeature_whenReloadReceipt_thenIsEligible() {
let reader = MockReceiptReader() let reader = MockReceiptReader()
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts) let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion)) XCTAssertFalse(sut.isEligible(forFeature: .fullVersion))
reader.setReceipt(withBuild: 1500, products: [.fullVersion]) reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion])
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion)) XCTAssertFalse(sut.isEligible(forFeature: .fullVersion))
sut.reloadReceipt() sut.reloadReceipt()
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion)) XCTAssertTrue(sut.isEligible(forFeature: .fullVersion))
} }
func test_givenPurchase_whenCancelled_thenRevoke() { func test_givenPurchasedFeatures_thenIsOnlyEligibleForFeatures() {
let reader = MockReceiptReader() let reader = MockReceiptReader()
reader.setReceipt(withBuild: 1500, products: [.fullVersion]) reader.setReceipt(withBuild: defaultBuildNumber, products: [.siriShortcuts, .networkSettings])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts) let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion))
reader.setReceipt(withBuild: 1500, products: [.fullVersion], cancelledProducts: [.fullVersion])
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion))
sut.reloadReceipt()
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion))
}
func test_givenFeature_thenIsOnlyEligibleForFeature() {
let reader = MockReceiptReader()
reader.setReceipt(withBuild: 1500, products: [.siriShortcuts, .networkSettings])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
XCTAssertTrue(sut.isEligible(forFeature: .siriShortcuts)) XCTAssertTrue(sut.isEligible(forFeature: .siriShortcuts))
XCTAssertTrue(sut.isEligible(forFeature: .networkSettings)) XCTAssertTrue(sut.isEligible(forFeature: .networkSettings))
@ -98,17 +89,78 @@ final class ProductManagerTests: XCTestCase {
XCTAssertFalse(sut.isFullVersion()) XCTAssertFalse(sut.isFullVersion())
} }
func test_givenPurchasedAndCancelledFeature_thenIsNotEligible() {
let reader = MockReceiptReader()
reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion], cancelledProducts: [.fullVersion])
let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion))
}
func test_givenFreeVersion_thenIsNotEligibleForAnyFeature() {
let reader = MockReceiptReader()
reader.setReceipt(withBuild: defaultBuildNumber, products: [])
let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertFalse(sut.isFullVersion())
XCTAssertTrue(LocalProduct
.allFeatures
.allSatisfy { !sut.isEligible(forFeature: $0) }
)
}
func test_givenFreeVersion_thenIsNotEligibleForAppleTV() {
let reader = MockReceiptReader()
reader.setReceipt(withBuild: defaultBuildNumber, products: [])
let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertFalse(sut.isEligible(forFeature: .appleTV))
}
func test_givenFullVersion_thenIsEligibleForAnyFeatureExceptAppleTV() {
let reader = MockReceiptReader()
reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion])
let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertTrue(sut.isFullVersion())
LocalProduct
.allFeatures
.forEach {
guard $0 != .appleTV && !$0.isLegacyPlatformVersion else {
XCTAssertFalse(sut.isEligible(forFeature: $0), $0.rawValue)
return
}
XCTAssertTrue(sut.isEligible(forFeature: $0), $0.rawValue)
}
}
func test_givenFullVersion_thenIsNotEligibleForAppleTV() {
let reader = MockReceiptReader()
reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion])
let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertFalse(sut.isEligible(forFeature: .appleTV))
}
func test_givenAppleTV_thenIsEligibleForAppleTV() {
let reader = MockReceiptReader()
reader.setReceipt(withBuild: defaultBuildNumber, products: [.appleTV])
let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertTrue(sut.isEligible(forFeature: .appleTV))
}
func test_givenPlatformVersion_thenIsFullVersionForPlatform() { func test_givenPlatformVersion_thenIsFullVersionForPlatform() {
let reader = MockReceiptReader() let reader = MockReceiptReader()
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts) let sut = ProductManager(inApp: inApp, receiptReader: reader)
#if targetEnvironment(macCatalyst) #if targetEnvironment(macCatalyst)
reader.setReceipt(withBuild: 1500, products: [.fullVersion_macOS, .networkSettings]) reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion_macOS, .networkSettings])
sut.reloadReceipt() sut.reloadReceipt()
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion_iOS)) XCTAssertFalse(sut.isEligible(forFeature: .fullVersion_iOS))
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion_macOS)) XCTAssertTrue(sut.isEligible(forFeature: .fullVersion_macOS))
#else #else
reader.setReceipt(withBuild: 1500, products: [.fullVersion_iOS, .networkSettings]) reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion_iOS, .networkSettings])
sut.reloadReceipt() sut.reloadReceipt()
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion_iOS)) XCTAssertTrue(sut.isEligible(forFeature: .fullVersion_iOS))
XCTAssertFalse(sut.isEligible(forFeature: .fullVersion_macOS)) XCTAssertFalse(sut.isEligible(forFeature: .fullVersion_macOS))
@ -119,61 +171,12 @@ final class ProductManagerTests: XCTestCase {
XCTAssertTrue(sut.isEligible(forFeature: .fullVersion)) XCTAssertTrue(sut.isEligible(forFeature: .fullVersion))
} }
func test_givenFullVersion_thenIsEligibleForAnyFeatureExceptAppleTV() {
let reader = MockReceiptReader()
reader.setReceipt(withBuild: 1500, products: [.fullVersion])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
XCTAssertTrue(sut.isFullVersion())
XCTAssertTrue(LocalProduct
.allFeatures
.filter { $0 != .appleTV && !$0.isLegacyPlatformVersion }
.allSatisfy(sut.isEligible(forFeature:))
)
}
func test_givenFreeVersion_thenIsNotEligibleForAnyFeature() {
let reader = MockReceiptReader()
reader.setReceipt(withBuild: 1500, products: [])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
XCTAssertFalse(sut.isFullVersion())
XCTAssertFalse(LocalProduct
.allFeatures
.allSatisfy(sut.isEligible(forFeature:))
)
}
func test_givenFreeVersion_thenIsNotEligibleForAppleTV() {
let reader = MockReceiptReader()
reader.setReceipt(withBuild: 1500, products: [])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
XCTAssertFalse(sut.isEligible(forFeature: .appleTV))
}
func test_givenFullVersion_thenIsNotEligibleForAppleTV() {
let reader = MockReceiptReader()
reader.setReceipt(withBuild: 1500, products: [.fullVersion])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
XCTAssertFalse(sut.isEligible(forFeature: .appleTV))
}
func test_givenAppleTV_thenIsEligibleForAppleTV() {
let reader = MockReceiptReader()
reader.setReceipt(withBuild: 1500, products: [.appleTV])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
XCTAssertTrue(sut.isEligible(forFeature: .appleTV))
}
// MARK: Purchasable // MARK: Purchasable
func test_givenNoPurchase_thenCanBuyFullAndPlatformVersion() { func test_givenNoPurchase_thenCanBuyFullAndPlatformVersion() {
let reader = MockReceiptReader() let reader = MockReceiptReader()
reader.setReceipt(withBuild: 1500, products: []) reader.setReceipt(withBuild: defaultBuildNumber, products: [])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts) let sut = ProductManager(inApp: inApp, receiptReader: reader)
#if targetEnvironment(macCatalyst) #if targetEnvironment(macCatalyst)
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion, .fullVersion_macOS]) XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion, .fullVersion_macOS])
@ -184,9 +187,9 @@ final class ProductManagerTests: XCTestCase {
func test_givenFullVersion_thenCannotPurchase() { func test_givenFullVersion_thenCannotPurchase() {
let reader = MockReceiptReader() let reader = MockReceiptReader()
reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion])
let sut = ProductManager(inApp: inApp, receiptReader: reader)
reader.setReceipt(withBuild: 1500, products: [.fullVersion])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts)
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), []) XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [])
} }
@ -194,12 +197,12 @@ final class ProductManagerTests: XCTestCase {
let reader = MockReceiptReader() let reader = MockReceiptReader()
#if targetEnvironment(macCatalyst) #if targetEnvironment(macCatalyst)
reader.setReceipt(withBuild: 1500, products: [.fullVersion_macOS]) reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion_macOS])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts) let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), []) XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [])
#else #else
reader.setReceipt(withBuild: 1500, products: [.fullVersion_iOS]) reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion_iOS])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts) let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), []) XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [])
#endif #endif
} }
@ -208,29 +211,71 @@ final class ProductManagerTests: XCTestCase {
let reader = MockReceiptReader() let reader = MockReceiptReader()
#if targetEnvironment(macCatalyst) #if targetEnvironment(macCatalyst)
reader.setReceipt(withBuild: 1500, products: [.fullVersion_iOS]) reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion_iOS])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts) let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion_macOS]) XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion_macOS])
#else #else
reader.setReceipt(withBuild: 1500, products: [.fullVersion_macOS]) reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion_macOS])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts) let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion_iOS]) XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion_iOS])
#endif #endif
} }
func test_givenAppleTV_whenDidNotPurchase_thenCanPurchase() { func test_givenAppleTV_whenDidNotPurchase_thenCanPurchase() {
let reader = MockReceiptReader() let reader = MockReceiptReader()
reader.setReceipt(withBuild: 1500, products: []) reader.setReceipt(withBuild: defaultBuildNumber, products: [])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts) let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertEqual(sut.purchasableProducts(withFeature: .appleTV), [.appleTV]) XCTAssertEqual(sut.purchasableProducts(withFeature: .appleTV), [.appleTV])
} }
func test_givenAppleTV_whenDidPurchase_thenCannotPurchase() { func test_givenAppleTV_whenDidPurchase_thenCannotPurchase() {
let reader = MockReceiptReader() let reader = MockReceiptReader()
reader.setReceipt(withBuild: 1500, products: [.appleTV]) reader.setReceipt(withBuild: defaultBuildNumber, products: [.appleTV])
let sut = ProductManager(inApp: inApp, receiptReader: reader, buildProducts: noBuildProducts) let sut = ProductManager(inApp: inApp, receiptReader: reader)
XCTAssertEqual(sut.purchasableProducts(withFeature: .appleTV), []) XCTAssertEqual(sut.purchasableProducts(withFeature: .appleTV), [])
} }
// MARK: App type
func test_givenBetaApp_thenIsNotEligibleForAnyFeature() {
let reader = MockReceiptReader()
let sut = ProductManager(inApp: inApp, receiptReader: reader, overriddenAppType: .beta)
XCTAssertTrue(LocalProduct
.allFeatures
.allSatisfy { !sut.isEligible(forFeature: $0) }
)
}
func test_givenFullApp_thenIsEligibleForAnyFeatureExceptAppleTV() {
let reader = MockReceiptReader()
let sut = ProductManager(inApp: inApp, receiptReader: reader, overriddenAppType: .fullVersion)
LocalProduct
.allFeatures
.forEach {
guard !$0.isLegacyPlatformVersion, $0 != .appleTV else {
XCTAssertFalse(sut.isEligible(forFeature: $0), $0.rawValue)
return
}
XCTAssertTrue(sut.isEligible(forFeature: $0), $0.rawValue)
}
}
func test_givenFullPlusTVApp_thenIsEligibleForAnyFeature() {
let reader = MockReceiptReader()
let sut = ProductManager(inApp: inApp, receiptReader: reader, overriddenAppType: .fullVersionPlusTV)
LocalProduct
.allFeatures
.forEach {
guard !$0.isLegacyPlatformVersion else {
XCTAssertFalse(sut.isEligible(forFeature: $0), $0.rawValue)
return
}
XCTAssertTrue(sut.isEligible(forFeature: $0), $0.rawValue)
}
}
} }