fix: Only suggest upgrading to a version F-Droid has built (#717)
The F-Droid API can return a `suggestedVersionCode` that F-Droid has not successfully built (e.g., there was a network issue preventing F-Droid from fetching the source code at the time of the last build). This meant users were being prompted to update when there was not a built package to update to. Fix this by verifying that `suggestedVersionCode` appears in the list of packages. If it doesn't then fall back to the highest version code in the list of packages. Fixes #684
This commit is contained in:
parent
de7e5a9df9
commit
61afde882c
|
@ -34,6 +34,19 @@ class UpdateCheck @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun remoteFetchLatestVersionCode(): Int? {
|
override suspend fun remoteFetchLatestVersionCode(): Int? {
|
||||||
return fdroidService.getPackage(BuildConfig.APPLICATION_ID).getOrNull()?.suggestedVersionCode
|
val fdroidPackage = fdroidService.getPackage(BuildConfig.APPLICATION_ID).getOrNull() ?: return null
|
||||||
|
|
||||||
|
// `packages` is a list of all packages that have been built and are available.
|
||||||
|
//
|
||||||
|
// The package with `suggestedVersionCode` might not have been built yet (see
|
||||||
|
// https://github.com/pachli/pachli-android/issues/684).
|
||||||
|
//
|
||||||
|
// Check that `suggestedVersionCode` appears in the list of packages. If it
|
||||||
|
// does, use that.
|
||||||
|
val suggestedVersionCode = fdroidPackage.suggestedVersionCode
|
||||||
|
if (fdroidPackage.packages.any { it.versionCode == suggestedVersionCode }) return suggestedVersionCode
|
||||||
|
|
||||||
|
// Otherwise, use the highest version code in `packages`.
|
||||||
|
return fdroidPackage.packages.maxByOrNull { it.versionCode }?.versionCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Pachli Association
|
||||||
|
*
|
||||||
|
* This file is a part of Pachli.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||||
|
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||||
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
* Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with Pachli; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package app.pachli.updatecheck
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import app.pachli.core.preferences.SharedPreferencesRepository
|
||||||
|
import app.pachli.core.testing.fakes.InMemorySharedPreferences
|
||||||
|
import at.connyduck.calladapter.networkresult.NetworkResult
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.kotlin.any
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.reset
|
||||||
|
import org.mockito.kotlin.stub
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class UpdateCheckTest {
|
||||||
|
private val fdroidService: FdroidService = mock()
|
||||||
|
private lateinit var updateCheck: UpdateCheck
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
reset(fdroidService)
|
||||||
|
updateCheck = UpdateCheck(
|
||||||
|
SharedPreferencesRepository(InMemorySharedPreferences(), TestScope()),
|
||||||
|
fdroidService,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `remoteFetchLatestVersionCode returns null on network error`() = runTest {
|
||||||
|
fdroidService.stub {
|
||||||
|
onBlocking { getPackage(any()) } doReturn NetworkResult.failure(Exception())
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(updateCheck.remoteFetchLatestVersionCode()).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `remoteFetchLatestVersionCode returns suggestedVersionCode if in packages`() = runTest {
|
||||||
|
fdroidService.stub {
|
||||||
|
onBlocking { getPackage(any()) } doReturn NetworkResult.success(
|
||||||
|
FdroidPackage(
|
||||||
|
packageName = "app.pachli",
|
||||||
|
suggestedVersionCode = 3,
|
||||||
|
packages = listOf(
|
||||||
|
FdroidPackageVersion(versionName = "3.0", versionCode = 3),
|
||||||
|
FdroidPackageVersion(versionName = "2.0", versionCode = 2),
|
||||||
|
FdroidPackageVersion(versionName = "1.0", versionCode = 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// "3" is the `suggestedVersionCode`, and is in `packages`, so should be returned.
|
||||||
|
assertThat(updateCheck.remoteFetchLatestVersionCode()).isEqualTo(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `remoteFetchLatestVersionCode returns greatest code if suggestedVersionCode is missing`() = runTest {
|
||||||
|
fdroidService.stub {
|
||||||
|
onBlocking { getPackage(any()) } doReturn NetworkResult.success(
|
||||||
|
FdroidPackage(
|
||||||
|
packageName = "app.pachli",
|
||||||
|
suggestedVersionCode = 3,
|
||||||
|
packages = listOf(
|
||||||
|
FdroidPackageVersion(versionName = "2.0", versionCode = 2),
|
||||||
|
FdroidPackageVersion(versionName = "1.0", versionCode = 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// "3" is the `suggestedVersionCode`, but is not in `packages`, so the greatest
|
||||||
|
// `versionCode` in `packages` should be returned.
|
||||||
|
assertThat(updateCheck.remoteFetchLatestVersionCode()).isEqualTo(2)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue