diff --git a/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/AppPrefsTest.kt b/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/AppPrefsTest.kt index 8c5e5c22b21..68cadd28851 100644 --- a/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/AppPrefsTest.kt +++ b/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/AppPrefsTest.kt @@ -10,6 +10,7 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.Before import org.junit.Test +@Suppress("UnitTestNamingRule") class AppPrefsTest { @Before fun setup() { @@ -531,7 +532,7 @@ class AppPrefsTest { val siteId = 123 // WHEN - AppPrefs.setPOSTabVisibilityForSite(siteId, true) + AppPrefs.setPOSTabVisibilityForSite(siteId) val result = AppPrefs.isPOSTabVisibleForSite(siteId) // THEN @@ -549,4 +550,47 @@ class AppPrefsTest { // THEN assertThat(result).isFalse } + + @Test + fun givenPOSTabVisibilitySetWhenClearedThenGetterReturnsFalse() { + // GIVEN + val siteId = 789 + AppPrefs.setPOSTabVisibilityForSite(siteId) + assertThat(AppPrefs.isPOSTabVisibleForSite(siteId)).isTrue + + // WHEN + AppPrefs.clearPOSTabVisibilityForSite(siteId) + + // THEN + assertThat(AppPrefs.isPOSTabVisibleForSite(siteId)).isFalse + } + + @Test + fun givenPOSLaunchableNotSetWhenIsPOSLaunchableForSiteThenReturnFalseByDefault() { + val siteId = 111 + + assertThat(AppPrefs.isPOSLaunchableForSite(siteId)).isFalse + } + + @Test + fun givenSetPOSLaunchableForSiteWhenIsPOSLaunchableForSiteThenReturnTrueOnlyForThatSite() { + val siteA = 222 + val siteB = 333 + + AppPrefs.setPOSLaunchableForSite(siteA) + + assertThat(AppPrefs.isPOSLaunchableForSite(siteA)).isTrue + assertThat(AppPrefs.isPOSLaunchableForSite(siteB)).isFalse + } + + @Test + fun givenSetPOSLaunchableForSiteWhenClearPOSLaunchableForSiteThenReturnFalse() { + val siteId = 444 + AppPrefs.setPOSLaunchableForSite(siteId) + assertThat(AppPrefs.isPOSLaunchableForSite(siteId)).isTrue + + AppPrefs.clearPOSLaunchableForSite(siteId) + + assertThat(AppPrefs.isPOSLaunchableForSite(siteId)).isFalse + } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/AppPrefs.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/AppPrefs.kt index c0d63c3a2ff..7230f5c253e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/AppPrefs.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/AppPrefs.kt @@ -210,6 +210,8 @@ object AppPrefs { APPLICATION_STORE_SNAPSHOT_TRACKED_FOR_SITE, POS_TAB_VISIBILITY, + + POS_LAUNCHABLE } fun init(context: Context) { @@ -1183,10 +1185,10 @@ object AppPrefs { return getInt(PrefKeyString(channel.name), 0).takeIf { it != 0 } } - fun setPOSTabVisibilityForSite(siteId: Int, visible: Boolean) { + fun setPOSTabVisibilityForSite(siteId: Int) { setBoolean( key = PrefKeyString("${UndeletablePrefKey.POS_TAB_VISIBILITY}:$siteId"), - value = visible + value = true ) } @@ -1197,6 +1199,28 @@ object AppPrefs { ) } + fun clearPOSTabVisibilityForSite(siteId: Int) { + remove(PrefKeyString("${UndeletablePrefKey.POS_TAB_VISIBILITY}:$siteId")) + } + + fun setPOSLaunchableForSite(siteId: Int) { + setBoolean( + key = PrefKeyString("${UndeletablePrefKey.POS_LAUNCHABLE}:$siteId"), + value = true + ) + } + + fun isPOSLaunchableForSite(siteId: Int): Boolean { + return getBoolean( + key = PrefKeyString("${UndeletablePrefKey.POS_LAUNCHABLE}:$siteId"), + default = false + ) + } + + fun clearPOSLaunchableForSite(siteId: Int) { + remove(PrefKeyString("${UndeletablePrefKey.POS_LAUNCHABLE}:$siteId")) + } + /** * Remove all user and site-related preferences. */ diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/WooPOSIsRemotelyEnabled.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/WooPOSIsRemotelyEnabled.kt index acc9713918e..757e55c332c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/WooPOSIsRemotelyEnabled.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/WooPOSIsRemotelyEnabled.kt @@ -3,6 +3,7 @@ package com.woocommerce.android.ui.woopos import com.google.common.reflect.TypeToken import com.google.gson.Gson import com.woocommerce.android.tools.SelectedSite +import com.woocommerce.android.ui.woopos.common.util.WooPosCouldNotDetermineValueException import com.woocommerce.android.util.WCSSRModelCachingFetcher import javax.inject.Inject @@ -12,7 +13,7 @@ class WooPOSIsRemotelyEnabled @Inject constructor( private val gson: Gson ) { - suspend operator fun invoke(forceRefresh: Boolean = false): Boolean { + suspend operator fun invoke(forceRefresh: Boolean = false): Result { if (forceRefresh) { ssrFetcher.invalidate() } @@ -25,9 +26,11 @@ class WooPOSIsRemotelyEnabled @Inject constructor( val settingsMap: Map = gson.fromJson(ssr.settings, type) val enabledFeatures = settingsMap["enabled_features"] as? List<*> - return enabledFeatures?.contains("point_of_sale") == true + return enabledFeatures?.let { + Result.success(it.contains("point_of_sale")) + } ?: Result.failure(WooPosCouldNotDetermineValueException()) } } - return false + return Result.failure(WooPosCouldNotDetermineValueException()) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/util/WooPosCouldNotDetermineValueException.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/util/WooPosCouldNotDetermineValueException.kt new file mode 100644 index 00000000000..688e1c4b142 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/util/WooPosCouldNotDetermineValueException.kt @@ -0,0 +1,3 @@ +package com.woocommerce.android.ui.woopos.common.util + +class WooPosCouldNotDetermineValueException : Exception() diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/eligibility/WooPosEligibilityViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/eligibility/WooPosEligibilityViewModel.kt index efd105f7a2e..e7a525320bc 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/eligibility/WooPosEligibilityViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/eligibility/WooPosEligibilityViewModel.kt @@ -96,7 +96,8 @@ class WooPosEligibilityViewModel @Inject constructor( resourceProvider.getString(R.string.woopos_eligibility_reason_unsupported_currency_generic) } } - WooPosLaunchability.NonLaunchabilityReason.NoSiteSelected -> + WooPosLaunchability.NonLaunchabilityReason.NoSiteSelected, + WooPosLaunchability.NonLaunchabilityReason.UnknownNoPositiveCache -> resourceProvider.getString(R.string.woopos_eligibility_reason_check_connection) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosCanBeLaunchedInTab.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosCanBeLaunchedInTab.kt index 2b2191e454f..538ca372e37 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosCanBeLaunchedInTab.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosCanBeLaunchedInTab.kt @@ -1,5 +1,6 @@ package com.woocommerce.android.ui.woopos.tab +import com.woocommerce.android.AppPrefs import com.woocommerce.android.extensions.semverCompareTo import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.woopos.WooPOSIsRemotelyEnabled @@ -8,6 +9,8 @@ import com.woocommerce.android.util.FetchActiveWCPluginVersion import com.woocommerce.android.util.GetWooCorePluginCachedVersion import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.model.settings.Settings import org.wordpress.android.fluxc.store.WooCommerceStore import javax.inject.Inject import javax.inject.Singleton @@ -19,6 +22,7 @@ import javax.inject.Singleton */ @Singleton class WooPosCanBeLaunchedInTab @Inject constructor( + private val appPrefs: AppPrefs, private val selectedSite: SelectedSite, private val getWooCoreCachedVersion: GetWooCorePluginCachedVersion, private val fetchWooCoreVersion: FetchActiveWCPluginVersion, @@ -39,51 +43,115 @@ class WooPosCanBeLaunchedInTab @Inject constructor( private suspend fun checkLaunchability(forceRefresh: Boolean = false): WooPosLaunchability { val site = selectedSite.getOrNull() ?: return WooPosLaunchability.NotLaunchable( - WooPosLaunchability.NonLaunchabilityReason.NoSiteSelected + reason = WooPosLaunchability.NonLaunchabilityReason.NoSiteSelected ) - val wooCoreVersion = if (forceRefresh) { - fetchWooCoreVersion() - } else { - getWooCoreCachedVersion() - } ?: return WooPosLaunchability.NotLaunchable( - WooPosLaunchability.NonLaunchabilityReason.WooCommercePluginNotFound - ) + val cachedPositive = appPrefs.isPOSLaunchableForSite(site.id) - if (!isWooCoreSupportsOrderAutoDraftsAndExtraPaymentsProps(wooCoreVersion)) { - return WooPosLaunchability.NotLaunchable( - WooPosLaunchability.NonLaunchabilityReason.UnsupportedWooCommerceVersion - ) + getNonLaunchabilityReasonFromVersionAndFeatureSwitch(forceRefresh, cachedPositive)?.let { + return prepareNotLaunchableStateWithCacheUpdate(site.id, it) } - if (isFeatureSwitchSupported(wooCoreVersion) && !isRemotelyEnabled(forceRefresh)) { - return WooPosLaunchability.NotLaunchable( - WooPosLaunchability.NonLaunchabilityReason.FeatureSwitchDisabled - ) + getNonLaunchabilityReasonFromSiteSettingsAndCurrency(site, forceRefresh, cachedPositive)?.let { + return prepareNotLaunchableStateWithCacheUpdate(site.id, it) } - val siteSettings = if (forceRefresh) { - wooCommerceStore.fetchSiteGeneralSettings(site).model + appPrefs.setPOSLaunchableForSite(site.id) + return WooPosLaunchability.Launchable + } + + private fun prepareNotLaunchableStateWithCacheUpdate( + siteId: Int, + reason: WooPosLaunchability.NonLaunchabilityReason + ): WooPosLaunchability.NotLaunchable { + if (reason != WooPosLaunchability.NonLaunchabilityReason.UnknownNoPositiveCache) { + appPrefs.clearPOSLaunchableForSite(siteId) + } + + return WooPosLaunchability.NotLaunchable(reason) + } + + private suspend fun getNonLaunchabilityReasonFromVersionAndFeatureSwitch( + forceRefresh: Boolean, + cachedPositive: Boolean + ): WooPosLaunchability.NonLaunchabilityReason? { + val wooCoreVersion = getWooCoreVersion(forceRefresh) + ?: return reasonIfNoPositiveCache(cachedPositive) + + return getNonLaunchabilityReasonFromWooCoreVersion(wooCoreVersion) + ?: getNonLaunchabilityReasonFromFeatureSwitch(wooCoreVersion, forceRefresh, cachedPositive) + } + + private suspend fun getNonLaunchabilityReasonFromSiteSettingsAndCurrency( + site: SiteModel, + forceRefresh: Boolean, + cachedPositive: Boolean + ): WooPosLaunchability.NonLaunchabilityReason? { + val siteSettings = resolveSiteSettings(site, forceRefresh) + ?: return reasonIfNoPositiveCache(cachedPositive) + + return if (!isCountryAndCurrencySupported(siteSettings.countryCode, siteSettings.currencyCode)) { + WooPosLaunchability.NonLaunchabilityReason.UnsupportedCurrency } else { - wooCommerceStore.getSiteSettings(site) - ?: wooCommerceStore.fetchSiteGeneralSettings(site).model + null } + } - if (siteSettings == null) { - return WooPosLaunchability.NotLaunchable( - WooPosLaunchability.NonLaunchabilityReason.SiteSettingsUnavailable - ) + private fun reasonIfNoPositiveCache(hasCachedPositive: Boolean): WooPosLaunchability.NonLaunchabilityReason? = + if (hasCachedPositive) { + null + } else { + WooPosLaunchability.NonLaunchabilityReason.UnknownNoPositiveCache } - return if (isCountryAndCurrencySupported(siteSettings.countryCode, siteSettings.currencyCode)) { - WooPosLaunchability.Launchable + private suspend fun getWooCoreVersion(forceRefresh: Boolean): String? = + if (forceRefresh) fetchWooCoreVersion() else getWooCoreCachedVersion() + + /** + * Checks the WooCommerce core version to see if it prevents POS from being launchable. + * Returns the NonLaunchabilityReason if it does, or null if the version is supported. + */ + private fun getNonLaunchabilityReasonFromWooCoreVersion( + wooCoreVersion: String + ): WooPosLaunchability.NonLaunchabilityReason? = + if (!isWooCoreSupportsOrderAutoDraftsAndExtraPaymentsProps(wooCoreVersion)) { + WooPosLaunchability.NonLaunchabilityReason.UnsupportedWooCommerceVersion } else { - WooPosLaunchability.NotLaunchable( - WooPosLaunchability.NonLaunchabilityReason.UnsupportedCurrency - ) + null } + + /** + * Checks the feature switch to see if it prevents POS from being launchable. + * Returns the NonLaunchabilityReason if it does, or null if POS might still be launchable. + */ + private suspend fun getNonLaunchabilityReasonFromFeatureSwitch( + wooCoreVersion: String, + forceRemoteRefresh: Boolean, + hasCachedLaunchableState: Boolean + ): WooPosLaunchability.NonLaunchabilityReason? { + val result = + if (!isFeatureSwitchSupported(wooCoreVersion)) { + null + } else { + val enabled = isRemotelyEnabled(forceRemoteRefresh).getOrNull() + when { + enabled == true -> null + enabled == false -> WooPosLaunchability.NonLaunchabilityReason.FeatureSwitchDisabled + hasCachedLaunchableState -> null + else -> WooPosLaunchability.NonLaunchabilityReason.UnknownNoPositiveCache + } + } + + return result } + private suspend fun resolveSiteSettings(site: SiteModel, forceRefresh: Boolean): Settings? = + if (forceRefresh) { + wooCommerceStore.fetchSiteGeneralSettings(site).model + } else { + wooCommerceStore.getSiteSettings(site) ?: wooCommerceStore.fetchSiteGeneralSettings(site).model + } + private fun isCountryAndCurrencySupported(countryCode: String, currency: String) = SUPPORTED_COUNTRY_CURRENCY_PAIRS.any { it.first.equals(countryCode, true) && it.second.equals(currency, true) @@ -117,5 +185,6 @@ sealed class WooPosLaunchability { FeatureSwitchDisabled, UnsupportedCurrency, NoSiteSelected, + UnknownNoPositiveCache } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosTabController.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosTabController.kt index 0f7f3ba15f9..4d9a7fa6887 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosTabController.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosTabController.kt @@ -10,6 +10,7 @@ import com.woocommerce.android.R import com.woocommerce.android.databinding.ActivityMainBinding import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.main.MainActivity +import com.woocommerce.android.ui.woopos.common.util.WooPosLogWrapper import com.woocommerce.android.ui.woopos.root.WooPosActivity import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker @@ -20,7 +21,8 @@ class WooPosTabController @Inject constructor( private val appPrefs: AppPrefs, private val selectedSite: SelectedSite, private val shouldPosTabBeVisible: WooPosTabShouldBeVisible, - private val analyticsTracker: WooPosAnalyticsTracker + private val analyticsTracker: WooPosAnalyticsTracker, + private val wooPosLog: WooPosLogWrapper ) : DefaultLifecycleObserver { private lateinit var activity: MainActivity @@ -64,10 +66,22 @@ class WooPosTabController @Inject constructor( private fun updateTabVisibilityFromRemoteAndPersist() { activity.lifecycleScope.launch { - val tabShouldBeVisible = shouldPosTabBeVisible() - setPOSTabVisibility(tabShouldBeVisible) - appPrefs.setPOSTabVisibilityForSite(selectedSite.getSelectedSiteId(), tabShouldBeVisible) - analyticsTracker.track(WooPosAnalyticsEvent.Event.TabVisibilityChecked(tabShouldBeVisible)) + val result = shouldPosTabBeVisible() + + result.onSuccess { tabShouldBeVisible -> + setPOSTabVisibility(tabShouldBeVisible) + if (tabShouldBeVisible) { + appPrefs.setPOSTabVisibilityForSite(selectedSite.getSelectedSiteId()) + } else { + appPrefs.clearPOSTabVisibilityForSite(selectedSite.getSelectedSiteId()) + } + } + + result.onFailure { error -> + wooPosLog.i("POS Tab Visibility Value cannot be determined") + } + + analyticsTracker.track(WooPosAnalyticsEvent.Event.TabVisibilityChecked(result)) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosTabShouldBeVisible.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosTabShouldBeVisible.kt index 0b75cf91b3e..30538e8e6be 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosTabShouldBeVisible.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosTabShouldBeVisible.kt @@ -2,6 +2,7 @@ package com.woocommerce.android.ui.woopos.tab import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.woopos.WooPosIsScreenSizeAllowed +import com.woocommerce.android.ui.woopos.common.util.WooPosCouldNotDetermineValueException import com.woocommerce.android.ui.woopos.common.util.WooPosLogWrapper import com.woocommerce.android.util.IsRemoteFeatureFlagEnabled import com.woocommerce.android.util.RemoteFeatureFlag.WOO_POS @@ -17,33 +18,34 @@ class WooPosTabShouldBeVisible @Inject constructor( private val isRemoteFeatureFlagEnabled: IsRemoteFeatureFlagEnabled, private val wooPosLog: WooPosLogWrapper, ) { - suspend operator fun invoke(): Boolean = withContext(Dispatchers.IO) { - val selectedSite = selectedSite.getOrNull() ?: return@withContext false + suspend operator fun invoke(): Result = withContext(Dispatchers.IO) { + val selectedSite = selectedSite.getOrNull() + ?: return@withContext Result.failure(WooPosCouldNotDetermineValueException()) if (!isRemoteFeatureFlagEnabled(WOO_POS)) { - return@withContext false.also { + return@withContext Result.success(false).also { wooPosLog.i("POS Tab Not visible reason: Remote feature flag is disabled") } } if (!isScreenSizeAllowed()) { - return@withContext false.also { + return@withContext Result.success(false).also { wooPosLog.i("POS Tab Not visible reason: Screen size is not allowed") } } - val siteSettings = wooCommerceStore.fetchSiteGeneralSettings(selectedSite).model - ?: return@withContext false.also { - wooPosLog.i("POS Tab Not visible reason: Site settings are not available") - } + val siteSettings = wooCommerceStore + .fetchSiteGeneralSettings(selectedSite) + .model + ?: return@withContext Result.failure(WooPosCouldNotDetermineValueException()) - return@withContext isCountrySupported( - countryCode = siteSettings.countryCode - ).also { isSupported -> - if (!isSupported) { - wooPosLog.i("POS Tab Not visible reason: Country ${siteSettings.countryCode} is not supported") + return@withContext Result.success( + isCountrySupported(countryCode = siteSettings.countryCode).also { isSupported -> + if (!isSupported) { + wooPosLog.i("POS Tab Not visible reason: Country ${siteSettings.countryCode} is not supported") + } } - } + ) } private fun isCountrySupported(countryCode: String) = SUPPORTED_COUNTRIES.contains(countryCode.lowercase()) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt index 895d153d9c4..aa145f93eaf 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt @@ -104,15 +104,16 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { } } - data class TabVisibilityChecked(val isVisible: Boolean) : Event() { + data class TabVisibilityChecked(val isVisible: Result) : Event() { override val name: String = "tab_visibility_checked" init { - addProperties( - mapOf( - "is_visible" to isVisible.toString() - ) + val value: String = isVisible.fold( + onSuccess = { it.toString() }, + onFailure = { "unknown" } ) + + addProperties(mapOf("is_visible" to value)) } } @@ -823,6 +824,7 @@ internal fun WooPosLaunchability.NonLaunchabilityReason.toAnalyticsReason(): Str WooPosLaunchability.NonLaunchabilityReason.FeatureSwitchDisabled -> "feature_switch_disabled" WooPosLaunchability.NonLaunchabilityReason.UnsupportedCurrency -> "store_currency" WooPosLaunchability.NonLaunchabilityReason.SiteSettingsUnavailable, + WooPosLaunchability.NonLaunchabilityReason.UnknownNoPositiveCache, WooPosLaunchability.NonLaunchabilityReason.NoSiteSelected -> "other" } } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/WooPOSIsRemotelyEnabledTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/WooPOSIsRemotelyEnabledTest.kt index 20d39423426..b430028c1e2 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/WooPOSIsRemotelyEnabledTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/WooPOSIsRemotelyEnabledTest.kt @@ -2,6 +2,7 @@ package com.woocommerce.android.ui.woopos import com.google.gson.Gson import com.woocommerce.android.tools.SelectedSite +import com.woocommerce.android.ui.woopos.common.util.WooPosCouldNotDetermineValueException import com.woocommerce.android.util.WCSSRModelCachingFetcher import kotlinx.coroutines.test.runTest import org.junit.Before @@ -30,23 +31,21 @@ class WooPOSIsRemotelyEnabledTest { } @Test - fun `given feature enabled remotely when invoked then returns true`() = runTest { - // GIVEN + fun `given feature enabled remotely, when invoked, then returns success true`() = runTest { val jsonSettings = """{"enabled_features": ["point_of_sale", "other_feature"]}""" whenever(ssrModel.settings).thenReturn(jsonSettings) whenever(cacheResult.isError).thenReturn(false) whenever(cacheResult.model).thenReturn(ssrModel) whenever(fetcher.load(siteModel)).thenReturn(cacheResult) - // WHEN val result = sut.invoke() - // THEN - assertTrue(result) + assertTrue(result.isSuccess) + assertTrue(result.getOrThrow()) } @Test - fun `given feature not in list when invoked then returns false`() = runTest { + fun `given feature not in list, when invoked, then returns success false`() = runTest { val jsonSettings = """{"enabled_features": ["something_else"]}""" whenever(ssrModel.settings).thenReturn(jsonSettings) whenever(cacheResult.isError).thenReturn(false) @@ -55,11 +54,12 @@ class WooPOSIsRemotelyEnabledTest { val result = sut.invoke() - assertFalse(result) + assertTrue(result.isSuccess) + assertFalse(result.getOrThrow()) } @Test - fun `given empty feature list when invoked then returns false`() = runTest { + fun `given empty feature list, when invoked, then returns success false`() = runTest { val jsonSettings = """{"enabled_features": []}""" whenever(ssrModel.settings).thenReturn(jsonSettings) whenever(cacheResult.isError).thenReturn(false) @@ -68,27 +68,30 @@ class WooPOSIsRemotelyEnabledTest { val result = sut.invoke() - assertFalse(result) + assertTrue(result.isSuccess) + assertFalse(result.getOrThrow()) } @Test - fun `given null result when invoked then returns false`() = runTest { + fun `given null result, when invoked, then returns failure unknown`() = runTest { whenever(cacheResult.isError).thenReturn(false) whenever(cacheResult.model).thenReturn(null) whenever(fetcher.load(siteModel)).thenReturn(cacheResult) val result = sut.invoke() - assertFalse(result) + assertTrue(result.isFailure) + assertTrue(result.exceptionOrNull() is WooPosCouldNotDetermineValueException) } @Test - fun `given error result when invoked then returns false`() = runTest { + fun `given error result, when invoked, then returns failure unknown`() = runTest { whenever(cacheResult.isError).thenReturn(true) whenever(fetcher.load(siteModel)).thenReturn(cacheResult) val result = sut.invoke() - assertFalse(result) + assertTrue(result.isFailure) + assertTrue(result.exceptionOrNull() is WooPosCouldNotDetermineValueException) } } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosCanBeLaunchedInTabTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosCanBeLaunchedInTabTest.kt index 0487b1fde70..3b7c4d3e21e 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosCanBeLaunchedInTabTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosCanBeLaunchedInTabTest.kt @@ -1,5 +1,6 @@ package com.woocommerce.android.ui.woopos.tab +import com.woocommerce.android.AppPrefs import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.woopos.WooPOSIsRemotelyEnabled import com.woocommerce.android.ui.woopos.tab.WooPosLaunchability.Launchable @@ -11,7 +12,10 @@ import com.woocommerce.android.viewmodel.BaseUnitTest import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Before import org.mockito.kotlin.any +import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import org.wordpress.android.fluxc.model.LocalOrRemoteId import org.wordpress.android.fluxc.model.SiteModel @@ -24,6 +28,7 @@ import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class WooPosCanBeLaunchedInTabTest : BaseUnitTest() { + private val appPrefs: AppPrefs = mock() private val selectedSite: SelectedSite = mock() private val wooCommerceStore: WooCommerceStore = mock() private val isRemotelyEnabled: WooPOSIsRemotelyEnabled = mock() @@ -31,19 +36,26 @@ class WooPosCanBeLaunchedInTabTest : BaseUnitTest() { private val fetchWooCoreVersion: FetchActiveWCPluginVersion = mock() private lateinit var sut: WooPosCanBeLaunchedInTab + private lateinit var siteModel: SiteModel @Before fun setup() = testBlocking { - val siteModel = SiteModel().also { it.id = 1 } + siteModel = SiteModel().also { it.id = 1 } whenever(selectedSite.getOrNull()).thenReturn(siteModel) + + // Default: WC 10.0.0, settings available and supported, remote enabled, no cached positive whenever(getWooCoreVersion()).thenReturn("10.0.0") whenever(fetchWooCoreVersion()).thenReturn("10.0.0") + val siteSettings = buildSiteSettings() whenever(wooCommerceStore.getSiteSettings(siteModel)).thenReturn(siteSettings) whenever(wooCommerceStore.fetchSiteGeneralSettings(siteModel)).thenReturn(WooResult(siteSettings)) - whenever(isRemotelyEnabled.invoke(any())).thenReturn(true) + + whenever(isRemotelyEnabled.invoke(any())).thenReturn(Result.success(true)) + whenever(appPrefs.isPOSLaunchableForSite(eq(siteModel.id))).thenReturn(false) sut = WooPosCanBeLaunchedInTab( + appPrefs = appPrefs, selectedSite = selectedSite, getWooCoreCachedVersion = getWooCoreVersion, fetchWooCoreVersion = fetchWooCoreVersion, @@ -53,133 +65,194 @@ class WooPosCanBeLaunchedInTabTest : BaseUnitTest() { ) } + // --- Happy paths --- + @Test - fun `given valid conditions, when invoked, then return Launchable`() = testBlocking { + fun `given valid conditions, when invoked, then return Launchable and set cache`() = testBlocking { val result = sut() assertEquals(Launchable, result) + verify(appPrefs, times(1)).setPOSLaunchableForSite(eq(siteModel.id)) } @Test - fun `given no site selected, when invoked, then return NotLaunchable with NoSiteSelected`() = testBlocking { - whenever(selectedSite.getOrNull()).thenReturn(null) + fun `given uk country and pounds, when invoked, then return Launchable`() = testBlocking { + val siteSettings = buildSiteSettings(countryCode = "GB", currencyCode = "GBP") + whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(siteSettings) val result = sut() - assertEquals(NotLaunchable(NonLaunchabilityReason.NoSiteSelected), result) + assertEquals(Launchable, result) } @Test - fun `given unsupported WooCommerce version, when invoked, then return NotLaunchable with UnsupportedWooCommerceVersion`() = testBlocking { - whenever(getWooCoreVersion()).thenReturn("9.5.0") // lower than 9.6.0 + fun `given us country and dollars, when invoked, then return Launchable`() = testBlocking { + val siteSettings = buildSiteSettings(countryCode = "US", currencyCode = "USD") + whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(siteSettings) val result = sut() - assertEquals(NotLaunchable(NonLaunchabilityReason.UnsupportedWooCommerceVersion), result) + assertEquals(Launchable, result) } @Test - fun `given feature switch supported but remotely disabled, when invoked, then return NotLaunchable with FeatureSwitchDisabled`() = testBlocking { - whenever(getWooCoreVersion()).thenReturn("10.0.0") - whenever(isRemotelyEnabled.invoke(any())).thenReturn(false) + fun `given site settings missing but fetched successfully, when invoked, then return Launchable`() = testBlocking { + whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(null) + val fetchedSettings = buildSiteSettings(currencyCode = "usd") + whenever(wooCommerceStore.fetchSiteGeneralSettings(any())).thenReturn(WooResult(fetchedSettings)) val result = sut() - assertEquals(NotLaunchable(NonLaunchabilityReason.FeatureSwitchDisabled), result) + assertEquals(Launchable, result) } + // --- No site --- + @Test - fun `given null site settings, when invoked, then return NotLaunchable with SiteSettingsUnavailable`() = testBlocking { - whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(null) - whenever(wooCommerceStore.fetchSiteGeneralSettings(any())).thenReturn(WooResult(null)) + fun `given no site selected, when invoked, then NotLaunchable NoSiteSelected and no prefs touched`() = testBlocking { + whenever(selectedSite.getOrNull()).thenReturn(null) val result = sut() - assertEquals(NotLaunchable(NonLaunchabilityReason.SiteSettingsUnavailable), result) + assertEquals(NotLaunchable(NonLaunchabilityReason.NoSiteSelected), result) + verify(appPrefs, times(0)).setPOSLaunchableForSite(any()) + verify(appPrefs, times(0)).clearPOSLaunchableForSite(any()) } + // --- Version checks --- + @Test - fun `given unsupported currency, when invoked, then return NotLaunchable with UnsupportedCurrency`() = testBlocking { - val siteSettings = buildSiteSettings(currencyCode = "eur") - whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(siteSettings) + fun `given unsupported WooCommerce version, when invoked, then NotLaunchable UnsupportedWooCommerceVersion and clears cache`() = testBlocking { + whenever(getWooCoreVersion()).thenReturn("9.5.0") val result = sut() - assertEquals(NotLaunchable(NonLaunchabilityReason.UnsupportedCurrency), result) + assertEquals(NotLaunchable(NonLaunchabilityReason.UnsupportedWooCommerceVersion), result) + verify(appPrefs, times(1)).clearPOSLaunchableForSite(eq(siteModel.id)) } + // Feature-switch unsupported (version >= 9_6_0 but switch threshold not met) @Test - fun `given uk country and pounds, when invoked, then return Launchable`() = testBlocking { - val siteSettings = buildSiteSettings(countryCode = "GB", currencyCode = "GBP") - whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(siteSettings) + fun `given WC 9_6_0 (switch unsupported) and supported settings, when invoked, then Launchable`() = testBlocking { + whenever(getWooCoreVersion()).thenReturn("9.6.0") val result = sut() assertEquals(Launchable, result) } @Test - fun `given us country and dollars, when invoked, then return Launchable`() = testBlocking { - val siteSettings = buildSiteSettings(countryCode = "US", currencyCode = "USD") - whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(siteSettings) + fun `given WC 9_6_0 (switch unsupported) and unsupported currency, when invoked, then NotLaunchable UnsupportedCurrency and clears cache`() = testBlocking { + whenever(getWooCoreVersion()).thenReturn("9.6.0") + val bad = buildSiteSettings(currencyCode = "EUR") + whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(bad) val result = sut() - assertEquals(Launchable, result) + assertEquals(NotLaunchable(NonLaunchabilityReason.UnsupportedCurrency), result) + verify(appPrefs, times(1)).clearPOSLaunchableForSite(eq(siteModel.id)) } + // --- Feature switch checks --- + @Test - fun `given uk country and usd, when invoked, then return Not Launchable`() = testBlocking { - val siteSettings = buildSiteSettings(countryCode = "GB", currencyCode = "USD") - whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(siteSettings) + fun `given feature switch supported but remotely disabled, when invoked, then NotLaunchable FeatureSwitchDisabled and clears cache`() = testBlocking { + whenever(getWooCoreVersion()).thenReturn("10.0.0") + whenever(isRemotelyEnabled.invoke(any())).thenReturn(Result.success(false)) val result = sut() - assertEquals(NotLaunchable(NonLaunchabilityReason.UnsupportedCurrency), result) + assertEquals(NotLaunchable(NonLaunchabilityReason.FeatureSwitchDisabled), result) + verify(appPrefs, times(1)).clearPOSLaunchableForSite(eq(siteModel.id)) } @Test - fun `given us country and pounds, when invoked, then return Not Launchable`() = testBlocking { - val siteSettings = buildSiteSettings(countryCode = "US", currencyCode = "GBP") - whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(siteSettings) + fun `given remote check failure and cached positive, when invoked, then Launchable`() = testBlocking { + whenever(isRemotelyEnabled.invoke(any())).thenReturn(Result.failure(RuntimeException("boom"))) + whenever(appPrefs.isPOSLaunchableForSite(eq(siteModel.id))).thenReturn(true) val result = sut() - assertEquals(NotLaunchable(NonLaunchabilityReason.UnsupportedCurrency), result) + assertEquals(Launchable, result) } @Test - fun `given site settings missing but fetched successfully, when invoked, then return Launchable`() = testBlocking { - whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(null) - val fetchedSettings = buildSiteSettings(currencyCode = "usd") - whenever(wooCommerceStore.fetchSiteGeneralSettings(any())).thenReturn(WooResult(fetchedSettings)) + fun `given remote check failure and no cached positive, when invoked, then NotLaunchable UnknownNoPositiveCache and does not clear`() = testBlocking { + whenever(isRemotelyEnabled.invoke(any())).thenReturn(Result.failure(RuntimeException("boom"))) + whenever(appPrefs.isPOSLaunchableForSite(eq(siteModel.id))).thenReturn(false) + val result = sut() + assertEquals(NotLaunchable(NonLaunchabilityReason.UnknownNoPositiveCache), result) + verify(appPrefs, times(0)).clearPOSLaunchableForSite(any()) + } + // --- Site settings fallback --- + + @Test + fun `given null site settings and no cached positive, when invoked, then NotLaunchable UnknownNoPositiveCache and does not clear`() = testBlocking { + whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(null) + whenever(wooCommerceStore.fetchSiteGeneralSettings(any())).thenReturn(WooResult(null)) + whenever(appPrefs.isPOSLaunchableForSite(eq(siteModel.id))).thenReturn(false) val result = sut() - assertEquals(Launchable, result) + assertEquals(NotLaunchable(NonLaunchabilityReason.UnknownNoPositiveCache), result) + verify(appPrefs, times(0)).clearPOSLaunchableForSite(any()) } @Test - fun `given site settings missing and fetched with unsupported currency, when invoked, then return NotLaunchable with UnsupportedCurrency`() = testBlocking { + fun `given site settings missing and fetched with unsupported currency, when invoked, then NotLaunchable UnsupportedCurrency and clears cache`() = testBlocking { whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(null) val fetchedSettings = buildSiteSettings(currencyCode = "eur") whenever(wooCommerceStore.fetchSiteGeneralSettings(any())).thenReturn(WooResult(fetchedSettings)) + val result = sut() + assertEquals(NotLaunchable(NonLaunchabilityReason.UnsupportedCurrency), result) + verify(appPrefs, times(1)).clearPOSLaunchableForSite(eq(siteModel.id)) + } + + // --- Currency matrix --- + + @Test + fun `given uk country and usd, when invoked, then NotLaunchable UnsupportedCurrency and clears cache`() = testBlocking { + val siteSettings = buildSiteSettings(countryCode = "GB", currencyCode = "USD") + whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(siteSettings) + val result = sut() + assertEquals(NotLaunchable(NonLaunchabilityReason.UnsupportedCurrency), result) + verify(appPrefs, times(1)).clearPOSLaunchableForSite(eq(siteModel.id)) + } + @Test + fun `given us country and pounds, when invoked, then NotLaunchable UnsupportedCurrency and clears cache`() = testBlocking { + val siteSettings = buildSiteSettings(countryCode = "US", currencyCode = "GBP") + whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(siteSettings) val result = sut() assertEquals(NotLaunchable(NonLaunchabilityReason.UnsupportedCurrency), result) + verify(appPrefs, times(1)).clearPOSLaunchableForSite(eq(siteModel.id)) } + // --- Force refresh paths --- + @Test - fun `given forceRefresh true with valid data, when invoked, then return Launchable`() = testBlocking { + fun `given forceRefresh true with valid data, when invoked, then Launchable and set cache`() = testBlocking { val result = sut(forceRefresh = true) assertEquals(Launchable, result) + verify(appPrefs, times(1)).setPOSLaunchableForSite(eq(siteModel.id)) } @Test - fun `given forceRefresh true and fetchWooCoreVersion returns null, when invoked, then return NotLaunchable with WooCommercePluginNotFound`() = testBlocking { + fun `given forceRefresh true and fetchWooCoreVersion returns null with no cached positive, when invoked, then NotLaunchable UnknownNoPositiveCache and does not clear`() = testBlocking { whenever(fetchWooCoreVersion()).thenReturn(null) + whenever(appPrefs.isPOSLaunchableForSite(eq(siteModel.id))).thenReturn(false) + val result = sut(forceRefresh = true) + assertEquals(NotLaunchable(NonLaunchabilityReason.UnknownNoPositiveCache), result) + verify(appPrefs, times(0)).clearPOSLaunchableForSite(any()) + } + @Test + fun `given forceRefresh true and fetchWooCoreVersion returns null with cached positive, when invoked, then Launchable`() = testBlocking { + whenever(fetchWooCoreVersion()).thenReturn(null) + whenever(appPrefs.isPOSLaunchableForSite(eq(siteModel.id))).thenReturn(true) val result = sut(forceRefresh = true) - assertEquals(NotLaunchable(NonLaunchabilityReason.WooCommercePluginNotFound), result) + assertEquals(Launchable, result) } @Test - fun `given forceRefresh true and fetched site settings is null, when invoked, then return NotLaunchable with SiteSettingsUnavailable`() = testBlocking { + fun `given forceRefresh true and fetched site settings is null with no cached positive, when invoked, then NotLaunchable UnknownNoPositiveCache and does not clear`() = testBlocking { whenever(wooCommerceStore.fetchSiteGeneralSettings(any())).thenReturn(WooResult(null)) - + whenever(appPrefs.isPOSLaunchableForSite(eq(siteModel.id))).thenReturn(false) val result = sut(forceRefresh = true) - assertEquals(NotLaunchable(NonLaunchabilityReason.SiteSettingsUnavailable), result) + assertEquals(NotLaunchable(NonLaunchabilityReason.UnknownNoPositiveCache), result) + verify(appPrefs, times(0)).clearPOSLaunchableForSite(any()) } @Test - fun `given forceRefresh true and fetched site settings with unsupported currency, when invoked, then return NotLaunchable with UnsupportedCurrency`() = testBlocking { - val fetchedSettings = buildSiteSettings(currencyCode = "eur") - whenever(wooCommerceStore.fetchSiteGeneralSettings(any())).thenReturn(WooResult(fetchedSettings)) - + fun `given forceRefresh true and fetched site settings is null with cached positive, when invoked, then Launchable`() = testBlocking { + whenever(wooCommerceStore.fetchSiteGeneralSettings(any())).thenReturn(WooResult(null)) + whenever(appPrefs.isPOSLaunchableForSite(eq(siteModel.id))).thenReturn(true) val result = sut(forceRefresh = true) - assertEquals(NotLaunchable(NonLaunchabilityReason.UnsupportedCurrency), result) + assertEquals(Launchable, result) } + // --- + private fun buildSiteSettings( countryCode: String = "US", currencyCode: String = "USD" diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosTabShouldBeVisibleTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosTabShouldBeVisibleTest.kt index 1dedeb01d8a..607242c0802 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosTabShouldBeVisibleTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/tab/WooPosTabShouldBeVisibleTest.kt @@ -2,6 +2,7 @@ package com.woocommerce.android.ui.woopos.tab import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.woopos.WooPosIsScreenSizeAllowed +import com.woocommerce.android.ui.woopos.common.util.WooPosCouldNotDetermineValueException import com.woocommerce.android.util.IsRemoteFeatureFlagEnabled import com.woocommerce.android.util.RemoteFeatureFlag.WOO_POS import com.woocommerce.android.viewmodel.BaseUnitTest @@ -29,10 +30,11 @@ class WooPosTabShouldBeVisibleTest : BaseUnitTest() { private val isRemoteFeatureFlagEnabled: IsRemoteFeatureFlagEnabled = mock() private lateinit var sut: WooPosTabShouldBeVisible + private lateinit var siteModel: SiteModel @Before fun setup() = testBlocking { - val siteModel = SiteModel().also { it.id = 1 } + siteModel = SiteModel().also { it.id = 1 } whenever(selectedSite.getOrNull()).thenReturn(siteModel) whenever(isScreenSizeAllowed()).thenReturn(true) whenever(isRemoteFeatureFlagEnabled(WOO_POS)).thenReturn(true) @@ -41,41 +43,51 @@ class WooPosTabShouldBeVisibleTest : BaseUnitTest() { sut = WooPosTabShouldBeVisible( selectedSite = selectedSite, - wooCommerceStore = wooCommerceStore, isScreenSizeAllowed = isScreenSizeAllowed, + wooCommerceStore = wooCommerceStore, isRemoteFeatureFlagEnabled = isRemoteFeatureFlagEnabled, wooPosLog = mock() ) } @Test - fun `given feature flag enabled and supported country, when invoked, then return true`() = testBlocking { - assertTrue(sut()) + fun `given feature flag enabled and supported country, when invoked, then return success true`() = testBlocking { + val r = sut() + assertTrue(r.isSuccess) + assertTrue(r.getOrThrow()) } @Test - fun `given feature flag disabled, when invoked, then return false`() = testBlocking { + fun `given feature flag disabled, when invoked, then return success false`() = testBlocking { whenever(isRemoteFeatureFlagEnabled(WOO_POS)).thenReturn(false) - assertFalse(sut()) + val r = sut() + assertTrue(r.isSuccess) + assertFalse(r.getOrThrow()) } @Test - fun `given screen size not allowed, when invoked, then return false`() = testBlocking { + fun `given screen size not allowed, when invoked, then return success false`() = testBlocking { whenever(isScreenSizeAllowed()).thenReturn(false) - assertFalse(sut()) + val r = sut() + assertTrue(r.isSuccess) + assertFalse(r.getOrThrow()) } @Test - fun `given null site, when invoked, then return false`() = testBlocking { + fun `given null site, when invoked, then return failure unknown`() = testBlocking { whenever(selectedSite.getOrNull()).thenReturn(null) - assertFalse(sut()) + val r = sut() + assertTrue(r.isFailure) + assertTrue(r.exceptionOrNull() is WooPosCouldNotDetermineValueException) } @Test - fun `given unsupported country, when invoked, then return false`() = testBlocking { + fun `given unsupported country, when invoked, then return success false`() = testBlocking { val siteSettings = buildSiteSettings(countryCode = "ca") whenever(wooCommerceStore.fetchSiteGeneralSettings(any())).thenReturn(WooResult(siteSettings)) - assertFalse(sut()) + val r = sut() + assertTrue(r.isSuccess) + assertFalse(r.getOrThrow()) } @Test @@ -89,19 +101,23 @@ class WooPosTabShouldBeVisibleTest : BaseUnitTest() { } @Test - fun `given fetched settings with supported country, when invoked, then return true`() = testBlocking { + fun `given fetched settings with supported country, when invoked, then return success true`() = testBlocking { val fetchedSettings = buildSiteSettings(countryCode = "gb") whenever(wooCommerceStore.fetchSiteGeneralSettings(any())).thenReturn(WooResult(fetchedSettings)) - assertTrue(sut()) + val r = sut() + assertTrue(r.isSuccess) + assertTrue(r.getOrThrow()) } @Test - fun `given fetched settings with unsupported country, when invoked, then return false`() = testBlocking { + fun `given fetched settings with unsupported country, when invoked, then return success false`() = testBlocking { val fetchedSettings = buildSiteSettings(countryCode = "ca") whenever(wooCommerceStore.fetchSiteGeneralSettings(any())).thenReturn(WooResult(fetchedSettings)) - assertFalse(sut()) + val r = sut() + assertTrue(r.isSuccess) + assertFalse(r.getOrThrow()) } private fun buildSiteSettings(countryCode: String = "us") =