package com.peachpasswords.autofill import com.peachpasswords.vault.VaultEntry import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test class AutofillPickerSearchTest { private fun entry( title: String, username: String? = null, email: String? = null, url: String? = null, ) = VaultEntry( id = title, title = title, username = username, email = email, url = url, createdAt = 0, modifiedAt = 0, ) @Test fun empty_query_matches_every_entry() { val gh = entry("GitHub", username = "june", url = "https://github.com") val fb = entry("Fastmail", email = "june@fastmail.com") assertTrue(matchesPickerSearch(gh, "")) assertTrue(matchesPickerSearch(fb, "")) } @Test fun whitespace_only_query_does_not_match_arbitrary_text() { // The function itself is exact-empty-or-substring; the picker uses // `isBlank()` upstream to decide between matched vs vault-wide search. // A whitespace-only query therefore should not claim a match unless // the entry literally contains whitespace in one of its fields. val gh = entry("GitHub", username = "june") assertFalse(matchesPickerSearch(gh, " ")) } @Test fun matches_title_case_insensitively() { val gh = entry("GitHub", username = "june") assertTrue(matchesPickerSearch(gh, "github")) assertTrue(matchesPickerSearch(gh, "GITHUB")) assertTrue(matchesPickerSearch(gh, "git")) } @Test fun matches_username_and_email() { val u = entry("Acme", username = "june.doe") val e = entry("Acme 2", email = "june@fastmail.com") assertTrue(matchesPickerSearch(u, "doe")) assertTrue(matchesPickerSearch(e, "fastmail")) } @Test fun matches_url_substring() { val gh = entry("GitHub", url = "https://github.com/june") assertTrue(matchesPickerSearch(gh, "github")) } @Test fun rejects_when_no_field_matches() { val gh = entry("GitHub", username = "june", url = "https://github.com") assertFalse(matchesPickerSearch(gh, "fastmail")) assertFalse(matchesPickerSearch(gh, "x")) } @Test fun null_fields_are_skipped_not_crashed() { val bare = entry("Bare Title") // title still matches, nothing else is required assertTrue(matchesPickerSearch(bare, "bare")) assertFalse(matchesPickerSearch(bare, "nope")) } @Test fun pickerEntries_filter_pipeline_drops_unmatched_and_keeps_order() { val entries = listOf( entry("GitHub", username = "june", url = "https://github.com"), entry("Fastmail", email = "june@fastmail.com"), entry("Peach", url = "https://peachpasswords.com"), ) val filtered = entries.filter { matchesPickerSearch(it, "github") } assertEquals(listOf("GitHub"), filtered.map { it.title }) } @Test fun malformed_and_local_manual_origins_never_match() { val entry = entry("Bank", url = "https://bank.example.com/login") listOf( "", "localhost", "http://localhost:8080/login", "127.0.0.1", "https://127.0.0.1/login", "https://[::1]/login", "https://", "not a domain", ).forEach { origin -> assertTrue( "origin '$origin' must fail closed", AutofillResponseBuilder.matchEntries(listOf(entry), origin).all.isEmpty(), ) } } @Test fun web_origin_preselects_matching_entries_by_domain() { val entry = entry("Bank", url = "https://bank.example.com/login") val parsed = ParsedStructure( webDomain = "bank.example.com", originDomains = listOf("bank.example.com"), ) assertEquals(listOf(entry), autofillPickerEntries(listOf(entry), parsed, limit = 12)) } @Test fun picker_search_locates_any_entry_via_substring() { // The picker's search field locates any vault entry by a substring of // title/username/email/url. val entries = listOf( entry("GitHub", username = "june", url = "https://github.com"), entry("Fastmail", email = "june@fastmail.com"), entry("Peach", url = "https://peachpasswords.com"), ) assertEquals(listOf("GitHub"), entries.filter { matchesPickerSearch(it, "git") }.map { it.title }) assertEquals(listOf("Fastmail"), entries.filter { matchesPickerSearch(it, "fastmail") }.map { it.title }) assertEquals(listOf("Peach"), entries.filter { matchesPickerSearch(it, "peach") }.map { it.title }) assertEquals( listOf("GitHub", "Fastmail"), entries.filter { matchesPickerSearch(it, "june") }.map { it.title }, ) } }