package main import ( "os" "path/filepath" "testing" ) func TestSplitRef(t *testing.T) { cases := []struct { in string mount, key string wantErr bool }{ {"gpg/app", "gpg", "app", false}, {"/gpg/app/", "gpg", "app", false}, {"secret/gpg/team", "secret/gpg", "team", false}, {"app", "", "", true}, {"gpg/", "", "", true}, {"/app", "", "", true}, } for _, c := range cases { m, k, err := splitRef(c.in) if c.wantErr { if err == nil { t.Errorf("splitRef(%q): expected error", c.in) } continue } if err != nil { t.Errorf("splitRef(%q): %v", c.in, err) continue } if m != c.mount || k != c.key { t.Errorf("splitRef(%q) = (%q,%q), want (%q,%q)", c.in, m, k, c.mount, c.key) } } } func TestParseClip(t *testing.T) { cases := []struct { in []string clip bool line int restName string }{ {[]string{"foo"}, false, 1, "foo"}, {[]string{"-c", "foo"}, true, 1, "foo"}, {[]string{"foo", "--clip"}, true, 1, "foo"}, {[]string{"--clip=3", "foo"}, true, 3, "foo"}, {[]string{"-c2", "foo"}, true, 2, "foo"}, } for _, c := range cases { clip, line, rest := parseClip(c.in) if clip != c.clip || line != c.line { t.Errorf("parseClip(%v) = (%v,%d), want (%v,%d)", c.in, clip, line, c.clip, c.line) } if len(rest) != 1 || rest[0] != c.restName { t.Errorf("parseClip(%v) rest = %v, want [%q]", c.in, rest, c.restName) } } } func TestGpgIDWalkUp(t *testing.T) { dir := t.TempDir() s := &store{dir: dir} // Root .gpg-id, plus a subtree override. if err := os.WriteFile(filepath.Join(dir, ".gpg-id"), []byte("gpg/personal\n"), 0o600); err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Join(dir, "work", "team"), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "work", ".gpg-id"), []byte("gpg/work\n"), 0o600); err != nil { t.Fatal(err) } cases := map[string]string{ "email/gmail": "gpg/personal", "work/deploy": "gpg/work", "work/team/secret": "gpg/work", "root-entry": "gpg/personal", } for name, want := range cases { got, err := s.vaultRef(name) if err != nil { t.Errorf("vaultRef(%q): %v", name, err) continue } if got != want { t.Errorf("vaultRef(%q) = %q, want %q", name, got, want) } } } func TestVaultRefUninitialized(t *testing.T) { s := &store{dir: t.TempDir()} if _, err := s.vaultRef("anything"); err == nil { t.Fatal("expected error for store with no .gpg-id / .vault-id") } } // TestDualModeVaultID covers a store shared with plain gpg `pass`: `.gpg-id` // holds GPG fingerprints, and a sibling `.vault-id` carries the Vault ref that // passv must use. func TestDualModeVaultID(t *testing.T) { dir := t.TempDir() s := &store{dir: dir} // .gpg-id has real GPG fingerprints (as plain pass would write); passv must // not treat those as a Vault ref. if err := os.WriteFile(filepath.Join(dir, ".gpg-id"), []byte("ABCD1234ABCD1234ABCD1234ABCD1234ABCD1234\nDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF\n"), 0o600); err != nil { t.Fatal(err) } // Without a .vault-id, passv cannot know which Vault key to use. if _, err := s.vaultRef("email/gmail"); err == nil { t.Fatal("expected error when .gpg-id holds fingerprints and no .vault-id exists") } // Adding .vault-id resolves it. if err := os.WriteFile(filepath.Join(dir, ".vault-id"), []byte("gpg/app\n"), 0o600); err != nil { t.Fatal(err) } got, err := s.vaultRef("email/gmail") if err != nil { t.Fatal(err) } if got != "gpg/app" { t.Fatalf("vaultRef = %q, want gpg/app", got) } } func TestGenPassword(t *testing.T) { pw, err := genPassword(32, false) if err != nil { t.Fatal(err) } if len(pw) != 32 { t.Fatalf("length = %d, want 32", len(pw)) } for _, r := range pw { if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9')) { t.Fatalf("no-symbols password contains symbol %q", r) } } // Two generations must differ (astronomically unlikely to collide). a, _ := genPassword(25, true) b, _ := genPassword(25, true) if a == b { t.Fatal("consecutive generated passwords collided") } } func TestListSkipsHidden(t *testing.T) { dir := t.TempDir() s := &store{dir: dir} os.MkdirAll(filepath.Join(dir, ".git"), 0o700) os.WriteFile(filepath.Join(dir, ".git", "config.gpg"), []byte("x"), 0o600) os.WriteFile(filepath.Join(dir, ".gpg-id"), []byte("gpg/app"), 0o600) os.MkdirAll(filepath.Join(dir, "email"), 0o700) os.WriteFile(filepath.Join(dir, "email", "gmail.gpg"), []byte("x"), 0o600) os.WriteFile(filepath.Join(dir, "note.txt"), []byte("x"), 0o600) names, err := s.list("") if err != nil { t.Fatal(err) } if len(names) != 1 || names[0] != "email/gmail" { t.Fatalf("list = %v, want [email/gmail]", names) } }