env-secrets 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex/rules/cicd.md +170 -0
- package/.codex/rules/linting.md +174 -0
- package/.codex/rules/local-dev-badges.md +93 -0
- package/.codex/rules/local-dev-env.md +271 -0
- package/.codex/rules/local-dev-license.md +104 -0
- package/.codex/rules/local-dev-mcp.md +72 -0
- package/.codex/rules/logging.md +358 -0
- package/.codex/rules/observability.md +25 -0
- package/.codex/rules/testing.md +133 -0
- package/.github/workflows/lint.yaml +7 -8
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/unittests.yaml +1 -1
- package/AGENTS.md +10 -4
- package/README.md +14 -9
- package/__e2e__/README.md +2 -5
- package/__e2e__/index.test.ts +152 -1
- package/__e2e__/utils/test-utils.ts +61 -1
- package/__tests__/cli/helpers.test.ts +129 -0
- package/__tests__/vaults/aws-config.test.ts +85 -0
- package/__tests__/vaults/secretsmanager-admin.test.ts +312 -0
- package/__tests__/vaults/secretsmanager.test.ts +57 -20
- package/dist/cli/helpers.js +110 -0
- package/dist/index.js +221 -2
- package/dist/vaults/aws-config.js +29 -0
- package/dist/vaults/secretsmanager-admin.js +240 -0
- package/dist/vaults/secretsmanager.js +20 -16
- package/docs/AWS.md +78 -3
- package/eslint.config.js +67 -0
- package/jest.e2e.config.js +1 -0
- package/package.json +23 -13
- package/src/cli/helpers.ts +144 -0
- package/src/index.ts +287 -2
- package/src/vaults/aws-config.ts +51 -0
- package/src/vaults/secretsmanager-admin.ts +352 -0
- package/src/vaults/secretsmanager.ts +32 -20
- package/website/docs/cli-reference.mdx +67 -0
- package/website/docs/examples.mdx +1 -1
- package/website/docs/installation.mdx +1 -1
- package/website/docs/providers/aws-secrets-manager.mdx +32 -0
- package/.eslintignore +0 -4
- package/.eslintrc +0 -18
- package/.lintstagedrc +0 -4
package/__e2e__/index.test.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
LocalStackHelper,
|
|
3
3
|
cli,
|
|
4
4
|
cliWithEnv,
|
|
5
|
+
cliWithEnvAndStdin,
|
|
5
6
|
createTempFile,
|
|
6
7
|
cleanupTempFile,
|
|
7
8
|
createTestProfile,
|
|
@@ -332,7 +333,157 @@ describe('End-to-End Tests', () => {
|
|
|
332
333
|
const result = await cliWithEnv(['aws'], getLocalStackEnv());
|
|
333
334
|
|
|
334
335
|
expect(result.code).toBe(1);
|
|
335
|
-
expect(result.stderr).toContain('required option');
|
|
336
|
+
expect(result.stderr).toContain('Missing required option --secret');
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
describe('AWS Secret Management Commands', () => {
|
|
341
|
+
test('should create, list, get, and delete a secret', async () => {
|
|
342
|
+
const secretName = `e2e-managed-secret-${Date.now()}`;
|
|
343
|
+
|
|
344
|
+
const createResult = await cliWithEnv(
|
|
345
|
+
['aws', 'secret', 'create', '-n', secretName, '-v', 'initial-value'],
|
|
346
|
+
getLocalStackEnv()
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
expect(createResult.code).toBe(0);
|
|
350
|
+
expect(createResult.stdout).toContain(secretName);
|
|
351
|
+
|
|
352
|
+
const listResult = await cliWithEnv(
|
|
353
|
+
['aws', 'secret', 'list', '--prefix', 'e2e-managed-secret-'],
|
|
354
|
+
getLocalStackEnv()
|
|
355
|
+
);
|
|
356
|
+
expect(listResult.code).toBe(0);
|
|
357
|
+
expect(listResult.stdout).toContain(secretName);
|
|
358
|
+
|
|
359
|
+
const getResult = await cliWithEnv(
|
|
360
|
+
['aws', 'secret', 'get', '-n', secretName],
|
|
361
|
+
getLocalStackEnv()
|
|
362
|
+
);
|
|
363
|
+
expect(getResult.code).toBe(0);
|
|
364
|
+
expect(getResult.stdout).toContain(secretName);
|
|
365
|
+
expect(getResult.stdout).not.toContain('initial-value');
|
|
366
|
+
|
|
367
|
+
const deleteResult = await cliWithEnv(
|
|
368
|
+
[
|
|
369
|
+
'aws',
|
|
370
|
+
'secret',
|
|
371
|
+
'delete',
|
|
372
|
+
'-n',
|
|
373
|
+
secretName,
|
|
374
|
+
'--force-delete-without-recovery',
|
|
375
|
+
'--yes'
|
|
376
|
+
],
|
|
377
|
+
getLocalStackEnv()
|
|
378
|
+
);
|
|
379
|
+
expect(deleteResult.code).toBe(0);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
test('should update secret value from stdin', async () => {
|
|
383
|
+
const secret = await createTestSecret({
|
|
384
|
+
name: `managed-secret-stdin-${Date.now()}`,
|
|
385
|
+
value: 'initial-value',
|
|
386
|
+
description: 'Secret for stdin update test'
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
const updateResult = await cliWithEnvAndStdin(
|
|
390
|
+
[
|
|
391
|
+
'aws',
|
|
392
|
+
'secret',
|
|
393
|
+
'update',
|
|
394
|
+
'-n',
|
|
395
|
+
secret.prefixedName,
|
|
396
|
+
'--value-stdin'
|
|
397
|
+
],
|
|
398
|
+
getLocalStackEnv(),
|
|
399
|
+
'stdin-updated-value'
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
expect(updateResult.code).toBe(0);
|
|
403
|
+
expect(updateResult.stderr).toBe('');
|
|
404
|
+
|
|
405
|
+
const deleteResult = await cliWithEnv(
|
|
406
|
+
[
|
|
407
|
+
'aws',
|
|
408
|
+
'secret',
|
|
409
|
+
'delete',
|
|
410
|
+
'-n',
|
|
411
|
+
secret.prefixedName,
|
|
412
|
+
'--force-delete-without-recovery',
|
|
413
|
+
'--yes'
|
|
414
|
+
],
|
|
415
|
+
getLocalStackEnv()
|
|
416
|
+
);
|
|
417
|
+
expect(deleteResult.code).toBe(0);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
test('should require confirmation for delete', async () => {
|
|
421
|
+
const secret = await createTestSecret({
|
|
422
|
+
name: `managed-secret-confirm-${Date.now()}`,
|
|
423
|
+
value: 'value',
|
|
424
|
+
description: 'Secret for delete confirmation test'
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const result = await cliWithEnv(
|
|
428
|
+
['aws', 'secret', 'delete', '-n', secret.prefixedName],
|
|
429
|
+
getLocalStackEnv()
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
expect(result.code).toBe(1);
|
|
433
|
+
expect(result.stderr).toContain('requires --yes confirmation');
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test('should honor region flag for secret list across multiple regions', async () => {
|
|
437
|
+
const secret = await createTestSecret(
|
|
438
|
+
{
|
|
439
|
+
name: `managed-secret-multi-region-${Date.now()}`,
|
|
440
|
+
value: '{"region":"us-west-2"}',
|
|
441
|
+
description: 'Secret used for region isolation test'
|
|
442
|
+
},
|
|
443
|
+
'us-west-2'
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
const westResult = await cliWithEnv(
|
|
447
|
+
[
|
|
448
|
+
'aws',
|
|
449
|
+
'secret',
|
|
450
|
+
'list',
|
|
451
|
+
'--prefix',
|
|
452
|
+
secret.prefixedName,
|
|
453
|
+
'-r',
|
|
454
|
+
'us-west-2',
|
|
455
|
+
'--output',
|
|
456
|
+
'json'
|
|
457
|
+
],
|
|
458
|
+
getLocalStackEnv()
|
|
459
|
+
);
|
|
460
|
+
expect(westResult.code).toBe(0);
|
|
461
|
+
const westRows = JSON.parse(westResult.stdout) as Array<{
|
|
462
|
+
name: string;
|
|
463
|
+
}>;
|
|
464
|
+
expect(westRows.some((row) => row.name === secret.prefixedName)).toBe(
|
|
465
|
+
true
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
const eastResult = await cliWithEnv(
|
|
469
|
+
[
|
|
470
|
+
'aws',
|
|
471
|
+
'secret',
|
|
472
|
+
'list',
|
|
473
|
+
'--prefix',
|
|
474
|
+
secret.prefixedName,
|
|
475
|
+
'-r',
|
|
476
|
+
'us-east-1',
|
|
477
|
+
'--output',
|
|
478
|
+
'json'
|
|
479
|
+
],
|
|
480
|
+
getLocalStackEnv()
|
|
481
|
+
);
|
|
482
|
+
expect(eastResult.code).toBe(0);
|
|
483
|
+
const eastRows = JSON.parse(eastResult.stdout) as Array<{
|
|
484
|
+
name: string;
|
|
485
|
+
}>;
|
|
486
|
+
expect(eastRows).toEqual([]);
|
|
336
487
|
});
|
|
337
488
|
});
|
|
338
489
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { exec } from 'child_process';
|
|
1
|
+
import { exec, spawn } from 'child_process';
|
|
2
2
|
import { promisify } from 'util';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import * as fs from 'fs';
|
|
@@ -506,6 +506,66 @@ export async function cliWithEnv(
|
|
|
506
506
|
});
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
+
export async function cliWithEnvAndStdin(
|
|
510
|
+
args: string[],
|
|
511
|
+
env: Record<string, string>,
|
|
512
|
+
stdin: string,
|
|
513
|
+
cwd = '.'
|
|
514
|
+
): Promise<CliResult> {
|
|
515
|
+
return await new Promise((resolve) => {
|
|
516
|
+
const cleanEnv = { ...process.env };
|
|
517
|
+
delete cleanEnv.AWS_PROFILE;
|
|
518
|
+
delete cleanEnv.AWS_DEFAULT_PROFILE;
|
|
519
|
+
delete cleanEnv.AWS_SESSION_TOKEN;
|
|
520
|
+
delete cleanEnv.AWS_SECURITY_TOKEN;
|
|
521
|
+
delete cleanEnv.AWS_ROLE_ARN;
|
|
522
|
+
delete cleanEnv.AWS_ROLE_SESSION_NAME;
|
|
523
|
+
delete cleanEnv.AWS_WEB_IDENTITY_TOKEN_FILE;
|
|
524
|
+
delete cleanEnv.AWS_WEB_IDENTITY_TOKEN;
|
|
525
|
+
|
|
526
|
+
const defaultEnv = {
|
|
527
|
+
AWS_ENDPOINT_URL: process.env.LOCALSTACK_URL || 'http://localhost:4566',
|
|
528
|
+
AWS_ACCESS_KEY_ID: 'test',
|
|
529
|
+
AWS_SECRET_ACCESS_KEY: 'test',
|
|
530
|
+
AWS_DEFAULT_REGION: 'us-east-1',
|
|
531
|
+
AWS_REGION: 'us-east-1',
|
|
532
|
+
NODE_ENV: 'test'
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
const envVars = { ...cleanEnv, ...defaultEnv, ...env };
|
|
536
|
+
const child = spawn('node', [path.resolve('./dist/index'), ...args], {
|
|
537
|
+
cwd,
|
|
538
|
+
env: envVars,
|
|
539
|
+
stdio: 'pipe'
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
let stdout = '';
|
|
543
|
+
let stderr = '';
|
|
544
|
+
let error: Error | null = null;
|
|
545
|
+
|
|
546
|
+
child.stdout.on('data', (chunk) => {
|
|
547
|
+
stdout += chunk.toString();
|
|
548
|
+
});
|
|
549
|
+
child.stderr.on('data', (chunk) => {
|
|
550
|
+
stderr += chunk.toString();
|
|
551
|
+
});
|
|
552
|
+
child.on('error', (err) => {
|
|
553
|
+
error = err;
|
|
554
|
+
});
|
|
555
|
+
child.on('close', (code) => {
|
|
556
|
+
resolve({
|
|
557
|
+
code: code ?? (error ? 1 : 0),
|
|
558
|
+
error,
|
|
559
|
+
stdout,
|
|
560
|
+
stderr
|
|
561
|
+
});
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
child.stdin.write(stdin);
|
|
565
|
+
child.stdin.end();
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
509
569
|
export function createTempFile(content: string) {
|
|
510
570
|
const tempDir = os.tmpdir();
|
|
511
571
|
const tempFile = path.join(tempDir, `env-secrets-test-${Date.now()}.env`);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { PassThrough } from 'node:stream';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
asOutputFormat,
|
|
5
|
+
parseRecoveryDays,
|
|
6
|
+
printData,
|
|
7
|
+
readStdin,
|
|
8
|
+
renderTable,
|
|
9
|
+
resolveAwsScope,
|
|
10
|
+
resolveSecretValue
|
|
11
|
+
} from '../../src/cli/helpers';
|
|
12
|
+
|
|
13
|
+
describe('cli/helpers', () => {
|
|
14
|
+
it('validates output format', () => {
|
|
15
|
+
expect(asOutputFormat('json')).toBe('json');
|
|
16
|
+
expect(asOutputFormat('table')).toBe('table');
|
|
17
|
+
expect(() => asOutputFormat('xml')).toThrow('Invalid output format');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('renders empty table message', () => {
|
|
21
|
+
const output = renderTable([{ key: 'name', label: 'Name' }], []);
|
|
22
|
+
expect(output).toBe('No results.');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('renders aligned table output', () => {
|
|
26
|
+
const output = renderTable(
|
|
27
|
+
[
|
|
28
|
+
{ key: 'name', label: 'Name' },
|
|
29
|
+
{ key: 'value', label: 'Value' }
|
|
30
|
+
],
|
|
31
|
+
[{ name: 'one', value: '1' }]
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(output).toContain('Name');
|
|
35
|
+
expect(output).toContain('Value');
|
|
36
|
+
expect(output).toContain('one');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('prints JSON output', () => {
|
|
40
|
+
const consoleSpy = jest
|
|
41
|
+
.spyOn(console, 'log')
|
|
42
|
+
.mockImplementation(() => undefined);
|
|
43
|
+
|
|
44
|
+
printData('json', [{ key: 'name', label: 'Name' }], [{ name: 'value' }]);
|
|
45
|
+
|
|
46
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
47
|
+
JSON.stringify([{ name: 'value' }], null, 2)
|
|
48
|
+
);
|
|
49
|
+
consoleSpy.mockRestore();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('parses valid recovery days and rejects invalid values', () => {
|
|
53
|
+
expect(parseRecoveryDays('7')).toBe(7);
|
|
54
|
+
expect(parseRecoveryDays('30')).toBe(30);
|
|
55
|
+
expect(() => parseRecoveryDays('6')).toThrow();
|
|
56
|
+
expect(() => parseRecoveryDays('31')).toThrow();
|
|
57
|
+
expect(() => parseRecoveryDays('abc')).toThrow();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('reads stdin content and strips one trailing newline', async () => {
|
|
61
|
+
const stream = new PassThrough();
|
|
62
|
+
const promise = readStdin(stream as unknown as NodeJS.ReadStream);
|
|
63
|
+
|
|
64
|
+
stream.write('secret-value\n');
|
|
65
|
+
stream.end();
|
|
66
|
+
|
|
67
|
+
await expect(promise).resolves.toBe('secret-value');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('resolves secret value from explicit --value', async () => {
|
|
71
|
+
await expect(resolveSecretValue('inline', false)).resolves.toBe('inline');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('rejects when both --value and --value-stdin are used', async () => {
|
|
75
|
+
await expect(resolveSecretValue('inline', true)).rejects.toThrow(
|
|
76
|
+
'Use either --value or --value-stdin, not both.'
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('rejects stdin mode when no stdin is provided', async () => {
|
|
81
|
+
const originalStdin = process.stdin;
|
|
82
|
+
Object.defineProperty(process, 'stdin', {
|
|
83
|
+
value: { ...originalStdin, isTTY: true },
|
|
84
|
+
configurable: true
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await expect(resolveSecretValue(undefined, true)).rejects.toThrow(
|
|
88
|
+
'No stdin detected. Pipe a value when using --value-stdin.'
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
Object.defineProperty(process, 'stdin', {
|
|
92
|
+
value: originalStdin,
|
|
93
|
+
configurable: true
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('prefers explicit aws scope options over global options', () => {
|
|
98
|
+
const command = {
|
|
99
|
+
optsWithGlobals: () => ({
|
|
100
|
+
profile: 'global-profile',
|
|
101
|
+
region: 'us-west-2'
|
|
102
|
+
})
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
expect(
|
|
106
|
+
resolveAwsScope(
|
|
107
|
+
{ profile: 'local-profile', region: 'us-east-1' },
|
|
108
|
+
command
|
|
109
|
+
)
|
|
110
|
+
).toEqual({
|
|
111
|
+
profile: 'local-profile',
|
|
112
|
+
region: 'us-east-1'
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('falls back to global aws scope options when local options are absent', () => {
|
|
117
|
+
const command = {
|
|
118
|
+
optsWithGlobals: () => ({
|
|
119
|
+
profile: 'global-profile',
|
|
120
|
+
region: 'us-west-2'
|
|
121
|
+
})
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
expect(resolveAwsScope({}, command)).toEqual({
|
|
125
|
+
profile: 'global-profile',
|
|
126
|
+
region: 'us-west-2'
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { fromIni } from '@aws-sdk/credential-providers';
|
|
2
|
+
|
|
3
|
+
jest.mock('@aws-sdk/credential-providers');
|
|
4
|
+
|
|
5
|
+
import { buildAwsClientConfig } from '../../src/vaults/aws-config';
|
|
6
|
+
|
|
7
|
+
const mockFromIni = fromIni as jest.MockedFunction<typeof fromIni>;
|
|
8
|
+
|
|
9
|
+
describe('aws-config', () => {
|
|
10
|
+
let originalEnv: NodeJS.ProcessEnv;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.clearAllMocks();
|
|
14
|
+
originalEnv = { ...process.env };
|
|
15
|
+
process.env = { ...originalEnv };
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
process.env = originalEnv;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('uses explicit profile when provided', () => {
|
|
23
|
+
const mockCredentials = jest.fn();
|
|
24
|
+
mockFromIni.mockReturnValue(mockCredentials as ReturnType<typeof fromIni>);
|
|
25
|
+
|
|
26
|
+
const config = buildAwsClientConfig({
|
|
27
|
+
profile: 'my-profile',
|
|
28
|
+
region: 'us-east-1'
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
expect(mockFromIni).toHaveBeenCalledWith({ profile: 'my-profile' });
|
|
32
|
+
expect(config).toEqual({
|
|
33
|
+
region: 'us-east-1',
|
|
34
|
+
credentials: mockCredentials
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('uses environment credentials when access key and secret are present', () => {
|
|
39
|
+
process.env.AWS_ACCESS_KEY_ID = 'test';
|
|
40
|
+
process.env.AWS_SECRET_ACCESS_KEY = 'test';
|
|
41
|
+
|
|
42
|
+
const config = buildAwsClientConfig({ region: 'us-east-1' });
|
|
43
|
+
|
|
44
|
+
expect(mockFromIni).not.toHaveBeenCalled();
|
|
45
|
+
expect(config).toEqual({
|
|
46
|
+
region: 'us-east-1',
|
|
47
|
+
credentials: undefined
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('falls back to default profile when no explicit credentials are provided', () => {
|
|
52
|
+
const mockCredentials = jest.fn();
|
|
53
|
+
mockFromIni.mockReturnValue(mockCredentials as ReturnType<typeof fromIni>);
|
|
54
|
+
|
|
55
|
+
const config = buildAwsClientConfig({ region: 'us-east-1' });
|
|
56
|
+
|
|
57
|
+
expect(mockFromIni).toHaveBeenCalledWith({ profile: 'default' });
|
|
58
|
+
expect(config).toEqual({
|
|
59
|
+
region: 'us-east-1',
|
|
60
|
+
credentials: mockCredentials
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('prefers AWS_ENDPOINT_URL for custom endpoint', () => {
|
|
65
|
+
const mockCredentials = jest.fn();
|
|
66
|
+
mockFromIni.mockReturnValue(mockCredentials as ReturnType<typeof fromIni>);
|
|
67
|
+
process.env.AWS_ENDPOINT_URL = 'http://localhost:4566';
|
|
68
|
+
process.env.AWS_SECRETS_MANAGER_ENDPOINT = 'http://localhost:4577';
|
|
69
|
+
|
|
70
|
+
const config = buildAwsClientConfig({ region: 'us-east-1' });
|
|
71
|
+
|
|
72
|
+
expect(config.endpoint).toBe('http://localhost:4566');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('uses AWS_SECRETS_MANAGER_ENDPOINT when AWS_ENDPOINT_URL is unset', () => {
|
|
76
|
+
const mockCredentials = jest.fn();
|
|
77
|
+
mockFromIni.mockReturnValue(mockCredentials as ReturnType<typeof fromIni>);
|
|
78
|
+
delete process.env.AWS_ENDPOINT_URL;
|
|
79
|
+
process.env.AWS_SECRETS_MANAGER_ENDPOINT = 'http://localhost:4577';
|
|
80
|
+
|
|
81
|
+
const config = buildAwsClientConfig({ region: 'us-east-1' });
|
|
82
|
+
|
|
83
|
+
expect(config.endpoint).toBe('http://localhost:4577');
|
|
84
|
+
});
|
|
85
|
+
});
|