1package com.peachpasswords.vault23import org.junit.Assert.assertEquals4import org.junit.Assert.assertFalse5import org.junit.Assert.assertTrue6import org.junit.Test78910111213class VaultHealthTest {1415 private val DAY = 24L * 60 * 60 * 100016 private val now = System.currentTimeMillis()1718 private fun login(19 id: String,20 password: String? = "Str0ng-Pass!xyz",21 url: String? = null,22 breach: Int? = null,23 totp: String? = null,24 changedAt: Long? = null,25 ) = VaultEntry(26 id = id, type = "login", title = id, password = password, url = url,27 breach = breach, totp = totp, passwordChangedAt = changedAt,28 createdAt = 0, modifiedAt = 0,29 )3031 private fun scoreOf(report: VaultHealth.VaultHealthReport, id: String) =32 report.entries.first { it.entryId == id }3334 3536 @Test fun breached_entry_flagged_and_counted() {37 val r = VaultHealth.buildReport(listOf(login("a", breach = 3)))38 assertTrue(scoreOf(r, "a").isBreach)39 assertEquals(1, r.breachCount)40 }4142 @Test fun unbreached_entry_not_flagged() {43 val r = VaultHealth.buildReport(listOf(login("a", breach = 0), login("b")))44 assertFalse(scoreOf(r, "a").isBreach)45 assertEquals(0, r.breachCount)46 }4748 4950 @Test fun reused_password_flagged_on_both_entries() {51 val r = VaultHealth.buildReport(listOf(login("a", password = "Shared-Pass!1"), login("b", password = "Shared-Pass!1")))52 assertTrue(scoreOf(r, "a").isReused)53 assertTrue(scoreOf(r, "b").isReused)54 assertEquals(2, r.reusedCount)55 }5657 @Test fun unique_passwords_not_reused() {58 val r = VaultHealth.buildReport(listOf(login("a", password = "Uniq-Pass!1"), login("b", password = "Other-Pass!2")))59 assertFalse(scoreOf(r, "a").isReused)60 assertEquals(0, r.reusedCount)61 }6263 6465 @Test fun short_password_is_weak() {66 assertTrue(scoreOf(VaultHealth.buildReport(listOf(login("a", password = "Ab1!"))), "a").isWeak)67 }6869 @Test fun low_char_class_password_is_weak() {70 71 assertTrue(scoreOf(VaultHealth.buildReport(listOf(login("a", password = "abcdefghijklmnop"))), "a").isWeak)72 }7374 @Test fun strong_password_not_weak() {75 assertFalse(scoreOf(VaultHealth.buildReport(listOf(login("a", password = "Str0ng-Pass!xyz"))), "a").isWeak)76 }7778 7980 @Test fun old_password_is_aged() {81 val r = VaultHealth.buildReport(listOf(login("a", changedAt = now - 200 * DAY)))82 assertTrue(scoreOf(r, "a").isAged)83 assertEquals(1, r.agedCount)84 }8586 @Test fun recent_password_not_aged() {87 val r = VaultHealth.buildReport(listOf(login("a", changedAt = now - 5 * DAY)))88 assertFalse(scoreOf(r, "a").isAged)89 }9091 @Test fun sensitive_entry_has_tighter_age_threshold() {92 93 val sensitive = login("s", url = "https://mybank.com", changedAt = now - 100 * DAY)94 val standard = login("n", url = "https://example.com", changedAt = now - 100 * DAY)95 val r = VaultHealth.buildReport(listOf(sensitive, standard))96 assertTrue(scoreOf(r, "s").isAged)97 assertFalse(scoreOf(r, "n").isAged)98 }99100 101102 @Test fun sensitive_domain_detected() {103 assertTrue(scoreOf(VaultHealth.buildReport(listOf(login("a", url = "https://gmail.com"))), "a").isSensitive)104 }105106 @Test fun non_sensitive_domain_not_flagged() {107 assertFalse(scoreOf(VaultHealth.buildReport(listOf(login("a", url = "https://example.com"))), "a").isSensitive)108 }109110 @Test fun sensitive_without_totp_flags_missing_totp() {111 val r = VaultHealth.buildReport(listOf(login("a", url = "https://gmail.com")))112 assertTrue(scoreOf(r, "a").missingTotp)113 }114115 @Test fun sensitive_with_totp_does_not_flag_missing_totp() {116 val r = VaultHealth.buildReport(listOf(login("a", url = "https://gmail.com", totp = "JBSWY3DPEHPK3PXP")))117 assertFalse(scoreOf(r, "a").missingTotp)118 }119120 121122 @Test fun all_healthy_vault_scores_zero() {123 val r = VaultHealth.buildReport(listOf(124 login("a", password = "Str0ng-Pass!xyz", url = "https://example.com", changedAt = now),125 login("b", password = "An0ther-Pass!qq", url = "https://other.example.com", changedAt = now),126 ))127 assertEquals(0, r.vaultScore)128 assertEquals(0, r.breachCount + r.reusedCount + r.agedCount + r.weakCount)129 }130131 @Test fun empty_vault_scores_zero() {132 assertEquals(0, VaultHealth.buildReport(emptyList()).vaultScore)133 }134135 @Test fun breach_dominates_entry_score() {136 137 val s = scoreOf(VaultHealth.buildReport(listOf(login("a", breach = 1))), "a")138 assertTrue(s.score >= 60)139 assertTrue(s.score <= 100)140 }141142 @Test fun non_login_and_deleted_entries_excluded() {143 val card = VaultEntry(id = "c", type = "card", title = "Visa", cardNumber = "4242", createdAt = 0, modifiedAt = 0)144 val note = VaultEntry(id = "n", type = "note", title = "memo", createdAt = 0, modifiedAt = 0)145 val deleted = login("d", breach = 9).copy(deletedAt = now)146 val live = login("l")147 val r = VaultHealth.buildReport(listOf(card, note, deleted, live))148 assertEquals(1, r.entries.size)149 assertEquals("l", r.entries.first().entryId)150 assertEquals(0, r.breachCount) 151 }152153 @Test fun sensitive_entries_weigh_double_in_vault_score() {154 155 val healthy = login("h", password = "Str0ng-Pass!xyz", url = "https://example.com", changedAt = now)156 val bad = login("b", password = "abc", url = "https://mybank.com", breach = 5, changedAt = now - 400 * DAY)157 val r = VaultHealth.buildReport(listOf(healthy, bad))158 159 assertEquals(66, r.vaultScore)160 }161}