Improve autofill status feedback

This commit is contained in:
Joe Julian
2026-04-01 17:12:28 -07:00
parent dba8786e43
commit 7918afcf43
6 changed files with 441 additions and 11 deletions
+30 -11
View File
@@ -28,10 +28,29 @@ type File struct {
Entries []Entry `json:"entries"`
}
type MatchStatus string
const (
MatchStatusNone MatchStatus = ""
MatchStatusFound MatchStatus = "found"
MatchStatusAmbiguous MatchStatus = "ambiguous"
MatchStatusMissing MatchStatus = "missing"
)
type MatchResult struct {
Status MatchStatus `json:"status"`
Entry Entry `json:"entry,omitempty"`
}
func Match(cache File, webURL string) (Entry, bool) {
result := Resolve(cache, webURL)
return result.Entry, result.Status == MatchStatusFound
}
func Resolve(cache File, webURL string) MatchResult {
target := normalizeURL(webURL)
if target.host == "" {
return Entry{}, false
return MatchResult{Status: MatchStatusMissing}
}
exactHost := make([]Entry, 0)
@@ -46,8 +65,8 @@ func Match(cache File, webURL string) (Entry, bool) {
}
}
if matched, ok := chooseEntry(target, exactHost); ok {
return matched, true
if result := chooseEntry(target, exactHost); result.Status != MatchStatusMissing {
return result
}
return chooseEntry(target, parentHost)
}
@@ -152,12 +171,12 @@ func cleanPath(path string) string {
return path
}
func chooseEntry(target normalizedTarget, entries []Entry) (Entry, bool) {
func chooseEntry(target normalizedTarget, entries []Entry) MatchResult {
switch len(entries) {
case 0:
return Entry{}, false
return MatchResult{Status: MatchStatusMissing}
case 1:
return entries[0], true
return MatchResult{Status: MatchStatusFound, Entry: entries[0]}
}
exact := make([]Entry, 0)
@@ -181,18 +200,18 @@ func chooseEntry(target normalizedTarget, entries []Entry) (Entry, bool) {
}
}
if len(exact) == 1 {
return exact[0], true
return MatchResult{Status: MatchStatusFound, Entry: exact[0]}
}
if len(exact) > 1 {
return Entry{}, false
return MatchResult{Status: MatchStatusAmbiguous}
}
if len(bestPrefix) == 1 {
return bestPrefix[0], true
return MatchResult{Status: MatchStatusFound, Entry: bestPrefix[0]}
}
if len(bestPrefix) == 0 {
return Entry{}, false
return MatchResult{Status: MatchStatusMissing}
}
return Entry{}, false
return MatchResult{Status: MatchStatusAmbiguous}
}
func collectTargets(item vault.Entry) []string {
+43
View File
@@ -156,6 +156,49 @@ func TestMatchRejectsAmbiguousSharedHost(t *testing.T) {
}
}
func TestResolveReportsFoundAmbiguousAndMissingStatuses(t *testing.T) {
t.Parallel()
cache := File{
Entries: []Entry{
{
ID: "one",
Title: "Admin Login",
Username: "admin",
Password: "secret1",
URL: "https://example.com/admin",
Host: "example.com",
},
{
ID: "two",
Title: "Shared Login A",
Username: "shared-a",
Password: "secret2",
URL: "https://shared.example.com",
Host: "shared.example.com",
},
{
ID: "three",
Title: "Shared Login B",
Username: "shared-b",
Password: "secret3",
URL: "https://shared.example.com",
Host: "shared.example.com",
},
},
}
if got := Resolve(cache, "https://example.com/admin/login"); got.Status != MatchStatusFound || got.Entry.ID != "one" {
t.Fatalf("Resolve(found) = %#v, want found entry one", got)
}
if got := Resolve(cache, "https://shared.example.com"); got.Status != MatchStatusAmbiguous {
t.Fatalf("Resolve(ambiguous) = %#v, want ambiguous", got)
}
if got := Resolve(cache, "https://nowhere.invalid"); got.Status != MatchStatusMissing {
t.Fatalf("Resolve(missing) = %#v, want missing", got)
}
}
func TestMatchChoosesLongestPathPrefix(t *testing.T) {
t.Parallel()