Merge branch 'countryCode' into 'master'

Rename location to countryCode

See merge request fdroid/fdroidclient!1464
This commit is contained in:
Torsten Grote
2024-11-08 14:14:24 +00:00
13 changed files with 1258 additions and 17 deletions

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,99 @@
package org.fdroid.database
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import androidx.room.Room
import androidx.room.testing.MigrationTestHelper
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
private const val TEST_DB = "migration-test"
@RunWith(AndroidJUnit4::class)
internal class CountryCodeMigrationTest {
@get:Rule
val helper: MigrationTestHelper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(),
FDroidDatabaseInt::class.java,
listOf(CountryCodeMigration()),
FrameworkSQLiteOpenHelperFactory(),
)
private val repo = InitialRepository(
name = "F-Droid",
address = "https://f-droid.org/repo",
description = "The official F-Droid Free Software repository. " +
"Everything in this repository is always built from the source code.",
certificate = "3082035e30820246a00302010202044c49cd00300d06092a864886f70d010105",
version = 13L,
enabled = true,
weight = 1,
)
@Test
fun migrateCountryCode() {
helper.createDatabase(TEST_DB, 7).use { db ->
// Database has schema version 7. Insert some data using SQL queries.
// We can't use DAO classes because they expect the latest schema.
val repoId = db.insert(
CoreRepository.TABLE,
SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
put("name", Converters.localizedTextV2toString(mapOf("en-US" to repo.name)))
put(
"description",
Converters.localizedTextV2toString(mapOf("en-US" to repo.description))
)
put("address", repo.address)
put("timestamp", 42)
put("certificate", repo.certificate)
})
db.insert(
RepositoryPreferences.TABLE,
SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
put("repoId", repoId)
put("enabled", repo.enabled)
put("lastETag", "foo")
put("weight", repo.weight)
})
db.insert(
Mirror.TABLE,
SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
put("repoId", repoId)
put("url", "foo")
put("location", "bar")
})
}
// Re-open the database with version 8, auto-migrations are applied automatically
helper.runMigrationsAndValidate(TEST_DB, 8, true).close()
// now get the Room DB, so we can use our DAOs for verifying the migration
Room.databaseBuilder(
ApplicationProvider.getApplicationContext(),
FDroidDatabaseInt::class.java,
TEST_DB
)
.addMigrations(MIGRATION_2_3, MIGRATION_5_6)
.allowMainThreadQueries()
.build().use { db ->
// check repo got timestamp and etag reset
val repos = db.getRepositoryDao().getRepositories()
assertEquals(1, repos.size)
assertEquals(null, repos[0].lastETag)
assertEquals(-1, repos[0].timestamp)
// check mirror
assertEquals(1, repos[0].mirrors.size)
val mirror = repos[0].mirrors[0]
assertEquals(repos[0].repoId, mirror.repoId)
assertEquals("foo", mirror.url)
assertEquals("bar", mirror.countryCode)
}
}
}

View File

@@ -7,6 +7,7 @@ import kotlinx.serialization.json.jsonObject
import org.fdroid.database.TestUtils.assertRepoEquals
import org.fdroid.index.v2.AntiFeatureV2
import org.fdroid.index.v2.CategoryV2
import org.fdroid.index.v2.MirrorV2
import org.fdroid.index.v2.ReleaseChannelV2
import org.fdroid.index.v2.RepoV2
import org.fdroid.test.DiffUtils.applyDiff
@@ -61,7 +62,8 @@ internal class RepositoryDiffTest : DbTest() {
val updateTimestamp = Random.nextLong()
val json = """
{
"timestamp": $updateTimestamp
"timestamp": $updateTimestamp,
"unknown": "field"
}""".trimIndent()
// decode diff from JSON and update DB with it
@@ -97,6 +99,22 @@ internal class RepositoryDiffTest : DbTest() {
}
}
@Test
fun mirrorUnknownKeyDiff() {
val repo = getRandomRepo()
val json = """
{
"mirrors": [
{ "url": "foo", "countryCode": "bar", "unknown": "doesntexist" }
]
}""".trimIndent()
testDiff(repo, json) { repos ->
val expectedMirrors = setOf(Mirror(repos[0].repoId, "foo", "bar"))
assertEquals(expectedMirrors, repos[0].mirrors.toSet())
assertRepoEquals(repo.copy(mirrors = listOf(MirrorV2("foo", "bar"))), repos[0])
}
}
@Test
fun descriptionDiff() {
val repo = getRandomRepo().copy(description = mapOf("de" to "foo", "en" to "bar"))

View File

@@ -16,7 +16,7 @@ import java.util.concurrent.Callable
// When bumping this version, please make sure to add one (or more) migration(s) below!
// Consider also providing tests for that migration.
// Don't forget to commit the new schema to the git repo as well.
version = 7,
version = 8,
entities = [
// repo
CoreRepository::class,
@@ -49,7 +49,8 @@ import java.util.concurrent.Callable
AutoMigration(4, 5),
// 5 to 6 is a manual migration
AutoMigration(6, 7),
// add future migrations here (if they are easy enough to be done automatically)
AutoMigration(7, 8, CountryCodeMigration::class),
// add future migrations above!
],
)
@TypeConverters(Converters::class)

View File

@@ -3,6 +3,7 @@ package org.fdroid.database
import android.content.ContentValues
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL
import androidx.room.RenameColumn
import androidx.room.migration.AutoMigrationSpec
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
@@ -134,3 +135,27 @@ internal val MIGRATION_5_6 = object : Migration(5, 6) {
db.execSQL("INSERT INTO AppMetadataFts(AppMetadataFts) VALUES('rebuild')")
}
}
/**
* Somebody changed the initial IndexV2 definition of MirrorV2.location to MirrorV2.countryCode
* in fdroidserver and doesn't want to undo this rename.
* So now we need to handle this in the client to be in line with the index format produced.
*/
@RenameColumn(
tableName = Mirror.TABLE,
fromColumnName = "location",
toColumnName = "countryCode",
)
internal class CountryCodeMigration : AutoMigrationSpec {
override fun onPostMigrate(db: SupportSQLiteDatabase) {
// reset timestamps and etags so next repo updates pull full index, refresh all data
db.beginTransaction()
try {
db.execSQL("UPDATE ${CoreRepository.TABLE} SET timestamp = -1")
db.execSQL("UPDATE ${RepositoryPreferences.TABLE} SET lastETag = NULL")
db.setTransactionSuccessful()
} finally {
db.endTransaction()
}
}
}

View File

@@ -232,7 +232,7 @@ public data class Repository internal constructor(
internal data class Mirror(
val repoId: Long,
val url: String,
val location: String? = null,
val countryCode: String? = null,
) {
internal companion object {
const val TABLE = "Mirror"
@@ -240,14 +240,14 @@ internal data class Mirror(
fun toDownloadMirror(): org.fdroid.download.Mirror = org.fdroid.download.Mirror(
baseUrl = url,
location = location,
countryCode = countryCode,
)
}
internal fun MirrorV2.toMirror(repoId: Long) = Mirror(
repoId = repoId,
url = url,
location = location,
countryCode = countryCode,
)
/**

View File

@@ -8,7 +8,7 @@ import mu.KotlinLogging
public data class Mirror @JvmOverloads constructor(
val baseUrl: String,
val location: String? = null,
val countryCode: String? = null,
/**
* If this is true, this as an IPFS HTTP gateway that only accepts CIDv1 and not regular paths.
* So use this mirror only, if you have a CIDv1 available for supplying it to [getUrl].

View File

@@ -108,7 +108,7 @@ public typealias LocalizedFileListV2 = Map<String, List<FileV2>>
@Serializable
public data class MirrorV2(
val url: String,
val location: String? = null,
val countryCode: String? = null,
)
@Serializable

View File

@@ -27,11 +27,11 @@
"mirrors": [
{
"url": "https://max-v1.com",
"location": "us"
"countryCode": "us"
},
{
"url": "https://max-v1.org",
"location": "nl"
"countryCode": "nl"
}
],
"timestamp": 9223372036854775807,

View File

@@ -25,11 +25,11 @@
"mirrors": [
{
"url": "https://max-v1.com",
"location": "us"
"countryCode": "us"
},
{
"url": "https://max-v1.org",
"location": "nl"
"countryCode": "nl"
}
],
"timestamp": 9223372036854775807,

View File

@@ -25,11 +25,11 @@
"mirrors": [
{
"url": "https://max-v1.com",
"location": "us"
"countryCode": "us"
},
{
"url": "https://max-v1.org",
"location": "nl"
"countryCode": "nl"
}
],
"timestamp": 9223372036854775807,

View File

@@ -26,11 +26,11 @@
"mirrors": [
{
"url": "https://max-v1.com",
"location": "us"
"countryCode": "us"
},
{
"url": "https://max-v1.org",
"location": "nl"
"countryCode": "nl"
}
],
"timestamp": 9223372036854775807,

View File

@@ -16,7 +16,7 @@ object TestRepoUtils {
fun getRandomMirror(): MirrorV2 = MirrorV2(
url = getRandomString(),
location = getRandomString().orNull()
countryCode = getRandomString().orNull()
)
fun getRandomLocalizedTextV2(size: Int = Random.nextInt(0, 23)): LocalizedTextV2 =