1package com.peachpasswords.autofill23import com.peachpasswords.vault.VaultEntry4import org.junit.Assert.assertEquals5import org.junit.Assert.assertFalse6import org.junit.Assert.assertTrue7import org.junit.Test89class AutofillMatchTest {10 private fun entry(title: String, url: String?) =11 VaultEntry(id = title, title = title, url = url, createdAt = 0, modifiedAt = 0)1213 @Test fun strict_domain_match_hits_only_the_matching_site() {14 val peach = entry("Peach", "https://peachpasswords.com/login")15 val mail = entry("Fastmail", "https://fastmail.com")16 val result = AutofillResponseBuilder.matchEntries(listOf(mail, peach), "peachpasswords.com")17 assertEquals(listOf(peach), result.hits)18 assertTrue("no unrelated entries leak for a web origin", result.rest.isEmpty())19 }2021 @Test fun no_false_positive_on_substring_lookalike() {22 val gh = entry("GitHub", "https://github.com/login")23 val result = AutofillResponseBuilder.matchEntries(listOf(gh), "notgithub.com")24 assertTrue("github.com must not match notgithub.com", result.hits.isEmpty())25 assertTrue(result.rest.isEmpty())26 }2728 @Test fun subdomain_origin_matches_registrable_domain() {29 val peach = entry("Peach", "https://peachpasswords.com")30 val result = AutofillResponseBuilder.matchEntries(listOf(peach), "app.peachpasswords.com")31 assertEquals(listOf(peach), result.hits)32 }3334 @Test fun additional_urls_match_registrable_domain() {35 val peach = entry("Peach", "https://peachpasswords.com")36 .copy(additionalUrls = listOf("https://accounts.peachpasswords.co.uk/login"))37 val result = AutofillResponseBuilder.matchEntries(listOf(peach), "signin.peachpasswords.co.uk")38 assertEquals(listOf(peach), result.hits)39 }4041 @Test fun additional_urls_do_not_match_substring_lookalikes() {42 val gh = entry("GitHub", null)43 .copy(additionalUrls = listOf("https://github.com/login"))44 val result = AutofillResponseBuilder.matchEntries(listOf(gh), "evilgithub.com")45 assertTrue(result.hits.isEmpty())46 assertTrue(result.rest.isEmpty())47 }4849 @Test fun picker_web_origin_uses_domain_matching() {50 val peach = entry("Peach", "https://peachpasswords.com")51 val parsed = ParsedStructure(webDomain = "app.peachpasswords.com")52 assertEquals(listOf(peach), autofillPickerEntries(listOf(peach), parsed, limit = 12))53 }5455 @Test fun picker_native_origin_requires_exact_package_match() {56 val app = entry("Native", "com.example.app")57 val other = entry("Other", "com.example.other")58 val parsed = ParsedStructure(packageName = "com.example.app")59 assertEquals(listOf(app), autofillPickerEntries(listOf(app, other), parsed, limit = 12))60 }6162 @Test fun picker_native_origin_rejects_package_substrings() {63 val app = entry("Native", "com.example.app")64 val parsed = ParsedStructure(packageName = "evil.com.example.app")65 assertTrue(autofillPickerEntries(listOf(app), parsed, limit = 12).isEmpty())66 }6768 @Test fun picker_label_routes_locked_users_to_unlock() {69 assertEquals("Unlock Peach", autofillPickerLabel(unlocked = false, datasetCount = 3))70 }7172 @Test fun picker_label_names_no_match_state() {73 assertEquals("No saved logins for this site", autofillPickerLabel(unlocked = true, datasetCount = 0))74 }7576 @Test fun picker_label_keeps_search_affordance_when_matches_exist() {77 assertEquals("Search Peach…", autofillPickerLabel(unlocked = true, datasetCount = 1))78 }7980 @Test fun null_origin_yields_no_hits_only_fallback() {81 val a = entry("A", "https://a.com")82 val b = entry("B", "https://b.com")83 val result = AutofillResponseBuilder.matchEntries(listOf(a, b), null)84 assertTrue(result.hits.isEmpty())85 assertEquals(listOf(a, b), result.rest)86 }8788 @Test fun trusted_browser_allowlist() {89 assertTrue(TrustedBrowsers.isTrusted("com.android.chrome"))90 assertTrue(TrustedBrowsers.isTrusted("org.mozilla.firefox"))91 assertFalse(TrustedBrowsers.isTrusted("com.evil.phisher"))92 assertFalse(TrustedBrowsers.isTrusted(null))93 }94}