1package com.peachpasswords.sync23import org.junit.Assert.assertEquals4import org.junit.Assert.assertFalse5import org.junit.Assert.assertNull6import org.junit.Assert.assertTrue7import org.junit.Test89class SyncAlertTest {10 @Test fun routine_entry_activity_is_not_a_user_facing_alert() {11 assertFalse(isDeviceTrustAlert(SyncAlert(SyncAlertType.REMOTE_PASSWORD_CHANGE, timestamp = 1L)))12 assertFalse(isDeviceTrustAlert(SyncAlert(SyncAlertType.REMOTE_DELETE, timestamp = 2L)))13 assertTrue(isDeviceTrustAlert(SyncAlert(SyncAlertType.DEVICE_KEY_ROTATED, timestamp = 3L)))14 assertTrue(isDeviceTrustAlert(SyncAlert(SyncAlertType.DEVICE_REVOKED, timestamp = 4L)))15 }1617 @Test fun device_key_rotation_detects_existing_peer_key_changes() {18 val existing = PairedDevice(19 deviceId = "browser",20 deviceName = "Browser",21 deviceType = "extension",22 longTermPublicKeyHex = "aa".repeat(32),23 signingPublicKeyHex = "bb".repeat(32),24 pairedAt = 1L,25 )26 val incoming = ManifestDeviceEntry(27 deviceId = "browser",28 deviceName = "Browser",29 deviceType = "extension",30 longTermPublicKeyHex = "aa".repeat(32),31 signingPublicKeyHex = "cc".repeat(32),32 pairedAt = 1L,33 )3435 val alert = deviceKeyRotationAlert(existing, incoming, nowMs = 99L)3637 assertEquals(SyncAlertType.DEVICE_KEY_ROTATED, alert?.type)38 assertEquals("Browser", alert?.deviceName)39 assertNull(deviceKeyRotationAlert(existing, incoming.copy(signingPublicKeyHex = "bb".repeat(32)), nowMs = 99L))40 }4142 @Test fun `S5 rotation alert names the device from local state not the manifest`() {43 44 45 val existing = PairedDevice(46 deviceId = "browser",47 deviceName = "Spare Tablet",48 deviceType = "extension",49 longTermPublicKeyHex = "aa".repeat(32),50 signingPublicKeyHex = "bb".repeat(32),51 pairedAt = 1L,52 )53 val incoming = ManifestDeviceEntry(54 deviceId = "browser",55 deviceName = "June's Primary Phone",56 deviceType = "extension",57 longTermPublicKeyHex = "aa".repeat(32),58 signingPublicKeyHex = "cc".repeat(32),59 pairedAt = 1L,60 )6162 val alert = deviceKeyRotationAlert(existing, incoming, nowMs = 99L)6364 assertEquals("Spare Tablet", alert?.deviceName)65 }6667 @Test fun system_notification_excludes_routine_entry_activity() {68 val content = syncSecurityNotificationContent(69 listOf(70 SyncAlert(71 SyncAlertType.REMOTE_PASSWORD_CHANGE,72 entryTitle = "Email",73 deviceName = "Phone",74 timestamp = 1L,75 ),76 SyncAlert(77 SyncAlertType.DEVICE_REVOKED,78 deviceName = "Chrome",79 timestamp = 2L,80 ),81 ),82 )8384 assertEquals("Peach Sync security", content?.title)85 assertEquals(listOf("Chrome revoked a paired device"), content?.lines)86 assertEquals("Chrome revoked a paired device", content?.summary)87 }8889 @Test fun multiple_security_changes_use_a_compact_notification_summary() {90 val content = syncSecurityNotificationContent(91 listOf(92 SyncAlert(SyncAlertType.DEVICE_REVOKED, deviceName = "Chrome", timestamp = 1L),93 SyncAlert(SyncAlertType.DEVICE_KEY_ROTATED, deviceName = "Phone", timestamp = 2L),94 ),95 )9697 assertEquals(2, content?.lines?.size)98 assertEquals("2 device security updates", content?.summary)99 }100}