From 231b67bee849eefb4f6bc28ace39f0caa1440b23 Mon Sep 17 00:00:00 2001 From: Rishabh Bhatia Date: Tue, 19 Nov 2024 22:35:57 +1100 Subject: [PATCH] Fix: Use SqlCipherDeletingErrorHandler in JobDatabase for corruption handling Previously, JobDatabase used SqlCipherErrorHandler, which did not delete corrupted `signal-jobmanager.db`. This fix ensures that SqlCipherDeletingErrorHandler is used, which deletes the database upon corruption. Closes #13762 --- .../securesms/database/JobDatabase.kt | 2 +- .../securesms/database/JobDatabaseTest.kt | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 app/src/test/java/org/thoughtcrime/securesms/database/JobDatabaseTest.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.kt index b5f5a9efede..73a52c7f2ef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.kt @@ -43,7 +43,7 @@ class JobDatabase( null, DATABASE_VERSION, 0, - SqlCipherErrorHandler(DATABASE_NAME), + SqlCipherDeletingErrorHandler(DATABASE_NAME), SqlCipherDatabaseHook(), true ), diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/JobDatabaseTest.kt b/app/src/test/java/org/thoughtcrime/securesms/database/JobDatabaseTest.kt new file mode 100644 index 00000000000..5d939ab8e31 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/database/JobDatabaseTest.kt @@ -0,0 +1,63 @@ +import android.app.Application +import io.mockk.every +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.unmockkObject +import io.mockk.verify +import net.zetetic.database.sqlcipher.SQLiteDatabase +import net.zetetic.database.sqlcipher.SQLiteOpenHelper +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.thoughtcrime.securesms.crypto.DatabaseSecret +import org.thoughtcrime.securesms.database.JobDatabase +import org.thoughtcrime.securesms.database.SqlCipherDeletingErrorHandler +import org.thoughtcrime.securesms.dependencies.AppDependencies + +class JobDatabaseTest { + + private lateinit var mockApplication: Application + private lateinit var mockDatabase: SQLiteDatabase + private lateinit var sqlCipherDeletingErrorHandler: SqlCipherDeletingErrorHandler + private lateinit var databaseSecret: DatabaseSecret + + @Before + fun setUp() { + mockApplication = mockk(relaxed = true) + + // Set _application field in AppDependencies using reflection + val applicationField = AppDependencies::class.java.getDeclaredField("_application") + applicationField.isAccessible = true + applicationField.set(null, mockApplication) + + databaseSecret = mockk(relaxed = true) + mockDatabase = mockk(relaxed = true) + every { mockDatabase.rawQuery(any(), any()) } returns mockk() + sqlCipherDeletingErrorHandler = spyk(SqlCipherDeletingErrorHandler("signal-jobmanager.db")) + every { mockApplication.deleteDatabase("signal-jobmanager.db") } returns true + } + + @After + fun tearDown() { + unmockkObject(AppDependencies) + } + + @Test + fun `onCorruption deletes database on corruption event`() { + sqlCipherDeletingErrorHandler.onCorruption(mockDatabase, "Database corrupted!") + verify { mockApplication.deleteDatabase("signal-jobmanager.db") } + } + + @Test + fun `JobDatabase initializes with SqlCipherDeletingErrorHandler`() { + val jobDatabase = JobDatabase(mockApplication, databaseSecret) + + // Use reflection to access the private errorHandler field + val errorHandlerField = SQLiteOpenHelper::class.java.getDeclaredField("mErrorHandler") + errorHandlerField.isAccessible = true + val errorHandler = errorHandlerField.get(jobDatabase) + assert(errorHandler is SqlCipherDeletingErrorHandler) { + "Expected SqlCipherDeletingErrorHandler, but got ${errorHandler?.javaClass?.name}" + } + } +}