Hello world 👋
Release / Build and publish (push) Successful in 22s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kebandClaude Opus 5.5 committed 2026-10-12 08:48:23 +02:00
commit cbb155b498
130 files changed
+26734

No files matched your search

+406
View File
@@ -0,0 +1,406 @@
package app
import (
"strings"
"testing"
tea "charm.land/bubbletea/v2"
"git.kebcloud.no/keb/lazysecrets/internal/provider/fake"
"github.com/charmbracelet/x/ansi"
)
func (d *driver) openSearch() {
d.t.Helper()
d.send(tea.KeyPressMsg{Code: 's', Mod: tea.ModCtrl})
if d.m.search == nil {
d.t.Fatalf("no search popup:\n%s", d.screen())
}
}
func ctrl(r rune) tea.KeyPressMsg { return tea.KeyPressMsg{Code: r, Mod: tea.ModCtrl} }
// searchAll picks All on the left and goes back to the results.
func (d *driver) searchAll() {
d.send(ctrl('h'))
for d.m.search.src > 0 {
d.send(tea.KeyPressMsg{Code: tea.KeyUp})
}
for d.m.search.scope().id != allSources {
d.send(tea.KeyPressMsg{Code: tea.KeyDown})
}
d.send(ctrl('l'))
}
func TestSearchLoaded(t *testing.T) {
t.Parallel()
d := newDriver(t)
d.keys("j") // platform-dev: away from what's loaded
d.openSearch()
for _, want := range []string{"Sources", "Results", "All", "platform-dev"} {
if !strings.Contains(d.screen(), want) {
t.Fatalf("missing %q:\n%s", want, d.screen())
}
}
d.typeText("api-key")
if !strings.Contains(d.screen(), "no matches here · All searches everything") {
t.Errorf("no hint to search everything:\n%s", d.screen())
}
d.searchAll()
if !strings.Contains(d.screen(), "platform-prod / kv-app-prod / api-key") {
t.Fatalf("loaded secret not found:\n%s", d.screen())
}
d.keys("enter")
if src, c, s := d.selected(); src != "platform-prod" || c != "kv-app-prod" || s != "api-key" {
t.Errorf("jumped to %s/%s/%s", src, c, s)
}
if d.m.search != nil {
t.Error("popup still open")
}
}
func TestSearchLoadsAsYouType(t *testing.T) {
t.Parallel()
d := newDriver(t)
d.openSearch() // on the source the cursor is on: platform-prod
// Typing loads the scope: that source, without ctrl+a.
d.typeText("smtp")
if !strings.Contains(d.screen(), "platform-prod / kv-shared-prod / smtp-password") {
t.Fatalf("not found in a vault that wasn't loaded:\n%s", d.screen())
}
if _, ok := d.m.cache.objects["Azure|sub-prod/kv-shared-prod|Secret"]; !ok {
t.Error("loaded list not in the panes' cache")
}
if _, ok := d.m.cache.containers[sourceRowKey("Azure", "sub-dev")]; ok {
t.Error("another source loaded too")
}
// On All: everything.
d.keys("tab") // to the scopes
for d.m.search.src > 0 {
d.send(tea.KeyPressMsg{Code: tea.KeyUp})
}
d.typeText("-")
if _, ok := d.m.cache.containers[sourceRowKey("Azure", "sub-dev")]; !ok {
t.Errorf("typing on All didn't load the other sources:\n%s", d.screen())
}
if d.m.search.input.Value() != "smtp-" {
t.Error("query lost while loading")
}
if !strings.Contains(d.screen(), "unreadable") { // kv-restricted refuses
t.Errorf("unreadable vaults not counted:\n%s", d.screen())
}
// Once: the next key doesn't ask for the list that failed again;
// ctrl+a still does, on purpose.
d.typeText("p")
if d.m.crawl.running {
t.Error("typing on reloads what failed")
}
}
func TestSearchSourceNarrowsAndLoads(t *testing.T) {
t.Parallel()
d := newDriver(t)
d.openSearch()
d.keys("tab") // to Sources
sel := func() string { return d.m.search.scopes[d.m.search.src].id }
for d.m.search.src > 0 {
d.send(tea.KeyPressMsg{Code: tea.KeyUp})
}
for !strings.HasPrefix(sel(), "platform-dev") {
d.send(tea.KeyPressMsg{Code: tea.KeyDown})
if d.m.search.src == len(d.m.search.scopes)-1 {
t.Fatal("platform-dev not in sources")
}
}
if !strings.Contains(ansiStrip(d.m.search.scopes[d.m.search.src].mark), "○") {
t.Errorf("unloaded source mark %q", d.m.search.scopes[d.m.search.src].mark)
}
if len(d.m.search.shown) != 0 {
t.Errorf("results not narrowed to platform-dev: %d", len(d.m.search.shown))
}
d.send(ctrl('r'))
if !strings.Contains(ansiStrip(d.m.search.scopes[d.m.search.src].mark), "●") {
t.Errorf("loaded source mark %q", d.m.search.scopes[d.m.search.src].mark)
}
for _, o := range d.m.search.shown {
if !strings.Contains(ansiStrip(o.label), "platform-dev") {
t.Errorf("result from elsewhere: %q", ansiStrip(o.label))
}
}
if len(d.m.search.shown) == 0 {
t.Error("nothing shown after loading platform-dev")
}
if _, ok := d.m.cache.objects["Azure|sub-prod/kv-shared-prod|Secret"]; ok {
t.Error("loaded another subscription too")
}
}
func TestSearchResultsStayPut(t *testing.T) {
t.Parallel()
d := newDriver(t)
d.openSearch()
d.send(tea.KeyPressMsg{Code: tea.KeyDown})
ids := func() []string {
var out []string
for _, o := range d.m.search.shown {
out = append(out, o.id)
}
return out
}
before, at := ids(), d.m.search.shown[d.m.search.res].id
d.send(ctrl('a'))
after := ids()
if len(after) <= len(before) {
t.Fatalf("nothing loaded: %d → %d", len(before), len(after))
}
for i, id := range before {
if after[i] != id {
t.Errorf("row %d changed from %q to %q", i, id, after[i])
}
}
if got := d.m.search.shown[d.m.search.res].id; got != at {
t.Errorf("cursor moved from %q to %q", at, got)
}
}
func TestStarFromSearch(t *testing.T) {
t.Parallel()
d := newDriver(t)
d.openSearch()
d.typeText("api-key")
d.send(ctrl('f'))
if !d.m.store.Node(kvKey + "/secrets/api-key").StarredFor("Azure") {
t.Errorf("ctrl+f in search didn't star:\n%s", d.screen())
}
}
func ansiStrip(s string) string { return ansi.Strip(s) }
func TestSearchStartsWhereYouAre(t *testing.T) {
t.Parallel()
d := newDriver(t)
d.keys("2", "j") // platform-prod / kv-shared-prod
d.openSearch()
v := d.m.search
if got := ansiStrip(v.scopes[v.src].label); !strings.Contains(got, "platform-prod") {
t.Errorf("source not preselected: %q", got)
}
if v.res >= len(v.shown) || !strings.Contains(ansiStrip(v.shown[v.res].label), "kv-shared-prod") {
t.Errorf("vault not under the cursor:\n%s", d.screen())
}
// From the Sources pane: just the source.
d.keys("esc", "esc", "1", "j") // stop typing, close; platform-dev
d.openSearch()
v = d.m.search
if got := ansiStrip(v.scopes[v.src].label); !strings.Contains(got, "platform-dev") {
t.Errorf("source not preselected: %q", got)
}
}
func TestSearchHint(t *testing.T) {
t.Parallel()
d := newDriver(t)
d.openSearch()
d.searchAll()
if !strings.Contains(d.screen(), "vaults loaded · ctrl+a loads everything") {
t.Errorf("no hint on All:\n%s", d.screen())
}
d.send(ctrl('a'))
if strings.Contains(d.screen(), "ctrl+a loads everything") {
t.Errorf("hint after loading everything:\n%s", d.screen())
}
}
func TestGoToTabsAndModes(t *testing.T) {
t.Parallel()
d := newDriver(t)
d.keys("2", "f", "j", "f") // two favourites
d.keys("F")
v := d.m.search
if v.tab != tabFavs || v.typing {
t.Fatalf("F: tab %d, typing %v", v.tab, v.typing)
}
d.keys("j") // browsing: j moves, doesn't type
if v.res != 1 || v.input.Value() != "" {
t.Errorf("j: cursor %d, input %q", v.res, v.input.Value())
}
d.keys("]")
if v.tab != tabRecent {
t.Errorf("] → tab %d, want Recent", v.tab)
}
d.keys("]", "]")
if v.tab != tabFavs {
t.Errorf("] wraps to tab %d, want Favourites", v.tab)
}
d.keys("[")
if v.tab != tabSearch || !strings.Contains(d.screen(), "Scope") {
t.Errorf("[ → tab %d, want Search:\n%s", v.tab, d.screen())
}
d.keys("/")
d.typeText("shared")
if !v.typing || len(v.shown) == 0 {
t.Fatalf("typing: %v, %d results", v.typing, len(v.shown))
}
d.keys("esc") // clears, back to browsing
if v.typing || v.input.Value() != "" || d.m.search == nil {
t.Errorf("esc while typing: typing %v, input %q, open %v", v.typing, v.input.Value(), d.m.search != nil)
}
d.keys("esc") // closes
if d.m.search != nil {
t.Error("esc while browsing didn't close")
}
}
func TestSearchHighlightsMatches(t *testing.T) {
t.Parallel()
// Icons: labels start with a multi-byte glyph, where byte offsets and
// characters differ.
d := newDriverOpts(t, Options{Icons: true})
d.openSearch()
d.typeText("dbpw")
if len(d.m.search.shown) == 0 {
t.Fatal("no results")
}
o := d.m.search.shown[0]
plain := []rune(ansiStrip(o.label))
var got string
for _, h := range o.hits {
got += string(plain[h])
}
if got != "dbpw" {
t.Errorf("hits %v spell %q in %q", o.hits, got, string(plain))
}
with := d.m.searchViewBox()
for i := range d.m.search.shown {
d.m.search.shown[i].hits = nil
}
if with == d.m.searchViewBox() {
t.Error("matched characters not styled")
}
}
func TestHelpBarFollowsPopup(t *testing.T) {
t.Parallel()
d := newDriver(t)
d.keys("F")
bar := lastLine(d.screen())
for _, want := range []string{"[ prev tab", "] next tab", "/ filter", "f star"} {
if !strings.Contains(bar, want) {
t.Errorf("browsing: help bar %q lacks %q", bar, want)
}
}
d.keys("/")
if bar := lastLine(d.screen()); !strings.Contains(bar, "ctrl+f star") || !strings.Contains(bar, "esc stop typing") {
t.Errorf("typing: help bar %q", bar)
}
}
func TestHelpFromPopups(t *testing.T) {
t.Parallel()
d := newDriver(t)
d.keys("F", "?")
if d.m.prompt == nil || d.m.prompt.title != "Keybindings" || d.m.prompt.options[0].label != "Go to" {
t.Fatalf("? in Go to:\n%s", d.screen())
}
if !strings.Contains(d.screen(), "switch list") { // tab: not on the help bar
t.Errorf("Go to's keys missing:\n%s", d.screen())
}
var sections []string
for _, o := range d.m.prompt.options {
if o.header {
sections = append(sections, o.label)
}
if strings.Contains(ansiStrip(o.label), "copy value") {
t.Errorf("pane key listed in Go to's help: %q", ansiStrip(o.label))
}
}
if strings.Join(sections, ",") != "Go to,While typing, after /" {
t.Errorf("sections %v", sections)
}
d.keys("esc") // back to Go to, where it was
if d.m.search == nil || d.m.search.tab != tabFavs || d.m.prompt != nil {
t.Errorf("not back in Go to: search %v, prompt %v", d.m.search != nil, d.m.prompt != nil)
}
// Picking a key in the list runs it in Go to.
d.keys("?")
d.typeText("next tab")
d.keys("enter")
if d.m.search == nil || d.m.search.tab != tabRecent {
t.Errorf("running ] from ? didn't switch Go to's tab")
}
// Menus too.
d.keys("esc", "3", "S", "?")
if d.m.prompt == nil || d.m.prompt.options[0].label != "This menu" {
t.Fatalf("? in a menu:\n%s", d.screen())
}
for _, o := range d.m.prompt.options {
if o.header && o.label != "This menu" {
t.Errorf("menu help has section %q", o.label)
}
}
d.keys("esc")
if d.m.prompt == nil || !strings.HasPrefix(d.m.prompt.title, "Share") {
t.Errorf("not back in the share menu:\n%s", d.screen())
}
}
func TestHelpRunsTypingKeysInGoTo(t *testing.T) {
t.Parallel()
d := newDriver(t)
d.keys("ctrl+s", "esc") // Go to on Search, browsing
before := d.m.search.results
d.keys("?")
d.typeText("ctrl+h") // "sources", a typing key
d.keys("enter")
if d.m.search == nil || !d.m.search.typing || d.m.search.results || !before {
t.Errorf("typing key from ? didn't run: typing %v, results %v", d.m.search != nil && d.m.search.typing, d.m.search.results)
}
}
func TestGoToHelpBarEndsWithHelp(t *testing.T) {
t.Parallel()
d := newDriver(t) // 100 columns: Go to's keys don't all fit
d.keys("ctrl+s", "esc")
bar := strings.TrimSpace(lastLine(d.screen()))
if !strings.HasSuffix(bar, "? help") || !strings.Contains(bar, "…") {
t.Errorf("help bar %q", bar)
}
}
func TestLoadWithEmptyResults(t *testing.T) {
t.Parallel()
c := &counting{Provider: fake.Azure(0)}
d := newDriverOpts(t, Options{}, c)
d.openSearch()
d.send(ctrl('h')) // pick platform-dev, never opened, in the scopes
for d.m.search.src > 0 {
d.send(tea.KeyPressMsg{Code: tea.KeyUp})
}
for i := 0; !strings.Contains(ansiStrip(d.m.search.scope().label), "platform-dev"); i++ {
if i > len(d.m.search.scopes) {
var labels []string
for _, o := range d.m.search.scopes {
labels = append(labels, ansiStrip(o.label))
}
t.Fatalf("no platform-dev scope: %q (at %d, results %v)", labels, d.m.search.src, d.m.search.results)
}
d.send(tea.KeyPressMsg{Code: tea.KeyDown})
}
d.send(ctrl('l'))
if len(d.m.search.shown) != 0 || !d.m.search.results {
t.Fatalf("want empty results with the cursor there: %d shown", len(d.m.search.shown))
}
before := c.containers
d.send(ctrl('r')) // in the results, nothing selected: the source
if c.containers == before {
t.Error("ctrl+r with empty results didn't load the source")
}
if len(d.m.search.shown) == 0 {
t.Errorf("nothing shown after loading:\n%s", d.screen())
}
}