Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JP Content Migration Flow] Check Wordpress version compatibility #17536

Merged
merged 11 commits into from
Nov 25, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import org.wordpress.android.ui.ActivityLauncher
import org.wordpress.android.ui.prefs.AppPrefsWrapper
import org.wordpress.android.util.BuildConfigWrapper
import org.wordpress.android.util.config.JetpackMigrationFlowFeatureConfig
import org.wordpress.android.util.helpers.Version
import org.wordpress.android.util.publicdata.AppStatus
import org.wordpress.android.util.publicdata.WordPressPublicData
import org.wordpress.android.viewmodel.ContextProvider
import javax.inject.Inject

Expand All @@ -14,13 +17,26 @@ class JetpackAppMigrationFlowUtils @Inject constructor(
private val contextProvider: ContextProvider,
private val appPrefsWrapper: AppPrefsWrapper,
private val accountStore: AccountStore,
private val appStatus: AppStatus,
private val wordPressPublicData: WordPressPublicData,
) {
private val minimumSupportedVersion = "21.3" // non semantic minimum supported version

fun shouldShowMigrationFlow() = buildConfigWrapper.isJetpackApp
&& jetpackMigrationFlowFeatureConfig.isEnabled()
&& appPrefsWrapper.getIsFirstTrySharedLoginJetpack()
&& !accountStore.hasAccessToken()
&& isWordPressInstalled()
&& isWordPressCompatible()

fun startJetpackMigrationFlow() {
ActivityLauncher.startJetpackMigrationFlow(contextProvider.getContext())
}

private fun isWordPressInstalled() = appStatus.isAppInstalled(wordPressPublicData.currentPackageId())

private fun isWordPressCompatible(): Boolean {
val wordPressVersion = wordPressPublicData.nonSemanticPackageVersion()
return wordPressVersion != null && Version(wordPressVersion) >= Version(minimumSupportedVersion)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.wordpress.android.util.publicdata.WordPressPublicData.PackageName.Van
import org.wordpress.android.util.publicdata.WordPressPublicData.PackageName.Wasabi
import javax.inject.Inject

class WordPressPublicData @Inject constructor() {
class WordPressPublicData @Inject constructor(private val packageManagerWrapper: PackageManagerWrapper) {
private sealed class PackageName(val type: String, val value: String) {
object Jalapeno : PackageName("jalapeno", "org.wordpress.android.prealpha")

Expand All @@ -21,4 +21,20 @@ class WordPressPublicData @Inject constructor() {
Wasabi.type -> Wasabi.value
else -> throw IllegalArgumentException("Failed to get Jetpack package ID: build flavor not found.")
}

fun currentPackageVersion(): String? = packageManagerWrapper.getPackageInfo(currentPackageId())?.versionName

fun nonSemanticPackageVersion(): String? {
val rawVersion = currentPackageVersion() ?: return null

// Clean app semantic versioning and keep ony major-minor version info. E.g 21.2-rc-3 turns to 21.2
val majorMinorRegex = "^(\\d*)(\\.(\\d*))".toRegex()
val wordPressVersion = majorMinorRegex.find(rawVersion)?.value

// Verify that the resulting version is supported by org.wordpress.android.util.helpers.Version.Version
val versionIsSupportedForComparison = wordPressVersion !=null
&& Regex("[0-9]+(\\.[0-9]+)*").matchEntire(wordPressVersion) != null

return if (versionIsSupportedForComparison) wordPressVersion else null
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,92 @@
package org.wordpress.android.sharedlogin

import android.content.pm.PackageInfo
import org.assertj.core.api.Assertions
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.wordpress.android.util.publicdata.PackageManagerWrapper
import org.wordpress.android.util.publicdata.WordPressPublicData

class WordPressPublicDataTest {
private val classToTest = WordPressPublicData()
private val packageManagerWrapper: PackageManagerWrapper = mock()

private val classToTest = WordPressPublicData(packageManagerWrapper)

@Test
fun `Should return correct current package ID`() {
val actual = classToTest.currentPackageId()
val expected = "org.wordpress.android"
Assertions.assertThat(actual).isEqualTo(expected)
}

@Test
fun `Should return correct current package version`() {
mockVersion("21.2-rc-3")
val actual = classToTest.currentPackageVersion()
val expected = "21.2-rc-3"
Assertions.assertThat(actual).isEqualTo(expected)
}

@Test
fun `Versions without semantic information should be equal to the non semantic version`() {
mockVersion("21.2")
val actual = classToTest.nonSemanticPackageVersion()
val expected = "21.2"
Assertions.assertThat(actual).isEqualTo(expected)
}

@Test
fun `Release candidate versions should be stripped from the non semantic version`() {
mockVersion("21.2-rc-3")
val actual = classToTest.nonSemanticPackageVersion()
val expected = "21.2"
Assertions.assertThat(actual).isEqualTo(expected)
}

@Test
fun `Alpha versions should be stripped from the non semantic version`() {
mockVersion("21.2-alpha-3")
val actual = classToTest.nonSemanticPackageVersion()
val expected = "21.2"
Assertions.assertThat(actual).isEqualTo(expected)
}

@Test
fun `Invalid versions should return a null non semantic version`() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice tests 👍

mockVersion("21.a2...-rc2")
val actual = classToTest.nonSemanticPackageVersion()
val expected = null
Assertions.assertThat(actual).isEqualTo(expected)
}

@Test
fun `Empty versions should return a null non semantic version`() {
mockVersion("")
val actual = classToTest.nonSemanticPackageVersion()
val expected = null
Assertions.assertThat(actual).isEqualTo(expected)
}

@Test
fun `Only the major-minor version information is returned`() {
mockVersion("21.3.1")
val actual = classToTest.nonSemanticPackageVersion()
val expected = "21.3"
Assertions.assertThat(actual).isEqualTo(expected)
}

@Test
fun `When only the major is provided a null non semantic version is returned`() {
mockVersion("21")
val actual = classToTest.nonSemanticPackageVersion()
val expected = null
Assertions.assertThat(actual).isEqualTo(expected)
}

private fun mockVersion(version: String) {
val packageInfo = PackageInfo().apply { versionName = version }
whenever(packageManagerWrapper.getPackageInfo(any(), any())).thenReturn(packageInfo)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package org.wordpress.android.ui.utils

import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.wordpress.android.fluxc.store.AccountStore
import org.wordpress.android.ui.prefs.AppPrefsWrapper
import org.wordpress.android.util.BuildConfigWrapper
import org.wordpress.android.util.config.JetpackMigrationFlowFeatureConfig
import org.wordpress.android.util.publicdata.AppStatus
import org.wordpress.android.util.publicdata.WordPressPublicData
import org.wordpress.android.viewmodel.ContextProvider

@RunWith(MockitoJUnitRunner::class)
class JetpackAppMigrationFlowUtilsTest {
private val buildConfigWrapper: BuildConfigWrapper = mock()
private val jetpackMigrationFlowFeatureConfig: JetpackMigrationFlowFeatureConfig = mock()
private val contextProvider: ContextProvider = mock()
private val appPrefsWrapper: AppPrefsWrapper = mock()
private val accountStore: AccountStore = mock()
private val appStatus: AppStatus = mock()
private val wordPressPublicData: WordPressPublicData = mock()

private val jetpackAppMigrationFlowUtils = JetpackAppMigrationFlowUtils(
buildConfigWrapper,
jetpackMigrationFlowFeatureConfig,
contextProvider,
appPrefsWrapper,
accountStore,
appStatus,
wordPressPublicData
)

@Before
fun setUp() {
whenever(buildConfigWrapper.isJetpackApp).thenReturn(true)
whenever(jetpackMigrationFlowFeatureConfig.isEnabled()).thenReturn(true)
whenever(appPrefsWrapper.getIsFirstTrySharedLoginJetpack()).thenReturn(true)
whenever(accountStore.hasAccessToken()).thenReturn(false)
whenever(wordPressPublicData.currentPackageId()).thenReturn("package")
whenever(appStatus.isAppInstalled(any())).thenReturn(true)
whenever(wordPressPublicData.nonSemanticPackageVersion()).thenReturn("21.3")
}

@Test
fun `When all conditions are met the migration flow should be shown`() {
val expected = true
val actual = jetpackAppMigrationFlowUtils.shouldShowMigrationFlow()
Assert.assertEquals(expected, actual)
}

@Test
fun `When not in the Jetpack app the migration flow should not be shown`() {
whenever(buildConfigWrapper.isJetpackApp).thenReturn(false)
val expected = false
val actual = jetpackAppMigrationFlowUtils.shouldShowMigrationFlow()
Assert.assertEquals(expected, actual)
}

@Test
fun `When the jetpackMigrationFlow is not enableed the Jetpack app the migration flow should not be shown`() {
whenever(jetpackMigrationFlowFeatureConfig.isEnabled()).thenReturn(false)
val expected = false
val actual = jetpackAppMigrationFlowUtils.shouldShowMigrationFlow()
Assert.assertEquals(expected, actual)
}

@Test
fun `When the it is not the first migration attempt the Jetpack app the migration flow should not be shown`() {
whenever(appPrefsWrapper.getIsFirstTrySharedLoginJetpack()).thenReturn(false)
val expected = false
val actual = jetpackAppMigrationFlowUtils.shouldShowMigrationFlow()
Assert.assertEquals(expected, actual)
}

@Test
fun `If the user is logged in the Jetpack app the migration flow should not be shown`() {
whenever(accountStore.hasAccessToken()).thenReturn(true)
val expected = false
val actual = jetpackAppMigrationFlowUtils.shouldShowMigrationFlow()
Assert.assertEquals(expected, actual)
}

@Test
fun `When the WordPress app is not installed the Jetpack app the migration flow should not be shown`() {
whenever(appStatus.isAppInstalled(any())).thenReturn(false)
val expected = false
val actual = jetpackAppMigrationFlowUtils.shouldShowMigrationFlow()
Assert.assertEquals(expected, actual)
}

@Test
fun `When the WordPress app is not compatible the Jetpack app the migration flow should not be shown`() {
whenever(wordPressPublicData.nonSemanticPackageVersion()).thenReturn("21.2")
val expected = false
val actual = jetpackAppMigrationFlowUtils.shouldShowMigrationFlow()
Assert.assertEquals(expected, actual)
}

@Test
fun `When the WordPress app version is null the Jetpack app the migration flow should not be shown`() {
whenever(wordPressPublicData.nonSemanticPackageVersion()).thenReturn(null)
val expected = false
val actual = jetpackAppMigrationFlowUtils.shouldShowMigrationFlow()
Assert.assertEquals(expected, actual)
}
}