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:
Nik Clayton 2024-05-30 13:49:42 +02:00 committed by GitHub
parent de7e5a9df9
commit 61afde882c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 112 additions and 1 deletions

View File

@ -34,6 +34,19 @@ class UpdateCheck @Inject constructor(
}
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
}
}

View File

@ -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)
}
}