CardSerializationTest.kt

3.4 KB · raw

1package com.peachpasswords.vault23import kotlinx.coroutines.runBlocking4import kotlinx.serialization.json.Json5import org.junit.Assert.assertEquals6import org.junit.Assert.assertFalse7import org.junit.Assert.assertTrue8import org.junit.Test910/** In-memory [VaultStorage] for exercising last4 derivation on save (MF-3). */11private class CardFakeStorage : VaultStorage {12    private var slotA: StoredVault? = null13    private var slotB: String? = null14    private var ver: Long = 015    private var settings = VaultSettings()16    override fun loadSlotA() = slotA17    override fun hasVault() = slotA != null18    override fun writeSlotA(slot: StoredVault): Boolean { slotA = slot; return true }19    override fun writeSlotB(blobBase64: String): Boolean { slotB = blobBase64; return true }20    private var decoy: StoredVault? = null21    override fun loadDecoySlot() = decoy22    override fun writeDecoySlot(slot: StoredVault): Boolean { decoy = slot; return true }23    override fun hasDecoySlot() = decoy != null24    override fun clearDecoySlot() { decoy = null }25    override fun version() = ver26    override fun stampLocalMutation(): Long { ver = maxOf(System.currentTimeMillis(), ver + 1); return ver }27    override fun loadSettings() = settings28    override fun writeSettings(s: VaultSettings): Boolean { settings = s; return true }29    override fun clearVault(): Boolean { slotA = null; slotB = null; ver = 0; settings = VaultSettings(); return true }30}3132class CardSerializationTest {33    private val json = Json { encodeDefaults = true }3435    @Test fun card_network_serializes_as_browser_key() {36        val card = VaultEntry(37            id = "c1", type = "card", title = "Visa", cardNumber = "4242424242424242",38            cardNetwork = "visa", last4 = "4242", createdAt = 0, modifiedAt = 0,39        )40        val encoded = json.encodeToString(VaultEntry.serializer(), card)41        assertTrue("serializes cardNetwork as \"network\"", encoded.contains("\"network\":\"visa\""))42        assertTrue("serializes last4 explicitly", encoded.contains("\"last4\":\"4242\""))43        assertFalse("never emits the Android-only key name", encoded.contains("cardNetwork"))44    }4546    @Test fun network_and_last4_round_trip_from_browser_keys() {47        val wire = """{"id":"c1","title":"Visa","type":"card","network":"visa","last4":"4242","createdAt":0,"modifiedAt":0}"""48        val decoded = json.decodeFromString(VaultEntry.serializer(), wire)49        assertEquals("visa", decoded.cardNetwork)50        assertEquals("4242", decoded.last4)51    }5253    @Test fun last4_is_derived_on_save() = runBlocking {54        val repo = VaultRepository(CardFakeStorage())55        repo.createVault("correct horse battery staple")56        val saved = repo.addEntry(57            VaultEntry(id = "", type = "card", title = "My Card", cardNumber = "4111 1111 1111 1234", createdAt = 0, modifiedAt = 0),58        )59        assertEquals("derives last4 from the PAN on save", "1234", saved.last4)6061        val updated = repo.updateEntry(saved.copy(cardNumber = "5555 5555 5555 4321"))62        assertEquals("re-derives last4 on update", "4321", updated.last4)63    }6465    @Test fun non_card_entry_has_no_last4() = runBlocking {66        val repo = VaultRepository(CardFakeStorage())67        repo.createVault("correct horse battery staple")68        val saved = repo.addEntry(VaultEntry(id = "", type = "login", title = "GitHub", password = "pw", createdAt = 0, modifiedAt = 0))69        assertEquals(null, saved.last4)70    }71}