From 4e590d00f3c226ad0997477b5e860dcaba7c28ac Mon Sep 17 00:00:00 2001 From: Toni Rico Date: Thu, 20 Jul 2023 10:43:24 +0200 Subject: [PATCH] Create different PurchasesConfiguration that requires an appUserId parameter --- .../apitester/java/PurchasesAPI.java | 3 +- .../apitester/kotlin/PurchasesAPI.kt | 4 +- build.gradle | 1 + .../purchases/amazon/AmazonConfiguration.kt | 19 ++++ .../purchases/amazon/AmazonConfiguration.kt | 0 .../purchases/PurchasesConfiguration.kt | 105 ++++++++++++++++++ .../purchases/PurchasesConfiguration.kt | 0 .../purchases/PurchasesCommonTest.kt | 37 ------ .../purchases/PurchasesFactoryTest.kt | 11 +- .../purchases/PurchasesConfigurationTest.kt | 83 ++++++++++++++ .../com/revenuecat/purchases/PurchasesTest.kt | 57 ++++++++++ .../purchases/PurchasesConfigurationTest.kt | 0 .../com/revenuecat/purchases/PurchasesTest.kt | 39 +++++++ 13 files changed, 314 insertions(+), 45 deletions(-) create mode 100644 feature/amazon/src/customEntitlementComputationDebug/kotlin/com/revenuecat/purchases/amazon/AmazonConfiguration.kt rename feature/amazon/src/{main/java => defaults/kotlin}/com/revenuecat/purchases/amazon/AmazonConfiguration.kt (100%) create mode 100644 purchases/src/customEntitlementComputation/kotlin/com/revenuecat/purchases/PurchasesConfiguration.kt rename purchases/src/{main => defaults}/kotlin/com/revenuecat/purchases/PurchasesConfiguration.kt (100%) create mode 100644 purchases/src/testCustomEntitlementComputation/kotlin/com/revenuecat/purchases/PurchasesConfigurationTest.kt create mode 100644 purchases/src/testCustomEntitlementComputation/kotlin/com/revenuecat/purchases/PurchasesTest.kt rename purchases/src/{test/java => testDefaults/kotlin}/com/revenuecat/purchases/PurchasesConfigurationTest.kt (100%) diff --git a/api-tester/src/customEntitlementComputation/kotlin/com/revenuecat/apitester/java/PurchasesAPI.java b/api-tester/src/customEntitlementComputation/kotlin/com/revenuecat/apitester/java/PurchasesAPI.java index cd126866b..963f49efd 100644 --- a/api-tester/src/customEntitlementComputation/kotlin/com/revenuecat/apitester/java/PurchasesAPI.java +++ b/api-tester/src/customEntitlementComputation/kotlin/com/revenuecat/apitester/java/PurchasesAPI.java @@ -158,8 +158,7 @@ static void checkConfiguration(final Context context, final boolean configured = Purchases.isConfigured(); - PurchasesConfiguration build = new PurchasesConfiguration.Builder(context, "") - .appUserID("") + PurchasesConfiguration build = new PurchasesConfiguration.Builder(context, "", "") .observerMode(true) .observerMode(false) .service(executorService) diff --git a/api-tester/src/customEntitlementComputation/kotlin/com/revenuecat/apitester/kotlin/PurchasesAPI.kt b/api-tester/src/customEntitlementComputation/kotlin/com/revenuecat/apitester/kotlin/PurchasesAPI.kt index 6c29c469d..42f30577a 100644 --- a/api-tester/src/customEntitlementComputation/kotlin/com/revenuecat/apitester/kotlin/PurchasesAPI.kt +++ b/api-tester/src/customEntitlementComputation/kotlin/com/revenuecat/apitester/kotlin/PurchasesAPI.kt @@ -173,14 +173,12 @@ private class PurchasesAPI { val features: List = ArrayList() val configured: Boolean = Purchases.isConfigured - val build = PurchasesConfiguration.Builder(context, apiKey = "") - .appUserID("") + val build = PurchasesConfiguration.Builder(context, apiKey = "", appUserID = "") .observerMode(true) .observerMode(false) .service(executorService) .diagnosticsEnabled(true) .entitlementVerificationMode(EntitlementVerificationMode.INFORMATIONAL) - .informationalVerificationModeAndDiagnosticsEnabled(true) .build() Purchases.configure(build) diff --git a/build.gradle b/build.gradle index f2022828c..5899539d4 100644 --- a/build.gradle +++ b/build.gradle @@ -44,6 +44,7 @@ task detektAll(type: io.gitlab.arturbosch.detekt.Detekt) { exclude("**/build/**") exclude("**/test/**/*.kt") exclude("**/testDefaults/**/*.kt") + exclude("**/testCustomEntitlementComputation/**/*.kt") config.setFrom(files("$rootDir/config/detekt/detekt.yml")) baseline.set(file("$rootDir/config/detekt/detekt-baseline.xml")) reports { diff --git a/feature/amazon/src/customEntitlementComputationDebug/kotlin/com/revenuecat/purchases/amazon/AmazonConfiguration.kt b/feature/amazon/src/customEntitlementComputationDebug/kotlin/com/revenuecat/purchases/amazon/AmazonConfiguration.kt new file mode 100644 index 000000000..cbb65eeaf --- /dev/null +++ b/feature/amazon/src/customEntitlementComputationDebug/kotlin/com/revenuecat/purchases/amazon/AmazonConfiguration.kt @@ -0,0 +1,19 @@ +package com.revenuecat.purchases.amazon + +import android.content.Context +import com.revenuecat.purchases.PurchasesConfiguration +import com.revenuecat.purchases.Store + +class AmazonConfiguration(builder: Builder) : PurchasesConfiguration(builder) { + + class Builder( + context: Context, + apiKey: String, + appUserId: String, + ) : PurchasesConfiguration.Builder(context, apiKey, appUserId) { + + init { + this.store(Store.AMAZON) + } + } +} diff --git a/feature/amazon/src/main/java/com/revenuecat/purchases/amazon/AmazonConfiguration.kt b/feature/amazon/src/defaults/kotlin/com/revenuecat/purchases/amazon/AmazonConfiguration.kt similarity index 100% rename from feature/amazon/src/main/java/com/revenuecat/purchases/amazon/AmazonConfiguration.kt rename to feature/amazon/src/defaults/kotlin/com/revenuecat/purchases/amazon/AmazonConfiguration.kt diff --git a/purchases/src/customEntitlementComputation/kotlin/com/revenuecat/purchases/PurchasesConfiguration.kt b/purchases/src/customEntitlementComputation/kotlin/com/revenuecat/purchases/PurchasesConfiguration.kt new file mode 100644 index 000000000..1ac0a6d6f --- /dev/null +++ b/purchases/src/customEntitlementComputation/kotlin/com/revenuecat/purchases/PurchasesConfiguration.kt @@ -0,0 +1,105 @@ +package com.revenuecat.purchases + +import android.content.Context +import java.util.concurrent.ExecutorService + +open class PurchasesConfiguration(builder: Builder) { + + val context: Context + val apiKey: String + val appUserID: String + val observerMode: Boolean + val service: ExecutorService? + val store: Store + val diagnosticsEnabled: Boolean + val dangerousSettings: DangerousSettings + val verificationMode: EntitlementVerificationMode + + init { + this.context = builder.context + this.apiKey = builder.apiKey + this.appUserID = builder.appUserID + this.observerMode = builder.observerMode + this.service = builder.service + this.store = builder.store + this.diagnosticsEnabled = builder.diagnosticsEnabled + this.verificationMode = builder.verificationMode + this.dangerousSettings = builder.dangerousSettings + } + + open class Builder( + @get:JvmSynthetic internal val context: Context, + @get:JvmSynthetic internal val apiKey: String, + @get:JvmSynthetic internal val appUserID: String, + ) { + @set:JvmSynthetic @get:JvmSynthetic + internal var observerMode: Boolean = false + + @set:JvmSynthetic @get:JvmSynthetic + internal var service: ExecutorService? = null + + @set:JvmSynthetic @get:JvmSynthetic + internal var store: Store = Store.PLAY_STORE + + @set:JvmSynthetic @get:JvmSynthetic + internal var diagnosticsEnabled: Boolean = false + + @set:JvmSynthetic @get:JvmSynthetic + internal var verificationMode: EntitlementVerificationMode = EntitlementVerificationMode.default + + // TODO Default to custom entitlement computation mode + @set:JvmSynthetic @get:JvmSynthetic + internal var dangerousSettings: DangerousSettings = DangerousSettings() + + fun observerMode(observerMode: Boolean) = apply { + this.observerMode = observerMode + } + + fun service(service: ExecutorService) = apply { + this.service = service + } + + fun store(store: Store) = apply { + this.store = store + } + + /** + * Enabling diagnostics will send some performance and debugging information from the SDK to our servers. + * Examples of this information include response times, cache hits or error codes. + * This information will be anonymized so it can't be traced back to the end-user. + * The default value is false. + */ + fun diagnosticsEnabled(diagnosticsEnabled: Boolean) = apply { + this.diagnosticsEnabled = diagnosticsEnabled + } + + /** + * Sets the [EntitlementVerificationMode] to perform signature verification of requests to the + * RevenueCat backend. + * + * When changing from [EntitlementVerificationMode.DISABLED] to other modes, the SDK will clear the + * CustomerInfo cache. + * This means users will need to connect to the internet to get their entitlements back. + * + * The result of the verification can be obtained from [EntitlementInfos.verification] or + * [EntitlementInfo.verification]. + * + * Default mode is disabled. Please see https://rev.cat/trusted-entitlements for more info. + */ + fun entitlementVerificationMode(verificationMode: EntitlementVerificationMode) = apply { + this.verificationMode = verificationMode + } + + /** + * Only use a Dangerous Setting if suggested by RevenueCat support team. + */ + fun dangerousSettings(dangerousSettings: DangerousSettings) = apply { + // TODO Set custom entitlement computation mode when passing in dangerous settings + this.dangerousSettings = dangerousSettings + } + + open fun build(): PurchasesConfiguration { + return PurchasesConfiguration(this) + } + } +} diff --git a/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesConfiguration.kt b/purchases/src/defaults/kotlin/com/revenuecat/purchases/PurchasesConfiguration.kt similarity index 100% rename from purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesConfiguration.kt rename to purchases/src/defaults/kotlin/com/revenuecat/purchases/PurchasesConfiguration.kt diff --git a/purchases/src/test/java/com/revenuecat/purchases/PurchasesCommonTest.kt b/purchases/src/test/java/com/revenuecat/purchases/PurchasesCommonTest.kt index 6f5c83da2..d730f12e3 100644 --- a/purchases/src/test/java/com/revenuecat/purchases/PurchasesCommonTest.kt +++ b/purchases/src/test/java/com/revenuecat/purchases/PurchasesCommonTest.kt @@ -145,43 +145,6 @@ internal class PurchasesCommonTest: BasePurchasesTest() { } } - @Test - fun `Setting platform info sets it in the AppConfig when configuring the SDK`() { - val expected = PlatformInfo("flavor", "version") - Purchases.platformInfo = expected - Purchases.configure(PurchasesConfiguration.Builder(mockContext, "api").build()) - assertThat(Purchases.sharedInstance.purchasesOrchestrator.appConfig.platformInfo).isEqualTo(expected) - } - - @Test - fun `Setting proxy URL info sets it in the HttpClient when configuring the SDK`() { - val expected = URL("https://a-proxy.com") - Purchases.proxyURL = expected - Purchases.configure(PurchasesConfiguration.Builder(mockContext, "api").build()) - assertThat(Purchases.sharedInstance.purchasesOrchestrator.appConfig.baseURL).isEqualTo(expected) - } - - @Test - fun `Setting observer mode on sets finish transactions to false`() { - val builder = PurchasesConfiguration.Builder(mockContext, "api").observerMode(true) - Purchases.configure(builder.build()) - assertThat(Purchases.sharedInstance.purchasesOrchestrator.appConfig.finishTransactions).isFalse() - } - - @Test - fun `Setting observer mode off sets finish transactions to true`() { - val builder = PurchasesConfiguration.Builder(mockContext, "api").observerMode(false) - Purchases.configure(builder.build()) - assertThat(Purchases.sharedInstance.purchasesOrchestrator.appConfig.finishTransactions).isTrue() - } - - @Test - fun `Setting store in the configuration sets it on the Purchases instance`() { - val builder = PurchasesConfiguration.Builder(mockContext, "api").store(Store.PLAY_STORE) - Purchases.configure(builder.build()) - assertThat(Purchases.sharedInstance.store).isEqualTo(Store.PLAY_STORE) - } - // endregion // region get products diff --git a/purchases/src/test/java/com/revenuecat/purchases/PurchasesFactoryTest.kt b/purchases/src/test/java/com/revenuecat/purchases/PurchasesFactoryTest.kt index 6beb2e2d0..98d4523c7 100644 --- a/purchases/src/test/java/com/revenuecat/purchases/PurchasesFactoryTest.kt +++ b/purchases/src/test/java/com/revenuecat/purchases/PurchasesFactoryTest.kt @@ -46,7 +46,7 @@ class PurchasesFactoryTest { @Test fun `creating purchase checks api key is not empty`() { - val configuration = createConfiguration(apiKey = "") + val configuration = createConfiguration(testApiKey = "") every { contextMock.checkCallingOrSelfPermission(Manifest.permission.INTERNET) } returns PackageManager.PERMISSION_GRANTED @@ -84,7 +84,12 @@ class PurchasesFactoryTest { verify(exactly = 1) { apiKeyValidatorMock.validateAndLog("fakeApiKey", Store.PLAY_STORE) } } - private fun createConfiguration(apiKey: String = "fakeApiKey"): PurchasesConfiguration { - return PurchasesConfiguration.Builder(contextMock, apiKey).build() + private fun createConfiguration(testApiKey: String = "fakeApiKey"): PurchasesConfiguration { + return mockk().apply { + every { context } returns contextMock + every { apiKey } returns testApiKey + every { appUserID } returns "appUserID" + every { store } returns Store.PLAY_STORE + } } } diff --git a/purchases/src/testCustomEntitlementComputation/kotlin/com/revenuecat/purchases/PurchasesConfigurationTest.kt b/purchases/src/testCustomEntitlementComputation/kotlin/com/revenuecat/purchases/PurchasesConfigurationTest.kt new file mode 100644 index 000000000..6c0e48623 --- /dev/null +++ b/purchases/src/testCustomEntitlementComputation/kotlin/com/revenuecat/purchases/PurchasesConfigurationTest.kt @@ -0,0 +1,83 @@ +package com.revenuecat.purchases + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.mockk.mockk +import org.assertj.core.api.Assertions.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.util.concurrent.ExecutorService + +@RunWith(AndroidJUnit4::class) +class PurchasesConfigurationTest { + + private val apiKey = "test-api-key" + private val appUserId = "test-app-user-id" + + private lateinit var context: Context + + private lateinit var builder: PurchasesConfiguration.Builder + + @Before + fun setup() { + context = mockk() + + builder = PurchasesConfiguration.Builder(context, apiKey, appUserId) + } + + @Test + fun `PurchasesConfiguration has expected default parameters`() { + val purchasesConfiguration = builder.build() + assertThat(purchasesConfiguration.apiKey).isEqualTo(apiKey) + assertThat(purchasesConfiguration.context).isEqualTo(context) + assertThat(purchasesConfiguration.appUserID).isEqualTo(appUserId) + assertThat(purchasesConfiguration.observerMode).isFalse + assertThat(purchasesConfiguration.service).isNull() + assertThat(purchasesConfiguration.store).isEqualTo(Store.PLAY_STORE) + assertThat(purchasesConfiguration.diagnosticsEnabled).isFalse + assertThat(purchasesConfiguration.verificationMode).isEqualTo(EntitlementVerificationMode.DISABLED) + assertThat(purchasesConfiguration.dangerousSettings).isEqualTo(DangerousSettings(autoSyncPurchases = true)) + } + + @Test + fun `PurchasesConfiguration sets observerMode correctly`() { + val purchasesConfiguration = builder.observerMode(true).build() + assertThat(purchasesConfiguration.observerMode).isTrue + } + + @Test + fun `PurchasesConfiguration sets service correctly`() { + val serviceMock: ExecutorService = mockk() + val purchasesConfiguration = builder.service(serviceMock).build() + assertThat(purchasesConfiguration.service).isEqualTo(serviceMock) + } + + @Test + fun `PurchasesConfiguration sets store correctly`() { + val purchasesConfiguration = builder.store(Store.AMAZON).build() + assertThat(purchasesConfiguration.store).isEqualTo(Store.AMAZON) + } + + @Test + fun `PurchasesConfiguration sets diagnosticsEnabled correctly`() { + val purchasesConfiguration = builder.diagnosticsEnabled(true).build() + assertThat(purchasesConfiguration.diagnosticsEnabled).isTrue + } + + @Test + fun `PurchasesConfiguration sets informational mode correctly`() { + val purchasesConfiguration = builder.entitlementVerificationMode( + EntitlementVerificationMode.INFORMATIONAL, + ).build() + assertThat(purchasesConfiguration.verificationMode).isEqualTo(EntitlementVerificationMode.INFORMATIONAL) + } + + @Test + fun `PurchasesConfiguration sets dangerous settings correctly`() { + // TODO: Add test for custom entitlement computation mode + val dangerousSettings = DangerousSettings(autoSyncPurchases = false) + val purchasesConfiguration = builder.dangerousSettings(dangerousSettings).build() + assertThat(purchasesConfiguration.dangerousSettings).isEqualTo(dangerousSettings) + } +} diff --git a/purchases/src/testCustomEntitlementComputation/kotlin/com/revenuecat/purchases/PurchasesTest.kt b/purchases/src/testCustomEntitlementComputation/kotlin/com/revenuecat/purchases/PurchasesTest.kt new file mode 100644 index 000000000..501711e7b --- /dev/null +++ b/purchases/src/testCustomEntitlementComputation/kotlin/com/revenuecat/purchases/PurchasesTest.kt @@ -0,0 +1,57 @@ +// Purchases +// +// Copyright © 2019 RevenueCat, Inc. All rights reserved. +// + +package com.revenuecat.purchases + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.revenuecat.purchases.common.PlatformInfo +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import java.net.URL + +@RunWith(AndroidJUnit4::class) +@Config(manifest = Config.NONE) +@Suppress("DEPRECATION") +internal class PurchasesTest : BasePurchasesTest() { + + @Test + fun `Setting platform info sets it in the AppConfig when configuring the SDK`() { + val expected = PlatformInfo("flavor", "version") + Purchases.platformInfo = expected + Purchases.configure(PurchasesConfiguration.Builder(mockContext, "api", "appUserId").build()) + assertThat(Purchases.sharedInstance.purchasesOrchestrator.appConfig.platformInfo).isEqualTo(expected) + } + + @Test + fun `Setting proxy URL info sets it in the HttpClient when configuring the SDK`() { + val expected = URL("https://a-proxy.com") + Purchases.proxyURL = expected + Purchases.configure(PurchasesConfiguration.Builder(mockContext, "api", "appUserId").build()) + assertThat(Purchases.sharedInstance.purchasesOrchestrator.appConfig.baseURL).isEqualTo(expected) + } + + @Test + fun `Setting observer mode on sets finish transactions to false`() { + val builder = PurchasesConfiguration.Builder(mockContext, "api", "appUserId").observerMode(true) + Purchases.configure(builder.build()) + assertThat(Purchases.sharedInstance.purchasesOrchestrator.appConfig.finishTransactions).isFalse() + } + + @Test + fun `Setting observer mode off sets finish transactions to true`() { + val builder = PurchasesConfiguration.Builder(mockContext, "api", "appUserId").observerMode(false) + Purchases.configure(builder.build()) + assertThat(Purchases.sharedInstance.purchasesOrchestrator.appConfig.finishTransactions).isTrue() + } + + @Test + fun `Setting store in the configuration sets it on the Purchases instance`() { + val builder = PurchasesConfiguration.Builder(mockContext, "api", "appUserId").store(Store.PLAY_STORE) + Purchases.configure(builder.build()) + assertThat(Purchases.sharedInstance.store).isEqualTo(Store.PLAY_STORE) + } +} diff --git a/purchases/src/test/java/com/revenuecat/purchases/PurchasesConfigurationTest.kt b/purchases/src/testDefaults/kotlin/com/revenuecat/purchases/PurchasesConfigurationTest.kt similarity index 100% rename from purchases/src/test/java/com/revenuecat/purchases/PurchasesConfigurationTest.kt rename to purchases/src/testDefaults/kotlin/com/revenuecat/purchases/PurchasesConfigurationTest.kt diff --git a/purchases/src/testDefaults/kotlin/com/revenuecat/purchases/PurchasesTest.kt b/purchases/src/testDefaults/kotlin/com/revenuecat/purchases/PurchasesTest.kt index d2a0d32c1..aadab40ae 100644 --- a/purchases/src/testDefaults/kotlin/com/revenuecat/purchases/PurchasesTest.kt +++ b/purchases/src/testDefaults/kotlin/com/revenuecat/purchases/PurchasesTest.kt @@ -9,6 +9,7 @@ import android.app.Activity import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.billingclient.api.BillingFlowParams.ProrationMode import com.android.billingclient.api.Purchase +import com.revenuecat.purchases.common.PlatformInfo import com.revenuecat.purchases.common.ReceiptInfo import com.revenuecat.purchases.common.ReplaceProductInfo import com.revenuecat.purchases.google.toInAppStoreProduct @@ -35,6 +36,7 @@ import org.junit.Assert.fail import org.junit.Test import org.junit.runner.RunWith import org.robolectric.annotation.Config +import java.net.URL import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlin.random.Random @@ -701,6 +703,43 @@ internal class PurchasesTest : BasePurchasesTest() { // endregion + @Test + fun `Setting platform info sets it in the AppConfig when configuring the SDK`() { + val expected = PlatformInfo("flavor", "version") + Purchases.platformInfo = expected + Purchases.configure(PurchasesConfiguration.Builder(mockContext, "api").build()) + assertThat(Purchases.sharedInstance.purchasesOrchestrator.appConfig.platformInfo).isEqualTo(expected) + } + + @Test + fun `Setting proxy URL info sets it in the HttpClient when configuring the SDK`() { + val expected = URL("https://a-proxy.com") + Purchases.proxyURL = expected + Purchases.configure(PurchasesConfiguration.Builder(mockContext, "api").build()) + assertThat(Purchases.sharedInstance.purchasesOrchestrator.appConfig.baseURL).isEqualTo(expected) + } + + @Test + fun `Setting observer mode on sets finish transactions to false`() { + val builder = PurchasesConfiguration.Builder(mockContext, "api").observerMode(true) + Purchases.configure(builder.build()) + assertThat(Purchases.sharedInstance.purchasesOrchestrator.appConfig.finishTransactions).isFalse() + } + + @Test + fun `Setting observer mode off sets finish transactions to true`() { + val builder = PurchasesConfiguration.Builder(mockContext, "api").observerMode(false) + Purchases.configure(builder.build()) + assertThat(Purchases.sharedInstance.purchasesOrchestrator.appConfig.finishTransactions).isTrue() + } + + @Test + fun `Setting store in the configuration sets it on the Purchases instance`() { + val builder = PurchasesConfiguration.Builder(mockContext, "api").store(Store.PLAY_STORE) + Purchases.configure(builder.build()) + assertThat(Purchases.sharedInstance.store).isEqualTo(Store.PLAY_STORE) + } + // region Private Methods private fun mockSynchronizeSubscriberAttributesForAllUsers() { every {