mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-02-05 12:42:34 -05:00
Merge branch 'countryCode' into 'master'
Rename location to countryCode See merge request fdroid/fdroidclient!1464
This commit is contained in:
1098
libs/database/schemas/org.fdroid.database.FDroidDatabaseInt/8.json
Normal file
1098
libs/database/schemas/org.fdroid.database.FDroidDatabaseInt/8.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user