v2.0.0 ae3e752

providers_test.go RawHistoryFolder

1package env
2
3import (
4	"context"
5	"os/exec"
6	"testing"
7	"time"
8
9	"github.com/stretchr/testify/assert"
10)
11
12func TestNewPassProvider(t *testing.T) {
13	provider := NewPassProvider()
14	assert.NotNil(t, provider)
15	assert.Equal(t, "pass", provider.Name())
16	assert.Equal(t, 10*time.Second, provider.timeout)
17}
18
19func TestPassProvider_Name(t *testing.T) {
20	provider := NewPassProvider()
21	assert.Equal(t, "pass", provider.Name())
22}
23
24func TestPassProvider_GetSecret(t *testing.T) {
25	provider := NewPassProvider()
26
27	tests := []struct {
28		name           string
29		path           string
30		mockCommand    bool
31		mockOutput     string
32		mockError      bool
33		expectedSecret string
34		expectedError  bool
35	}{
36		{
37			name:           "successful secret retrieval",
38			path:           "test/secret",
39			mockCommand:    true,
40			mockOutput:     "secret-value\n",
41			expectedSecret: "secret-value",
42		},
43		{
44			name:          "command execution fails",
45			path:          "test/nonexistent",
46			mockCommand:   true,
47			mockError:     true,
48			expectedError: true,
49		},
50		{
51			name:          "empty secret returned",
52			path:          "test/empty",
53			mockCommand:   true,
54			mockOutput:    "\n",
55			expectedError: true,
56		},
57	}
58
59	for _, tt := range tests {
60		t.Run(tt.name, func(t *testing.T) {
61			// Check if pass command is available
62			_, err := exec.LookPath("pass")
63			if err != nil {
64				t.Skip("pass command not available, skipping test")
65			}
66
67			// Since we can't easily mock exec.Command in this context,
68			// we'll test the interface and structure, but skip actual execution
69			// unless we have a real pass installation
70
71			// Test the provider structure
72			assert.Equal(t, "pass", provider.Name())
73			assert.NotZero(t, provider.timeout)
74
75			// For actual testing, we would need a more sophisticated mocking approach
76			// or integration tests with a real pass setup
77			if !tt.mockCommand {
78				secret, err := provider.GetSecret(tt.path)
79				if tt.expectedError {
80					assert.Error(t, err)
81				} else {
82					assert.NoError(t, err)
83					assert.Equal(t, tt.expectedSecret, secret)
84				}
85			}
86		})
87	}
88}
89
90func TestNewSopsProvider(t *testing.T) {
91	provider := NewSopsProvider()
92	assert.NotNil(t, provider)
93	assert.Equal(t, "sops", provider.Name())
94	assert.Equal(t, 10*time.Second, provider.timeout)
95}
96
97func TestSopsProvider_Name(t *testing.T) {
98	provider := NewSopsProvider()
99	assert.Equal(t, "sops", provider.Name())
100}
101
102func TestSopsProvider_GetSecret(t *testing.T) {
103	provider := NewSopsProvider()
104
105	tests := []struct {
106		name           string
107		path           string
108		expectedSecret string
109		expectedError  bool
110	}{
111		{
112			name:          "invalid path format - no colon",
113			path:          "invalid-path",
114			expectedError: true,
115		},
116		{
117			name:          "invalid path format - empty key",
118			path:          "file.yaml:",
119			expectedError: true,
120		},
121		{
122			name:          "invalid path format - empty file",
123			path:          ":key.path",
124			expectedError: true,
125		},
126	}
127
128	for _, tt := range tests {
129		t.Run(tt.name, func(t *testing.T) {
130			// Check if sops command is available
131			_, err := exec.LookPath("sops")
132			if err != nil {
133				t.Skip("sops command not available, skipping test")
134			}
135
136			// Test the provider structure
137			assert.Equal(t, "sops", provider.Name())
138			assert.NotZero(t, provider.timeout)
139
140			// Test path validation
141			secret, err := provider.GetSecret(tt.path)
142			if tt.expectedError {
143				assert.Error(t, err)
144				assert.Empty(t, secret)
145			} else {
146				// For successful cases, we'd need actual sops files
147				// which is complex to set up in unit tests
148				// This would be better tested in integration tests
149			}
150		})
151	}
152}
153
154func TestSopsProvider_GetSecret_ValidFormat(t *testing.T) {
155	provider := NewSopsProvider()
156	
157	// Check if sops command is available
158	_, err := exec.LookPath("sops")
159	if err != nil {
160		t.Skip("sops command not available, skipping test")
161	}
162
163	// Test with valid format but non-existent file (should fail gracefully)
164	_, err = provider.GetSecret("nonexistent.yaml:some.key")
165	assert.Error(t, err)
166	assert.Contains(t, err.Error(), "sops command failed")
167}
168
169func TestSecretProvider_Interface(t *testing.T) {
170	// Test that our providers implement the SecretProvider interface
171	var _ SecretProvider = NewPassProvider()
172	var _ SecretProvider = NewSopsProvider()
173}
174
175func TestProviders_Timeout(t *testing.T) {
176	// Test that providers have reasonable timeouts
177	passProvider := NewPassProvider()
178	sopsProvider := NewSopsProvider()
179
180	assert.Equal(t, 10*time.Second, passProvider.timeout)
181	assert.Equal(t, 10*time.Second, sopsProvider.timeout)
182}
183
184func TestProviders_ContextCancellation(t *testing.T) {
185	// Test that providers respect context cancellation
186	passProvider := NewPassProvider()
187	sopsProvider := NewSopsProvider()
188
189	// Create a context that's already cancelled
190	_, cancel := context.WithCancel(context.Background())
191	cancel()
192
193	// Providers should handle cancelled contexts gracefully
194	// (This is more of a behavioral test - the actual implementation
195	// uses exec.CommandContext which should respect cancellation)
196	assert.NotNil(t, passProvider)
197	assert.NotNil(t, sopsProvider)
198}
199
200// Mock implementations for more controlled testing
201type MockPassProvider struct {
202	output string
203	err    error
204}
205
206func (m *MockPassProvider) Name() string {
207	return "pass"
208}
209
210func (m *MockPassProvider) GetSecret(path string) (string, error) {
211	if m.err != nil {
212		return "", m.err
213	}
214	return m.output, nil
215}
216
217type MockSopsProvider struct {
218	output string
219	err    error
220}
221
222func (m *MockSopsProvider) Name() string {
223	return "sops"
224}
225
226func (m *MockSopsProvider) GetSecret(path string) (string, error) {
227	if m.err != nil {
228		return "", m.err
229	}
230	return m.output, nil
231}
232
233func TestMockProviders(t *testing.T) {
234	// Test our mock implementations
235	mockPass := &MockPassProvider{output: "test-secret", err: nil}
236	mockSops := &MockSopsProvider{output: "sops-secret", err: nil}
237
238	assert.Equal(t, "pass", mockPass.Name())
239	assert.Equal(t, "sops", mockSops.Name())
240
241	secret, err := mockPass.GetSecret("test/path")
242	assert.NoError(t, err)
243	assert.Equal(t, "test-secret", secret)
244
245	secret, err = mockSops.GetSecret("file.yaml:key")
246	assert.NoError(t, err)
247	assert.Equal(t, "sops-secret", secret)
248}

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