Skip to content

Commit bcf574c

Browse files
committed
test(persistence): cover the v29 feed-clearing migration
Folds in the standalone migration work from #1284, which duplicated the 28 -> 29 clear this branch already carries. Adds TransactionHistoryMigrationTest, following the hand-rolled pattern in UserProfileMigrationTest: asserts the feed is emptied, that a neighbouring table survives, and that it's a no-op on an already-empty feed. Also switches Migration28To29 to the onPostMigrate(SQLiteConnection) overload. Room's SupportSQLiteDatabase variant only fires when the database is built without a driver, so a future setDriver would turn the delete into a silent no-op.
1 parent e3a38a2 commit bcf574c

2 files changed

Lines changed: 93 additions & 2 deletions

File tree

‎apps/flipcash/shared/persistence/db/src/main/kotlin/com/flipcash/app/persistence/FlipcashDatabase.kt‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import androidx.room.RoomDatabase
1010
import androidx.room.TypeConverters
1111
import androidx.room.migration.AutoMigrationSpec
1212
import androidx.room.migration.Migration
13+
import androidx.sqlite.SQLiteConnection
1314
import androidx.sqlite.db.SupportSQLiteDatabase
15+
import androidx.sqlite.execSQL
1416
import kotlinx.coroutines.flow.MutableStateFlow
1517
import kotlinx.coroutines.flow.StateFlow
1618
import kotlinx.coroutines.flow.asStateFlow
@@ -199,10 +201,14 @@ abstract class FlipcashDatabase : RoomDatabase() {
199201
* Clearing the cache is the repair. The next sync sees an empty table, falls into its
200202
* descending re-seed, and re-fetches the recent feed from scratch; paging refills older
201203
* history on demand.
204+
*
205+
* Takes the [SQLiteConnection] overload rather than the [SupportSQLiteDatabase] one used by
206+
* [Migration22To23]: the latter only fires when Room is built without a driver, so a future
207+
* `setDriver` would turn the delete into a silent no-op.
202208
*/
203209
class Migration28To29 : AutoMigrationSpec {
204-
override fun onPostMigrate(db: SupportSQLiteDatabase) {
205-
db.execSQL("DELETE FROM messages")
210+
override fun onPostMigrate(connection: SQLiteConnection) {
211+
connection.execSQL("DELETE FROM messages")
206212
}
207213
}
208214

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.flipcash.app.persistence
2+
3+
import android.content.Context
4+
import androidx.sqlite.db.SupportSQLiteDatabase
5+
import androidx.sqlite.db.SupportSQLiteOpenHelper
6+
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
7+
import androidx.sqlite.driver.SupportSQLiteConnection
8+
import androidx.test.core.app.ApplicationProvider
9+
import org.junit.Test
10+
import org.junit.runner.RunWith
11+
import org.robolectric.RobolectricTestRunner
12+
import kotlin.test.assertEquals
13+
14+
/**
15+
* Covers the v29 upgrade dropping the cached transaction history.
16+
*
17+
* The feed is a re-fetchable cache ([FlipcashDatabase.Migration28To29]), so the contract is simply
18+
* "after the upgrade, `messages` is empty and every other table is untouched".
19+
*/
20+
@RunWith(RobolectricTestRunner::class)
21+
class TransactionHistoryMigrationTest {
22+
23+
private val context = ApplicationProvider.getApplicationContext<Context>()
24+
25+
private fun openV28Database(): SupportSQLiteDatabase {
26+
val callback = object : SupportSQLiteOpenHelper.Callback(1) {
27+
override fun onCreate(db: SupportSQLiteDatabase) = Unit
28+
override fun onUpgrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) = Unit
29+
}
30+
val config = SupportSQLiteOpenHelper.Configuration.builder(context)
31+
.name(null) // in-memory
32+
.callback(callback)
33+
.build()
34+
val db = FrameworkSQLiteOpenHelperFactory().create(config).writableDatabase
35+
db.execSQL(
36+
"CREATE TABLE messages (idBase58 TEXT NOT NULL, text TEXT NOT NULL, " +
37+
"amountUsdc INTEGER, amountNative INTEGER, nativeCurrency TEXT, rate REAL, " +
38+
"state TEXT NOT NULL, timestamp INTEGER NOT NULL, metadata TEXT, " +
39+
"mintBase58 TEXT DEFAULT 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', " +
40+
"text_substitutions TEXT, PRIMARY KEY(idBase58))"
41+
)
42+
// A neighbouring table, to pin down that the migration only clears the feed.
43+
db.execSQL(
44+
"CREATE TABLE blocked_users (user_id_hex TEXT NOT NULL, " +
45+
"blocked_at_epoch_ms INTEGER NOT NULL, PRIMARY KEY(user_id_hex))"
46+
)
47+
return db
48+
}
49+
50+
private fun SupportSQLiteDatabase.count(table: String): Int =
51+
query("SELECT COUNT(*) FROM $table").use { it.moveToFirst(); it.getInt(0) }
52+
53+
private fun SupportSQLiteDatabase.insertMessage(id: String, timestamp: Long) = execSQL(
54+
"INSERT INTO messages (idBase58, text, state, timestamp) VALUES (?,?,?,?)",
55+
arrayOf<Any?>(id, "sent \$1.00", "COMPLETED", timestamp),
56+
)
57+
58+
@Test
59+
fun `migration 28 to 29 drops the cached transaction history`() {
60+
val db = openV28Database()
61+
db.insertMessage("msgA", 1L)
62+
db.insertMessage("msgB", 2L)
63+
db.execSQL("INSERT INTO blocked_users VALUES (?,?)", arrayOf<Any?>("u1", 111L))
64+
assertEquals(2, db.count("messages"))
65+
66+
FlipcashDatabase.Migration28To29().onPostMigrate(SupportSQLiteConnection(db))
67+
68+
assertEquals(0, db.count("messages"))
69+
// Only the feed is cleared — unrelated caches survive the upgrade.
70+
assertEquals(1, db.count("blocked_users"))
71+
72+
db.close()
73+
}
74+
75+
@Test
76+
fun `migration 28 to 29 is a no-op on an already-empty feed`() {
77+
val db = openV28Database()
78+
79+
FlipcashDatabase.Migration28To29().onPostMigrate(SupportSQLiteConnection(db))
80+
81+
assertEquals(0, db.count("messages"))
82+
83+
db.close()
84+
}
85+
}

0 commit comments

Comments
 (0)