diff --git a/appstate/state.go b/appstate/state.go index 12a0d54..6533172 100644 --- a/appstate/state.go +++ b/appstate/state.go @@ -133,6 +133,52 @@ func (s *State) APITokens() ([]apitokens.Token, error) { return apitokens.Entries(model) } +func (s *State) RemoteProfiles() ([]vault.RemoteProfile, error) { + model, err := s.currentModel() + if err != nil { + return nil, err + } + profiles := slices.Clone(model.RemoteProfiles) + slices.SortFunc(profiles, func(a, b vault.RemoteProfile) int { + switch { + case a.Name < b.Name: + return -1 + case a.Name > b.Name: + return 1 + case a.ID < b.ID: + return -1 + case a.ID > b.ID: + return 1 + default: + return 0 + } + }) + return profiles, nil +} + +func (s *State) RemoteCredentialEntries() ([]vault.Entry, error) { + model, err := s.currentModel() + if err != nil { + return nil, err + } + entries := slices.Clone(model.Entries) + slices.SortFunc(entries, func(a, b vault.Entry) int { + switch { + case a.Title < b.Title: + return -1 + case a.Title > b.Title: + return 1 + case a.ID < b.ID: + return -1 + case a.ID > b.ID: + return 1 + default: + return 0 + } + }) + return entries, nil +} + func (s *State) IssueAPIToken(name, clientName string, expiresAt *time.Time, now time.Time) (apitokens.Token, string, error) { session, ok := s.Session.(MutableSession) if !ok { diff --git a/appstate/state_test.go b/appstate/state_test.go index e72265f..1da9d45 100644 --- a/appstate/state_test.go +++ b/appstate/state_test.go @@ -164,6 +164,71 @@ func TestIssueRotateDisableRevokeAndDeleteAPIToken(t *testing.T) { } } +func TestRemoteProfilesReturnsVaultProfiles(t *testing.T) { + t.Parallel() + + state := State{ + Session: stubSession{ + model: vault.Model{ + RemoteProfiles: []vault.RemoteProfile{ + { + ID: "family-webdav", + Name: "Family Vault", + Backend: vault.RemoteBackendWebDAV, + BaseURL: "https://dav.example.invalid/remote.php/dav", + Path: "files/family/keepass.kdbx", + }, + { + ID: "archive-webdav", + Name: "Archive Vault", + Backend: vault.RemoteBackendWebDAV, + BaseURL: "https://dav.example.invalid/remote.php/dav", + Path: "files/family/archive.kdbx", + }, + }, + }, + }, + } + + got, err := state.RemoteProfiles() + if err != nil { + t.Fatalf("RemoteProfiles() error = %v", err) + } + if len(got) != 2 { + t.Fatalf("len(RemoteProfiles()) = %d, want 2", len(got)) + } + if got[0].ID != "archive-webdav" || got[1].ID != "family-webdav" { + t.Fatalf("RemoteProfiles() = %#v, want sorted by name/id", got) + } +} + +func TestRemoteCredentialEntriesReturnsSortedVaultEntries(t *testing.T) { + t.Parallel() + + state := State{ + Session: stubSession{ + model: vault.Model{ + Entries: []vault.Entry{ + {ID: "cred-2", Title: "Zulu Sign-In", Username: "zuser", Path: []string{"Crew", "Internet"}}, + {ID: "cred-1", Title: "Alpha Sign-In", Username: "auser", Path: []string{"Crew", "Internet"}}, + {ID: "cred-3", Title: "Beta Sign-In", Username: "buser", Path: []string{"Crew", "Home"}}, + }, + }, + }, + } + + got, err := state.RemoteCredentialEntries() + if err != nil { + t.Fatalf("RemoteCredentialEntries() error = %v", err) + } + if len(got) != 3 { + t.Fatalf("len(RemoteCredentialEntries()) = %d, want 3", len(got)) + } + if got[0].ID != "cred-1" || got[1].ID != "cred-3" || got[2].ID != "cred-2" { + t.Fatalf("RemoteCredentialEntries() = %#v, want entries sorted by title", got) + } +} + func TestVisibleEntriesUsesGlobalSearchWhenQueryPresent(t *testing.T) { t.Parallel()