Skip to content

Commit

Permalink
Fix: Use SqlCipherDeletingErrorHandler in JobDatabase for corruption …
Browse files Browse the repository at this point in the history
…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 signalapp#13762
  • Loading branch information
rishabh-bhatia committed Nov 19, 2024
1 parent 9b36c62 commit 231b67b
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class JobDatabase(
null,
DATABASE_VERSION,
0,
SqlCipherErrorHandler(DATABASE_NAME),
SqlCipherDeletingErrorHandler(DATABASE_NAME),
SqlCipherDatabaseHook(),
true
),
Expand Down
Original file line number Diff line number Diff line change
@@ -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}"
}
}
}

0 comments on commit 231b67b

Please sign in to comment.