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

Fix migrations #133

Merged
merged 16 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions app/schemas/at.bitfire.icsdroid.db.AppDatabase/2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "d61bf6fb08b622a180a1933b983faae2",
"entities": [
{
"tableName": "subscriptions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `calendarId` INTEGER, `url` TEXT NOT NULL, `eTag` TEXT, `displayName` TEXT NOT NULL, `lastModified` INTEGER, `lastSync` INTEGER, `errorMessage` TEXT, `ignoreEmbeddedAlerts` INTEGER NOT NULL, `defaultAlarmMinutes` INTEGER, `color` INTEGER)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "calendarId",
"columnName": "calendarId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "eTag",
"columnName": "eTag",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "displayName",
"columnName": "displayName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastModified",
"columnName": "lastModified",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastSync",
"columnName": "lastSync",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "errorMessage",
"columnName": "errorMessage",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ignoreEmbeddedAlerts",
"columnName": "ignoreEmbeddedAlerts",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "defaultAlarmMinutes",
"columnName": "defaultAlarmMinutes",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "color",
"columnName": "color",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "credentials",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`subscriptionId` INTEGER NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, PRIMARY KEY(`subscriptionId`), FOREIGN KEY(`subscriptionId`) REFERENCES `subscriptions`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "subscriptionId",
"columnName": "subscriptionId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"subscriptionId"
]
},
"indices": [],
"foreignKeys": [
{
"table": "subscriptions",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"subscriptionId"
],
"referencedColumns": [
"id"
]
}
]
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd61bf6fb08b622a180a1933b983faae2')"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,25 @@ import androidx.room.Room
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import androidx.work.Configuration
import androidx.work.Data
import androidx.work.ListenableWorker.Result
import androidx.work.testing.SynchronousExecutor
import androidx.work.testing.TestListenableWorkerBuilder
import androidx.work.testing.WorkManagerTestInitHelper
import at.bitfire.ical4android.AndroidCalendar
import at.bitfire.ical4android.util.MiscUtils.ContentProviderClientHelper.closeCompat
import at.bitfire.icsdroid.AppAccount
import at.bitfire.icsdroid.Constants.TAG
import at.bitfire.icsdroid.SyncWorker
import at.bitfire.icsdroid.calendar.LocalCalendar
import at.bitfire.icsdroid.db.AppDatabase
import at.bitfire.icsdroid.db.CalendarCredentials
import at.bitfire.icsdroid.db.LocalCalendar
import at.bitfire.icsdroid.db.dao.CredentialsDao
import at.bitfire.icsdroid.db.dao.SubscriptionsDao
import at.bitfire.icsdroid.db.entity.Subscription
import kotlinx.coroutines.runBlocking
import org.junit.*
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.*

class CalendarToRoomMigrationTest {

Expand Down Expand Up @@ -104,12 +106,14 @@ class CalendarToRoomMigrationTest {
Calendars.NAME to CALENDAR_URL
)
)
val calendarId = ContentUris.parseId(uri)
Log.i(TAG, "Created test calendar $calendarId")

val calendar = AndroidCalendar.findByID(
account,
provider,
LocalCalendar.Factory,
ContentUris.parseId(uri)
calendarId
)

// associate credentials, too
Expand All @@ -119,20 +123,27 @@ class CalendarToRoomMigrationTest {
}

@Test
fun testSubscriptionCreated() {
val worker = TestListenableWorkerBuilder<SyncWorker>(
context = appContext
).build()

fun testMigrateFromV2_0_3() {
// prepare: create local calendar without subscription
val calendar = createCalendar()
assertFalse(calendar.isManagedByDB())

try {
runBlocking {
val result = worker.doWork()
assertEquals(result, Result.success())

val subscription = subscriptionsDao.getAll().first()
// check that the calendar has been added to the subscriptions list
assertEquals(calendar.id, subscription.id)
// run worker
val result = TestListenableWorkerBuilder<SyncWorker>(appContext)
.setInputData(Data.Builder()
.putBoolean(SyncWorker.ONLY_MIGRATE, true)
.build())
.build().doWork()
assertEquals(Result.success(), result)

// check that calendar is marked as "managed by DB" so that it won't be migrated again
assertTrue(calendar.isManagedByDB())

// check that the subscription has been added
val subscription = subscriptionsDao.getByCalendarId(calendar.id)!!
assertEquals(calendar.id, subscription.calendarId)
assertEquals(CALENDAR_DISPLAY_NAME, subscription.displayName)
assertEquals(Uri.parse(CALENDAR_URL), subscription.url)

Expand All @@ -146,4 +157,38 @@ class CalendarToRoomMigrationTest {
}
}

@Test
fun testMigrateFromV2_1() {
// prepare: create local calendar plus subscription with subscription.id = LocalCalendar.id,
// but with calendarId=null and COLUMN_MANAGED_BY_DB=null
val calendar = createCalendar()
assertFalse(calendar.isManagedByDB())

val oldSubscriptionId = subscriptionsDao.add(Subscription.fromLegacyCalendar(calendar).copy(id = calendar.id, calendarId = null))

try {
runBlocking {
// run worker
val result = TestListenableWorkerBuilder<SyncWorker>(appContext)
.setInputData(Data.Builder()
.putBoolean(SyncWorker.ONLY_MIGRATE, true)
.build())
.build().doWork()
assertEquals(Result.success(), result)

// check that calendar is marked as "managed by DB" so that it won't be migrated again
assertTrue(calendar.isManagedByDB())

// check that the subscription has been added
val subscription = subscriptionsDao.getByCalendarId(calendar.id)!!
assertEquals(oldSubscriptionId, subscription.id)
assertEquals(calendar.id, subscription.calendarId)
assertEquals(CALENDAR_DISPLAY_NAME, subscription.displayName)
assertEquals(Uri.parse(CALENDAR_URL), subscription.url)
}
} finally {
calendar.delete()
}
}

}
4 changes: 2 additions & 2 deletions app/src/main/java/at/bitfire/icsdroid/ProcessEventsTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import android.util.Log
import androidx.core.app.NotificationCompat
import at.bitfire.ical4android.Event
import at.bitfire.icsdroid.db.AppDatabase
import at.bitfire.icsdroid.db.LocalCalendar
import at.bitfire.icsdroid.db.LocalEvent
import at.bitfire.icsdroid.calendar.LocalCalendar
import at.bitfire.icsdroid.calendar.LocalEvent
import at.bitfire.icsdroid.db.entity.Subscription
import at.bitfire.icsdroid.ui.EditCalendarActivity
import at.bitfire.icsdroid.ui.NotificationUtils
Expand Down
Loading