1package com.peachpasswords.vault23import kotlinx.coroutines.runBlocking4import org.junit.Assert.assertEquals5import org.junit.Assert.assertTrue6import org.junit.Test7891011121314class VaultKeyRotationHooksTest {1516 private class MemStorage : VaultStorage {17 var slotA: StoredVault? = null18 var slotB: String? = null19 private var ver: Long = 020 private var settings = VaultSettings()21 override fun loadSlotA() = slotA22 override fun hasVault() = slotA != null23 override fun writeSlotA(slot: StoredVault): Boolean { slotA = slot; return true }24 override fun loadSlotB() = slotB25 override fun writeSlotB(blobBase64: String): Boolean { slotB = blobBase64; return true }26 var decoy: StoredVault? = null27 override fun loadDecoySlot() = decoy28 override fun writeDecoySlot(slot: StoredVault): Boolean { decoy = slot; return true }29 override fun hasDecoySlot() = decoy != null30 override fun clearDecoySlot() { decoy = null }31 override fun version() = ver32 override fun stampLocalMutation(): Long { ver = maxOf(System.currentTimeMillis(), ver + 1); return ver }33 override fun loadSettings() = settings34 override fun writeSettings(s: VaultSettings): Boolean { settings = s; return true }35 var history = VaultHistory()36 override fun loadVaultHistory() = history37 override fun writeVaultHistory(h: VaultHistory): Boolean { history = h; return true }38 override fun clearVaultHistory() { history = VaultHistory() }39 override fun clearVault(): Boolean { slotA = null; slotB = null; decoy = null; ver = 040 settings = VaultSettings(); history = VaultHistory(); return true }41 }4243 @Test fun every_rotation_site_fires_the_escrow_signal() = runBlocking {44 val repo = VaultRepository(MemStorage())45 var firings = 046 VaultKeyRotationHooks.onRotated = { firings++ }47 try {48 assertTrue(repo.createVault("first-password") is CreateVaultResult.Success)49 assertEquals("creating a vault is not a rotation", 0, firings)5051 assertTrue(repo.changePassword("first-password", "second-password"))52 assertEquals("changePassword must invalidate both escrows", 1, firings)5354 55 56 57 val words = List(12) { "not-a-real-word" }58 assertTrue(repo.resetPasswordWithRecoveryWords(words, "third-password"))59 assertEquals("recovery reset must invalidate both escrows", 2, firings)60 } finally {61 VaultKeyRotationHooks.onRotated = {}62 }63 }64}