`lazysecrets list` prints every source, vault and secret lazysecrets can find, one per line, for fzf, editor pickers and scripts: what Go to searches, without the TUI. What's cached prints at once; what's missing or older than `--max-age` loads in the background and streams in. It stops when the reader goes (a pick in fzf), saving what it loaded. - Text: kind, mark (✗/⚠, ★, ◷ and the provider icon), name, status (why it couldn't be read, or expired / expires in 12d), note, account and link, tab-separated; kind first and account and link last, so new fields go between. Coloured from the theme; `--color never`, `--icons=false`, `--stars=false`, `--recent`, `--notes=false`, `--expiry=false`. - `--format json`: an object per line, with starred, recent, lastUsed and uses, expires, enabled, note, reference and url too. - `--kind` lists only some kinds, and doesn't load lists only other kinds need; `--cached` loads nothing. - `lazysecrets open --action go|copy|show` skips the "what to do" menu and ends in the list, like Go to; `open` and `get` take `--account`, the account list printed, rather than asking which. - The cache file moved to `internal/cachefile`: saves merge with the file under a lock, so list and the TUI don't undo each other. - `ui.recentIcon` marks items used lately with ◷ in the TUI's lists too; Recent now means the last 90 days. - --demo has stars, notes, recent use, and expired, expiring and disabled secrets. - docs/integration.md documents get, list and an fzf picker; docs/links.md is docs/sharing.md. Reviewed-on: #70 Co-authored-by: Karl-Einar Bergstrøm <karl-einarb@outlook.com>
123 lines
3.5 KiB
Go
123 lines
3.5 KiB
Go
package cachefile
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.kebcloud.no/keb/lazysecrets/internal/provider"
|
|
)
|
|
|
|
func list(at time.Time, names ...string) Stamped[[]provider.Container] {
|
|
var cs []provider.Container
|
|
for _, n := range names {
|
|
cs = append(cs, provider.Container{Name: n})
|
|
}
|
|
return Stamped[[]provider.Container]{At: at, Value: cs}
|
|
}
|
|
|
|
func TestSaveMerges(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "cache.json")
|
|
now := time.Now()
|
|
all := func(string) bool { return true }
|
|
|
|
// The TUI saved two lists.
|
|
tui := New()
|
|
tui.Containers["az|a"] = list(now.Add(-time.Hour), "kv-a-old")
|
|
tui.Containers["az|b"] = list(now.Add(-time.Minute), "kv-b")
|
|
tui.Cursor["az|b"] = "kv-b"
|
|
if err := Save(path, tui, all, now); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// lazysecrets list loads a again, and c.
|
|
listed := Read(path)
|
|
listed.Containers["az|a"] = list(now, "kv-a-new")
|
|
listed.Containers["az|c"] = list(now, "kv-c")
|
|
if err := Save(path, listed, all, now); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The TUI saves what it has, older: list's lists stay.
|
|
if err := Save(path, tui, all, now); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
f := Read(path)
|
|
for key, want := range map[string]string{"az|a": "kv-a-new", "az|b": "kv-b", "az|c": "kv-c"} {
|
|
if got := f.Containers[key].Value; len(got) != 1 || got[0].Name != want {
|
|
t.Errorf("%s = %v, want %s", key, got, want)
|
|
}
|
|
}
|
|
if f.Cursor["az|b"] != "kv-b" {
|
|
t.Error("cursor lost")
|
|
}
|
|
}
|
|
|
|
func TestSaveDrops(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "cache.json")
|
|
now := time.Now()
|
|
f := New()
|
|
f.Containers["az|old"] = list(now.Add(-MaxAge-time.Hour), "kv-old")
|
|
f.Containers["gone|x"] = list(now, "kv-gone")
|
|
f.Containers["az|x"] = list(now, "kv-x")
|
|
if err := Save(path, f, func(id string) bool { return true }, now); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Signed out of "gone" since: dropped from the file too.
|
|
if err := Save(path, New(), func(id string) bool { return id == "az" }, now); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := Read(path).Containers
|
|
if _, ok := got["az|old"]; ok {
|
|
t.Error("kept a list older than MaxAge")
|
|
}
|
|
if _, ok := got["gone|x"]; ok {
|
|
t.Error("kept a signed-out account's list")
|
|
}
|
|
if _, ok := got["az|x"]; !ok {
|
|
t.Error("dropped a list on disk the save didn't have")
|
|
}
|
|
}
|
|
|
|
func TestReadOtherVersion(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "cache.json")
|
|
f := New()
|
|
f.Version = Version + 1
|
|
f.Containers["az|x"] = list(time.Now(), "kv-x")
|
|
if err := write(path, f); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := Read(path); len(got.Containers) != 0 || got.Version != Version {
|
|
t.Errorf("read another version's file: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestSaveDroppedAndCursors(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "cache.json")
|
|
now := time.Now()
|
|
all := func(string) bool { return true }
|
|
f := New()
|
|
f.Containers["az|bad"] = list(now, "kv-old")
|
|
f.Cursor["az|x"] = "kv-tui"
|
|
if err := Save(path, f, all, now); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The TUI fails to load it again and drops it: it stays gone.
|
|
f = New()
|
|
f.Dropped = map[string]bool{"az|bad": true}
|
|
if err := Save(path, f, all, now); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, ok := Read(path).Containers["az|bad"]; ok {
|
|
t.Error("a dropped list came back")
|
|
}
|
|
// lazysecrets list saves without cursors: the TUI's stay.
|
|
listed := New()
|
|
listed.Cursor = nil
|
|
listed.Containers["az|y"] = list(now, "kv-y")
|
|
if err := Save(path, listed, all, now); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if Read(path).Cursor["az|x"] != "kv-tui" {
|
|
t.Error("the TUI's cursor was lost")
|
|
}
|
|
}
|