1package com.peachpasswords.vault23import org.junit.Assert.assertEquals4import org.junit.Assert.assertFalse5import org.junit.Assert.assertTrue6import org.junit.Test78class VaultHealthAdvancedTest {910 private val now = 1_000_000_000_000L11 private val day = 86_400_000L1213 private fun entry(14 id: String,15 url: String? = "https://example.com",16 password: String? = "Str0ng!Passw0rd", 17 totp: String? = "otpauth://totp/x?secret=JBSWY3DPEHPK3PXP",18 breach: Int? = null,19 passwordChangedAt: Long? = now,20 createdAt: Long = now,21 type: String? = "login",22 deletedAt: Long? = null,23 ) = VaultEntry(24 id = id,25 title = "Entry $id",26 type = type,27 url = url,28 password = password,29 totp = totp,30 breach = breach,31 passwordChangedAt = passwordChangedAt,32 createdAt = createdAt,33 modifiedAt = createdAt,34 deletedAt = deletedAt,35 )3637 @Test fun reused_passwords_form_one_group_and_score() {38 val a = entry("a", url = "https://one.com", password = "Sh4red!Password")39 val b = entry("b", url = "https://two.com", password = "Sh4red!Password")40 val report = VaultHealth.buildAdvancedReport(listOf(a, b), now)41 assertEquals(1, report.reusedGroups)42 val byId = report.remediation.associateBy { it.id }43 assertTrue(byId["a"]!!.factors.reused)44 assertTrue(byId["b"]!!.factors.reused)45 assertEquals(25, byId["a"]!!.score)46 assertTrue(byId["a"]!!.reasons.contains("Reused"))47 }4849 @Test fun weak_short_password_flagged() {50 val report = VaultHealth.buildAdvancedReport(listOf(entry("w", password = "short")), now)51 val r = report.remediation.single()52 assertTrue(r.factors.weak)53 assertTrue(r.reasons.contains("Weak"))54 assertEquals(20, r.score)55 }5657 @Test fun non_sensitive_ages_at_180_days() {58 val fresh = entry("fresh", password = "Fresh!Passw0rd1", passwordChangedAt = now - 100 * day)59 val stale = entry("stale", password = "Stale!Passw0rd2", passwordChangedAt = now - 200 * day)60 val report = VaultHealth.buildAdvancedReport(listOf(fresh, stale), now)61 assertEquals(1, report.agedCount)62 val staleReport = report.remediation.first { it.id == "stale" }63 assertTrue(staleReport.factors.aged)64 assertEquals(200L, staleReport.ageDays)65 assertTrue(staleReport.reasons.contains("200 days old"))66 assertEquals(15, staleReport.score)67 }6869 @Test fun sensitive_domain_ages_at_90_days() {70 71 val bank = entry("bank", url = "https://mybank.com", passwordChangedAt = now - 100 * day)72 val r = VaultHealth.buildAdvancedReport(listOf(bank), now).remediation.single()73 assertTrue(r.factors.aged)74 assertFalse(r.factors.missingTotpOnSensitive) 75 assertEquals(15, r.score)76 }7778 @Test fun sensitive_without_totp_adds_reason() {79 val bank = entry("bank", url = "https://mybank.com", totp = null)80 val r = VaultHealth.buildAdvancedReport(listOf(bank), now).remediation.single()81 assertTrue(r.factors.missingTotpOnSensitive)82 assertTrue(r.reasons.contains("No 2FA on sensitive site"))83 assertEquals(10, r.score)84 }8586 @Test fun breached_carries_exposure_count() {87 val r = VaultHealth.buildAdvancedReport(listOf(entry("x", breach = 3)), now).remediation.single()88 assertTrue(r.factors.breached)89 assertEquals(3, r.factors.breachCount)90 assertTrue(r.reasons.contains("Breached (3 exposures)"))91 assertEquals(60, r.score)92 }9394 @Test fun score_caps_at_100() {95 96 val a = entry("a", password = "weak", breach = 1, passwordChangedAt = now - 400 * day)97 val b = entry("b", password = "weak") 98 val report = VaultHealth.buildAdvancedReport(listOf(a, b), now)99 assertEquals(100, report.remediation.first { it.id == "a" }.score)100 }101102 @Test fun remediation_sorted_desc_and_excludes_zero() {103 val breached = entry("hi", password = "Hi!Passw0rd\$trong1", breach = 5)104 val weak = entry("lo", password = "weak")105 val healthy = entry("ok", password = "Ok!Passw0rd\$trong2") 106 val report = VaultHealth.buildAdvancedReport(listOf(weak, breached, healthy), now)107 assertEquals(listOf("hi", "lo"), report.remediation.map { it.id })108 assertTrue(report.remediation.none { it.id == "ok" })109 }110111 @Test fun deleted_and_non_login_excluded_from_count() {112 val entries = listOf(113 entry("live"),114 entry("gone", deletedAt = now),115 entry("card", type = "card"),116 )117 assertEquals(1, VaultHealth.buildAdvancedReport(entries, now).entryCount)118 }119120 @Test fun vault_score_weights_sensitive_double() {121 val bank = entry("bank", url = "https://mybank.com", totp = null) 122 val weak = entry("weak", password = "weak") 123 val report = VaultHealth.buildAdvancedReport(listOf(bank, weak), now)124 assertEquals(13, report.vaultScore) 125 }126127 @Test fun empty_vault_yields_zero() {128 val report = VaultHealth.buildAdvancedReport(emptyList(), now)129 assertEquals(0, report.vaultScore)130 assertEquals(0, report.entryCount)131 assertTrue(report.remediation.isEmpty())132 }133}