1package com.peachpasswords.autofill23import com.peachpasswords.vault.VaultEntry4import org.junit.Assert.assertEquals5import org.junit.Assert.assertTrue6import org.junit.Test78class AutofillMatchTest {9 private fun entry(title: String, url: String?) =10 VaultEntry(id = title, title = title, url = url, createdAt = 0, modifiedAt = 0)1112 @Test fun strict_domain_match_hits_only_the_matching_site() {13 val peach = entry("Peach", "https://peachpasswords.com/login")14 val mail = entry("Fastmail", "https://fastmail.com")15 val result = AutofillResponseBuilder.matchEntries(listOf(mail, peach), "peachpasswords.com")16 assertEquals(listOf(peach), result.hits)17 assertTrue("no unrelated entries leak for a web origin", result.rest.isEmpty())18 }1920 @Test fun no_false_positive_on_substring_lookalike() {21 val gh = entry("GitHub", "https://github.com/login")22 val result = AutofillResponseBuilder.matchEntries(listOf(gh), "notgithub.com")23 assertTrue("github.com must not match notgithub.com", result.hits.isEmpty())24 assertTrue(result.rest.isEmpty())25 }2627 @Test fun subdomain_origin_matches_registrable_domain() {28 val peach = entry("Peach", "https://peachpasswords.com")29 val result = AutofillResponseBuilder.matchEntries(listOf(peach), "app.peachpasswords.com")30 assertEquals(listOf(peach), result.hits)31 }3233 @Test fun additional_urls_match_registrable_domain() {34 val peach = entry("Peach", "https://peachpasswords.com")35 .copy(additionalUrls = listOf("https://accounts.peachpasswords.co.uk/login"))36 val result = AutofillResponseBuilder.matchEntries(listOf(peach), "signin.peachpasswords.co.uk")37 assertEquals(listOf(peach), result.hits)38 }3940 @Test fun additional_urls_do_not_match_substring_lookalikes() {41 val gh = entry("GitHub", null)42 .copy(additionalUrls = listOf("https://github.com/login"))43 val result = AutofillResponseBuilder.matchEntries(listOf(gh), "evilgithub.com")44 assertTrue(result.hits.isEmpty())45 assertTrue(result.rest.isEmpty())46 }4748 @Test fun picker_web_origin_uses_domain_matching() {49 val peach = entry("Peach", "https://peachpasswords.com")50 val parsed = ParsedStructure(51 webDomain = "app.peachpasswords.com",52 originDomains = listOf("peachpasswords.com"),53 )54 assertEquals(listOf(peach), autofillPickerEntries(listOf(peach), parsed, limit = 12))55 }5657 @Test fun picker_native_origin_matches_app_link_domains_and_title() {58 val byDomain = entry("Native", "https://example.com/login")59 val byTitle = entry("example app", "https://unrelated.test")60 val other = entry("Other", "https://other.example.net")61 val parsed = ParsedStructure(62 packageName = "com.example.app",63 appTitle = "Example App",64 originDomains = listOf("example.com"),65 )66 assertEquals(67 listOf(byDomain, byTitle),68 autofillPickerEntries(listOf(byDomain, byTitle, other), parsed, limit = 12),69 )70 }7172 @Test fun origin_without_domains_or_title_has_no_matches() {73 val app = entry("Native", "https://example.com/login")74 val parsed = ParsedStructure(packageName = "com.example.app")75 assertTrue(autofillPickerEntries(listOf(app), parsed, limit = 12).isEmpty())76 }7778 @Test fun picker_label_routes_locked_users_to_unlock() {79 assertEquals("Unlock Peach", autofillPickerLabel(unlocked = false, datasetCount = 3))80 }8182 @Test fun picker_label_names_no_match_state() {83 assertEquals("No saved logins for this site", autofillPickerLabel(unlocked = true, datasetCount = 0))84 }8586 @Test fun picker_label_keeps_search_affordance_when_matches_exist() {87 assertEquals("Search Peach…", autofillPickerLabel(unlocked = true, datasetCount = 1))88 }8990 @Test fun null_origin_yields_no_entries() {91 val a = entry("A", "https://a.com")92 val b = entry("B", "https://b.com")93 val result = AutofillResponseBuilder.matchEntries(listOf(a, b), null)94 assertTrue(result.all.isEmpty())95 }9697 @Test fun web_origin_matches_its_page_domain() {98 val bank = entry("Bank", "https://bank.example.com")99 val parsed = ParsedStructure(100 webDomain = "bank.example.com",101 originDomains = listOf("bank.example.com"),102 )103 assertEquals(listOf(bank), AutofillResponseBuilder.matchEntries(listOf(bank), parsed).all)104 }105106 @Test fun parser_identity_uses_only_framework_activity_component() {107 assertEquals(108 "com.example.host",109 authenticatedPackageName("com.example.host"),110 )111 assertEquals(null, authenticatedPackageName(null))112 }113}114