Skip to content

Commit 31e8ffc

Browse files
committed
test: add unit tests for secret referencing utilities
1 parent b8024a0 commit 31e8ffc

File tree

1 file changed

+387
-0
lines changed

1 file changed

+387
-0
lines changed
Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
import { Secret } from '../../src/types';
2+
import {
3+
parseSecretReference,
4+
resolveSecretReferences,
5+
normalizeKey
6+
} from '../../src/utils/secretReferencing';
7+
8+
describe('Secret Referencing Utils', () => {
9+
describe('parseSecretReference', () => {
10+
it('should parse local secret reference', () => {
11+
const result = parseSecretReference('${SECRET_KEY}');
12+
expect(result).toEqual({
13+
app: null,
14+
env: null,
15+
path: '/',
16+
key: 'SECRET_KEY'
17+
});
18+
});
19+
20+
it('should parse local folder secret reference', () => {
21+
const result = parseSecretReference('${/path/to/SECRET_KEY}');
22+
expect(result).toEqual({
23+
app: null,
24+
env: null,
25+
path: '/path/to',
26+
key: 'SECRET_KEY'
27+
});
28+
});
29+
30+
it('should parse cross-env secret reference', () => {
31+
const result = parseSecretReference('${prod.SECRET_KEY}');
32+
expect(result).toEqual({
33+
app: null,
34+
env: 'prod',
35+
path: '/',
36+
key: 'SECRET_KEY'
37+
});
38+
});
39+
40+
it('should parse cross-env folder secret reference', () => {
41+
const result = parseSecretReference('${prod./path/to/SECRET_KEY}');
42+
expect(result).toEqual({
43+
app: null,
44+
env: 'prod',
45+
path: '/path/to',
46+
key: 'SECRET_KEY'
47+
});
48+
});
49+
50+
it('should parse cross-app secret reference', () => {
51+
const result = parseSecretReference('${app-name::SECRET_KEY}');
52+
expect(result).toEqual({
53+
app: 'app-name',
54+
env: null,
55+
path: '/',
56+
key: 'SECRET_KEY'
57+
});
58+
});
59+
60+
it('should parse cross-app folder secret reference', () => {
61+
const result = parseSecretReference('${app-name::/path/to/SECRET_KEY}');
62+
expect(result).toEqual({
63+
app: 'app-name',
64+
env: null,
65+
path: '/path/to',
66+
key: 'SECRET_KEY'
67+
});
68+
});
69+
70+
it('should parse cross-app cross-env secret reference', () => {
71+
const result = parseSecretReference('${app-name::prod.SECRET_KEY}');
72+
expect(result).toEqual({
73+
app: 'app-name',
74+
env: 'prod',
75+
path: '/',
76+
key: 'SECRET_KEY'
77+
});
78+
});
79+
80+
it('should parse cross-app cross-env folder secret reference', () => {
81+
const result = parseSecretReference('${app-name::prod./path/to/SECRET_KEY}');
82+
expect(result).toEqual({
83+
app: 'app-name',
84+
env: 'prod',
85+
path: '/path/to',
86+
key: 'SECRET_KEY'
87+
});
88+
});
89+
90+
it('should handle invalid reference format', () => {
91+
expect(() => parseSecretReference('invalid')).toThrow('Invalid secret reference format');
92+
});
93+
});
94+
95+
describe('normalizeKey', () => {
96+
it('should normalize key without app', () => {
97+
const result = normalizeKey('prod', '/path/to', 'SECRET_KEY');
98+
expect(result).toBe('prod:/path/to:SECRET_KEY');
99+
});
100+
101+
it('should normalize key with app', () => {
102+
const result = normalizeKey('prod', '/path/to', 'SECRET_KEY', 'app-name');
103+
expect(result).toBe('app-name:prod:/path/to:SECRET_KEY');
104+
});
105+
106+
it('should handle trailing slashes in path', () => {
107+
const result = normalizeKey('prod', '/path/to/', 'SECRET_KEY');
108+
expect(result).toBe('prod:/path/to:SECRET_KEY');
109+
});
110+
});
111+
112+
describe('resolveSecretReferences', () => {
113+
const mockFetcher = jest.fn();
114+
const cache = new Map<string, string>();
115+
116+
beforeEach(() => {
117+
mockFetcher.mockClear();
118+
cache.clear();
119+
});
120+
121+
it('should resolve local secret reference', async () => {
122+
mockFetcher.mockResolvedValueOnce({
123+
value: 'secret-value',
124+
key: 'SECRET_KEY',
125+
environment: 'dev',
126+
path: '/',
127+
id: '1',
128+
comment: '',
129+
tags: [],
130+
keyDigest: '',
131+
createdAt: undefined,
132+
updatedAt: new Date().toISOString(),
133+
version: 1
134+
});
135+
136+
const result = await resolveSecretReferences(
137+
'${SECRET_KEY}',
138+
'dev',
139+
'/',
140+
mockFetcher,
141+
null,
142+
cache
143+
);
144+
145+
expect(result).toBe('secret-value');
146+
expect(mockFetcher).toHaveBeenCalledWith('dev', '/', 'SECRET_KEY', null);
147+
});
148+
149+
it('should resolve cross-env secret reference', async () => {
150+
mockFetcher.mockResolvedValueOnce({
151+
value: 'prod-secret',
152+
key: 'SECRET_KEY',
153+
environment: 'prod',
154+
path: '/',
155+
id: '1',
156+
comment: '',
157+
tags: [],
158+
keyDigest: '',
159+
createdAt: undefined,
160+
updatedAt: new Date().toISOString(),
161+
version: 1
162+
});
163+
164+
const result = await resolveSecretReferences(
165+
'${prod.SECRET_KEY}',
166+
'dev',
167+
'/',
168+
mockFetcher,
169+
null,
170+
cache
171+
);
172+
173+
expect(result).toBe('prod-secret');
174+
expect(mockFetcher).toHaveBeenCalledWith('prod', '/', 'SECRET_KEY', null);
175+
});
176+
177+
it('should resolve cross-app secret reference', async () => {
178+
mockFetcher.mockResolvedValueOnce({
179+
value: 'app-secret',
180+
key: 'SECRET_KEY',
181+
environment: 'dev',
182+
path: '/',
183+
id: '1',
184+
comment: '',
185+
tags: [],
186+
keyDigest: '',
187+
createdAt: undefined,
188+
updatedAt: new Date().toISOString(),
189+
version: 1
190+
});
191+
192+
const result = await resolveSecretReferences(
193+
'${app-name::SECRET_KEY}',
194+
'dev',
195+
'/',
196+
mockFetcher,
197+
null,
198+
cache
199+
);
200+
201+
expect(result).toBe('app-secret');
202+
expect(mockFetcher).toHaveBeenCalledWith('dev', '/', 'SECRET_KEY', 'app-name');
203+
});
204+
205+
it('should resolve nested secret references', async () => {
206+
// First level reference
207+
mockFetcher.mockResolvedValueOnce({
208+
value: '${NESTED_KEY}',
209+
key: 'SECRET_KEY',
210+
environment: 'dev',
211+
path: '/',
212+
id: '1',
213+
comment: '',
214+
tags: [],
215+
keyDigest: '',
216+
createdAt: undefined,
217+
updatedAt: new Date().toISOString(),
218+
version: 1
219+
});
220+
221+
// Second level reference
222+
mockFetcher.mockResolvedValueOnce({
223+
value: 'final-value',
224+
key: 'NESTED_KEY',
225+
environment: 'dev',
226+
path: '/',
227+
id: '2',
228+
comment: '',
229+
tags: [],
230+
keyDigest: '',
231+
createdAt: undefined,
232+
updatedAt: new Date().toISOString(),
233+
version: 1
234+
});
235+
236+
const result = await resolveSecretReferences(
237+
'${SECRET_KEY}',
238+
'dev',
239+
'/',
240+
mockFetcher,
241+
null,
242+
cache
243+
);
244+
245+
expect(result).toBe('final-value');
246+
expect(mockFetcher).toHaveBeenCalledTimes(2);
247+
});
248+
249+
it('should detect circular references', async () => {
250+
// First level reference
251+
mockFetcher.mockResolvedValueOnce({
252+
value: '${CIRCULAR_KEY}',
253+
key: 'SECRET_KEY',
254+
environment: 'dev',
255+
path: '/',
256+
id: '1',
257+
comment: '',
258+
tags: [],
259+
keyDigest: '',
260+
createdAt: undefined,
261+
updatedAt: new Date().toISOString(),
262+
version: 1
263+
});
264+
265+
// Circular reference
266+
mockFetcher.mockResolvedValueOnce({
267+
value: '${SECRET_KEY}',
268+
key: 'CIRCULAR_KEY',
269+
environment: 'dev',
270+
path: '/',
271+
id: '2',
272+
comment: '',
273+
tags: [],
274+
keyDigest: '',
275+
createdAt: undefined,
276+
updatedAt: new Date().toISOString(),
277+
version: 1
278+
});
279+
280+
await expect(resolveSecretReferences(
281+
'${SECRET_KEY}',
282+
'dev',
283+
'/',
284+
mockFetcher,
285+
null,
286+
cache
287+
)).rejects.toThrow('Circular reference detected');
288+
});
289+
290+
it('should handle multiple references in a single value', async () => {
291+
mockFetcher
292+
.mockResolvedValueOnce({
293+
value: 'first-value',
294+
key: 'FIRST_KEY',
295+
environment: 'dev',
296+
path: '/',
297+
id: '1',
298+
comment: '',
299+
tags: [],
300+
keyDigest: '',
301+
createdAt: undefined,
302+
updatedAt: new Date().toISOString(),
303+
version: 1
304+
})
305+
.mockResolvedValueOnce({
306+
value: 'second-value',
307+
key: 'SECOND_KEY',
308+
environment: 'dev',
309+
path: '/',
310+
id: '2',
311+
comment: '',
312+
tags: [],
313+
keyDigest: '',
314+
createdAt: undefined,
315+
updatedAt: new Date().toISOString(),
316+
version: 1
317+
});
318+
319+
const result = await resolveSecretReferences(
320+
'${FIRST_KEY}-${SECOND_KEY}',
321+
'dev',
322+
'/',
323+
mockFetcher,
324+
null,
325+
cache
326+
);
327+
328+
expect(result).toBe('first-value-second-value');
329+
expect(mockFetcher).toHaveBeenCalledTimes(2);
330+
});
331+
332+
it('should handle folder paths correctly', async () => {
333+
mockFetcher.mockResolvedValueOnce({
334+
value: 'folder-secret',
335+
key: 'SECRET_KEY',
336+
environment: 'dev',
337+
path: '/path/to',
338+
id: '1',
339+
comment: '',
340+
tags: [],
341+
keyDigest: '',
342+
createdAt: undefined,
343+
updatedAt: new Date().toISOString(),
344+
version: 1
345+
});
346+
347+
const result = await resolveSecretReferences(
348+
'${/path/to/SECRET_KEY}',
349+
'dev',
350+
'/',
351+
mockFetcher,
352+
null,
353+
cache
354+
);
355+
356+
expect(result).toBe('folder-secret');
357+
expect(mockFetcher).toHaveBeenCalledWith('dev', '/path/to', 'SECRET_KEY', null);
358+
});
359+
360+
it('should handle mixed references with literals', async () => {
361+
mockFetcher.mockResolvedValueOnce({
362+
value: 'secret-value',
363+
key: 'SECRET_KEY',
364+
environment: 'dev',
365+
path: '/',
366+
id: '1',
367+
comment: '',
368+
tags: [],
369+
keyDigest: '',
370+
createdAt: undefined,
371+
updatedAt: new Date().toISOString(),
372+
version: 1
373+
});
374+
375+
const result = await resolveSecretReferences(
376+
'prefix-${SECRET_KEY}-suffix',
377+
'dev',
378+
'/',
379+
mockFetcher,
380+
null,
381+
cache
382+
);
383+
384+
expect(result).toBe('prefix-secret-value-suffix');
385+
});
386+
});
387+
});

0 commit comments

Comments
 (0)