v2.9.0 8ebdfe0

pass_store_test.go RawHistoryFolder

1package domainsecrets
2
3import (
4	"context"
5	"fmt"
6	"os/exec"
7	"testing"
8	"time"
9
10	"github.com/stretchr/testify/assert"
11	"github.com/stretchr/testify/require"
12
13	"github.com/bnema/gordon/internal/domain"
14)
15
16func passCmd(args ...string) error {
17	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
18	defer cancel()
19	return exec.CommandContext(ctx, "pass", args...).Run()
20}
21
22func requirePass(t *testing.T) {
23	if err := passCmd("version"); err != nil {
24		t.Skip("pass not available")
25	}
26	if err := passCmd("ls"); err != nil {
27		t.Skip("pass store not initialized")
28	}
29}
30
31func cleanupPassDomain(_ *testing.T, domainName string, keys []string) {
32	safeDomain, err := domain.SanitizeDomainForEnvFile(domainName)
33	if err != nil {
34		return
35	}
36
37	for _, key := range keys {
38		path := fmt.Sprintf("%s/%s/%s", PassDomainSecretsPath, safeDomain, key)
39		_ = passCmd("rm", "-f", path)
40	}
41
42	manifestPath := fmt.Sprintf("%s/%s/.keys", PassDomainSecretsPath, safeDomain)
43	_ = passCmd("rm", "-f", manifestPath)
44}
45
46func TestPassStore_SetGetDelete(t *testing.T) {
47	requirePass(t)
48
49	store, err := NewPassStore(testLogger())
50	require.NoError(t, err)
51
52	domainName := fmt.Sprintf("test.%d.example.com", time.Now().UnixNano())
53	keys := []string{"API_KEY", "DB_PASSWORD"}
54	defer cleanupPassDomain(t, domainName, keys)
55
56	secretsMap := map[string]string{
57		"API_KEY":     "alpha",
58		"DB_PASSWORD": "bravo",
59	}
60
61	err = store.Set(domainName, secretsMap)
62	require.NoError(t, err)
63
64	keysList, err := store.ListKeys(domainName)
65	require.NoError(t, err)
66	assert.Len(t, keysList, 2)
67	assert.ElementsMatch(t, keys, keysList)
68
69	values, err := store.GetAll(domainName)
70	require.NoError(t, err)
71	assert.Equal(t, "alpha", values["API_KEY"])
72	assert.Equal(t, "bravo", values["DB_PASSWORD"])
73
74	err = store.Delete(domainName, "DB_PASSWORD")
75	require.NoError(t, err)
76
77	keysList, err = store.ListKeys(domainName)
78	require.NoError(t, err)
79	assert.ElementsMatch(t, []string{"API_KEY"}, keysList)
80}
81
82func TestPassStore_SetGetAttachment(t *testing.T) {
83	requirePass(t)
84
85	store, err := NewPassStore(testLogger())
86	require.NoError(t, err)
87
88	containerName := fmt.Sprintf("gitea-postgres-%d", time.Now().UnixNano())
89	keys := []string{"POSTGRES_USER", "POSTGRES_PASSWORD"}
90	defer CleanupPassAttachment(t, containerName, keys)
91
92	secretsMap := map[string]string{
93		"POSTGRES_USER":     "gitea",
94		"POSTGRES_PASSWORD": "secret123",
95	}
96
97	err = store.SetAttachment(containerName, secretsMap)
98	require.NoError(t, err)
99
100	values, err := store.GetAllAttachment(containerName)
101	require.NoError(t, err)
102	assert.Equal(t, "gitea", values["POSTGRES_USER"])
103	assert.Equal(t, "secret123", values["POSTGRES_PASSWORD"])
104}
105
106func TestPassStore_ListAttachmentKeys_AfterSet(t *testing.T) {
107	requirePass(t)
108
109	store, err := NewPassStore(testLogger())
110	require.NoError(t, err)
111
112	containerName := fmt.Sprintf("redis-cache-%d", time.Now().UnixNano())
113	keys := []string{"REDIS_PASSWORD"}
114	defer CleanupPassAttachment(t, containerName, keys)
115
116	secretsMap := map[string]string{
117		"REDIS_PASSWORD": "redis123",
118	}
119
120	err = store.SetAttachment(containerName, secretsMap)
121	require.NoError(t, err)
122
123	values, err := store.GetAllAttachment(containerName)
124	require.NoError(t, err)
125	assert.Len(t, values, 1)
126	assert.Equal(t, "redis123", values["REDIS_PASSWORD"])
127}
128
129func TestParsePassListOutput(t *testing.T) {
130	tests := []struct {
131		name     string
132		basePath string
133		output   string
134		want     []passListEntry
135	}{
136		{
137			name:     "simple tree",
138			basePath: "domain",
139			output:   "domain\n├── key1\n└── key2",
140			want:     []passListEntry{{name: "key1", depth: 1}, {name: "key2", depth: 1}},
141		},
142		{
143			name:     "nested tree",
144			basePath: "domain",
145			output:   "domain\n│   ├── subkey1\n│   └── subkey2\n└── key1",
146			want:     []passListEntry{{name: "subkey1", depth: 2}, {name: "subkey2", depth: 2}, {name: "key1", depth: 1}},
147		},
148		{
149			name:     "empty output",
150			basePath: "domain",
151			output:   "",
152			want:     []passListEntry{},
153		},
154		{
155			name:     "ASCII fallback chars",
156			basePath: "domain",
157			output:   "domain\n|   |-- subkey1\n`-- key1",
158			want:     []passListEntry{{name: "subkey1", depth: 2}, {name: "key1", depth: 1}},
159		},
160	}
161
162	for _, tt := range tests {
163		t.Run(tt.name, func(t *testing.T) {
164			got := parsePassListOutput(tt.basePath, tt.output)
165			assert.Equal(t, tt.want, got)
166		})
167	}
168}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close