|
| 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