Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 058de773 authored by Rafael Tonholo's avatar Rafael Tonholo Committed by GitHub
Browse files

Merge pull request #9826 from rafaeltonholo/fix/9825/fix-app-crash

fix: db migration to version 90 crashing the app and causing database wipe
parents 1877d84d b2033cbb
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ package net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import net.thunderbird.core.logging.Logger
import net.thunderbird.feature.mail.folder.api.FOLDER_DEFAULT_PATH_DELIMITER
import net.thunderbird.feature.mail.folder.api.Folder
import net.thunderbird.feature.mail.folder.api.FolderPathDelimiter
import net.thunderbird.feature.mail.folder.api.FolderType
@@ -28,7 +29,7 @@ internal class GetDisplayTreeFolder(
            )
        }

        val pathDelimiter = folders.first().pathDelimiter
        val pathDelimiter = folders.firstOrNull()?.pathDelimiter ?: FOLDER_DEFAULT_PATH_DELIMITER
        val accountFolders = folders.filterIsInstance<MailDisplayFolder>().map {
            val path = flattenPath(it.folder.name, pathDelimiter, maxDepth)
            logger.debug { "Flattened path for ${it.folder.name} → $path" }
+19 −14
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import net.thunderbird.core.android.account.LegacyAccountDto
import net.thunderbird.core.common.mail.Protocols
import net.thunderbird.core.logging.Logger
import net.thunderbird.core.logging.legacy.Log
import okio.IOException
import org.intellij.lang.annotations.Language
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@@ -55,11 +56,6 @@ internal class MigrationTo90(
        try {
            logger.verbose(TAG) { "fetching IMAP prefix" }
            imapStore.fetchImapPrefix()
        } catch (e: AuthenticationFailedException) {
            logger.warn(TAG, e) { "failed to fetch IMAP prefix. skipping db migration" }
            return
        }

            val imapPrefix = imapStore.combinedPrefix

            if (imapPrefix?.isNotBlank() == true) {
@@ -71,6 +67,15 @@ internal class MigrationTo90(
            }

            logger.verbose(TAG) { "completed db migration to version 90 for account ${account.uuid}" }
        } catch (e: AuthenticationFailedException) {
            logger.warn(TAG, e) {
                "failed to fetch IMAP prefix due to authentication error. skipping db migration"
            }
        } catch (e: IOException) {
            logger.warn(TAG, e) {
                "failed to fetch IMAP prefix due to network error. skipping db migration"
            }
        }
    }

    private fun createImapStore(account: LegacyAccountDto): ImapStore {
+43 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ import com.fsck.k9.mail.store.imap.ImapStoreSettings
import com.fsck.k9.mailstore.MigrationsHelper
import com.fsck.k9.storage.messages.createFolder
import com.fsck.k9.storage.messages.readFolders
import java.io.IOException
import net.thunderbird.core.android.account.LegacyAccountDto
import net.thunderbird.core.common.mail.Protocols
import net.thunderbird.core.logging.legacy.Log
@@ -220,7 +221,7 @@ class MigrationTo90Test : KoinTest {
    }

    @Test
    fun `given an imap account - when can't fetch imap prefix during the migration - migration should not execute sql queries`() {
    fun `given an imap account - when can't fetch imap prefix during the migration due to authentication error - migration should not execute sql queries`() {
        // Arrange
        populateDatabase()
        val prefix = "INBOX."
@@ -256,6 +257,43 @@ class MigrationTo90Test : KoinTest {
            }
    }

    @Test
    fun `given an imap account - when can't fetch imap prefix during the migration due to network error - migration should not execute sql queries`() {
        // Arrange
        populateDatabase()
        val prefix = "INBOX."
        val imapStore = createImapStoreSpy(
            imapPrefix = prefix,
            folderPathDelimiter = ".",
            ioException = IOException("failed to fetch"),
        )
        val incomingServerSettings = createIncomingServerSettings()
        val account = createAccount(incomingServerSettings)
        val migrationHelper = createMigrationsHelper(account)
        val dbSpy = spy<SQLiteDatabase> { database }
        val migration = MigrationTo90(
            db = dbSpy,
            migrationsHelper = migrationHelper,
            imapStoreFactory = createImapStoreFactory(imapStore),
        )
        val expected = database.readFolders().map { it.serverId }
        val updateQuery = migration.buildQuery(imapPrefix = prefix)

        // Act
        migration.removeImapPrefixFromFolderServerId()
        val actual = database.readFolders().mapNotNull { it.serverId }
        testLogger.dumpLogs()

        // Assert
        verify(imapStore, times(1)).fetchImapPrefix()
        verify(dbSpy, times(0)).execSQL(updateQuery)
        assertThat(actual)
            .all {
                hasSize(expected.size)
                isEqualTo(expected)
            }
    }

    private fun createIncomingServerSettings(
        protocolType: String = Protocols.IMAP,
        authType: AuthType = AuthType.NONE,
@@ -308,6 +346,7 @@ class MigrationTo90Test : KoinTest {
        imapPrefix: String? = null,
        folderPathDelimiter: FolderPathDelimiter = FOLDER_DEFAULT_PATH_DELIMITER,
        authenticationFailedException: AuthenticationFailedException? = null,
        ioException: IOException? = null,
    ): ImapStore = spy<ImapStore> {
        on { this.combinedPrefix } doReturn imapPrefix
            ?.takeIf { it.isNotBlank() }
@@ -317,6 +356,9 @@ class MigrationTo90Test : KoinTest {
            if (authenticationFailedException != null) {
                throw authenticationFailedException
            }
            if (ioException != null) {
                throw ioException
            }
        }
    }