env-secrets 0.1.10 → 0.3.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/.devcontainer/devcontainer.json +33 -0
- package/.dockerignore +9 -0
- package/.eslintignore +4 -2
- package/.github/dependabot.yml +4 -0
- package/.github/workflows/build-main.yml +6 -2
- package/.github/workflows/deploy-docs.yml +50 -0
- package/.github/workflows/e2e-tests.yaml +54 -0
- package/.github/workflows/lint.yaml +6 -2
- package/.github/workflows/release.yml +13 -3
- package/.github/workflows/snyk.yaml +5 -1
- package/.github/workflows/unittests.yaml +18 -6
- package/.lintstagedrc +2 -7
- package/.prettierignore +6 -0
- package/AGENTS.md +149 -0
- package/Dockerfile +14 -0
- package/README.md +507 -36
- package/__e2e__/README.md +160 -0
- package/__e2e__/index.test.ts +339 -0
- package/__e2e__/setup.ts +58 -0
- package/__e2e__/utils/debug-logger.ts +45 -0
- package/__e2e__/utils/test-utils.ts +645 -0
- package/__tests__/index.test.ts +573 -31
- package/__tests__/vaults/secretsmanager.test.ts +460 -0
- package/__tests__/vaults/utils.test.ts +183 -0
- package/__tests__/version.test.ts +8 -0
- package/dist/index.js +36 -10
- package/dist/vaults/secretsmanager.js +44 -43
- package/dist/vaults/utils.js +2 -2
- package/docker-compose.yaml +29 -0
- package/docs/AWS.md +257 -0
- package/jest.config.js +4 -1
- package/jest.e2e.config.js +8 -0
- package/package.json +18 -10
- package/src/index.ts +44 -10
- package/src/vaults/secretsmanager.ts +48 -48
- package/src/vaults/utils.ts +6 -4
- package/website/docs/advanced-usage.mdx +399 -0
- package/website/docs/best-practices.mdx +416 -0
- package/website/docs/cli-reference.mdx +204 -0
- package/website/docs/examples.mdx +960 -0
- package/website/docs/faq.mdx +302 -0
- package/website/docs/index.mdx +56 -0
- package/website/docs/installation.mdx +30 -0
- package/website/docs/overview.mdx +17 -0
- package/website/docs/production-deployment.mdx +622 -0
- package/website/docs/providers/aws-secrets-manager.mdx +28 -0
- package/website/docs/security.mdx +122 -0
- package/website/docs/troubleshooting.mdx +236 -0
- package/website/docs/tutorials/local-dev/devcontainer-localstack.mdx +31 -0
- package/website/docs/tutorials/local-dev/docker-compose.mdx +22 -0
- package/website/docs/tutorials/local-dev/nextjs.mdx +18 -0
- package/website/docs/tutorials/local-dev/node-python-go.mdx +39 -0
- package/website/docs/tutorials/local-dev/quickstart.mdx +23 -0
- package/website/docusaurus.config.ts +89 -0
- package/website/package.json +21 -0
- package/website/sidebars.ts +33 -0
- package/website/src/css/custom.css +1 -0
- package/website/static/img/env-secrets.png +0 -0
- package/website/static/img/favicon.ico +0 -0
- package/website/static/img/logo.svg +4 -0
- package/website/yarn.lock +8764 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# End-to-End Tests
|
|
2
|
+
|
|
3
|
+
This directory contains comprehensive end-to-end tests for the env-secrets CLI tool.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
### LocalStack Setup
|
|
8
|
+
|
|
9
|
+
The e2e tests use LocalStack to emulate AWS Secrets Manager. You need to have LocalStack running before executing the tests.
|
|
10
|
+
|
|
11
|
+
#### Using Docker Compose (Recommended)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Start LocalStack
|
|
15
|
+
docker-compose up -d localstack
|
|
16
|
+
|
|
17
|
+
# Wait for LocalStack to be ready
|
|
18
|
+
docker-compose logs -f localstack
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
#### Manual LocalStack Setup
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Install LocalStack
|
|
25
|
+
pip install localstack
|
|
26
|
+
|
|
27
|
+
# Start LocalStack
|
|
28
|
+
localstack start
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### awslocal Setup
|
|
32
|
+
|
|
33
|
+
The tests require `awslocal` to be installed, which is a wrapper around AWS CLI that automatically points to LocalStack:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Install awslocal using pip (recommended)
|
|
37
|
+
pip install awscli-local
|
|
38
|
+
|
|
39
|
+
# Or using npm
|
|
40
|
+
npm install -g awscli-local
|
|
41
|
+
|
|
42
|
+
# Verify installation
|
|
43
|
+
awslocal --version
|
|
44
|
+
|
|
45
|
+
# Test connectivity to LocalStack
|
|
46
|
+
awslocal sts get-caller-identity
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Note**: The tests will automatically check if `awslocal` is installed and provide helpful error messages if it's missing.
|
|
50
|
+
|
|
51
|
+
## Environment Variables
|
|
52
|
+
|
|
53
|
+
The following environment variables are used by the e2e tests:
|
|
54
|
+
|
|
55
|
+
- `LOCALSTACK_URL`: LocalStack endpoint (default: `http://localhost:4566`)
|
|
56
|
+
- `AWS_ACCESS_KEY_ID`: AWS access key (default: `test`)
|
|
57
|
+
- `AWS_SECRET_ACCESS_KEY`: AWS secret key (default: `test`)
|
|
58
|
+
- `AWS_DEFAULT_REGION`: AWS region (default: `us-east-1`)
|
|
59
|
+
- `DEBUG`: Enable debug output (optional)
|
|
60
|
+
|
|
61
|
+
**Note**: The tests automatically clean up AWS environment variables (like `AWS_PROFILE`, `AWS_SESSION_TOKEN`, etc.) to ensure a clean test environment.
|
|
62
|
+
|
|
63
|
+
## Running Tests
|
|
64
|
+
|
|
65
|
+
### Run All E2E Tests
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm run test:e2e
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Run E2E Tests with Coverage
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm run test:e2e:coverage
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Run Specific Test File
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm run build
|
|
81
|
+
npx jest --config jest.e2e.config.js __e2e__/index.test.ts
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Run Tests with Debug Output
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
DEBUG=1 npm run test:e2e
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Test Structure
|
|
91
|
+
|
|
92
|
+
### Test Categories
|
|
93
|
+
|
|
94
|
+
1. **CLI Help Commands**: Tests for help, version, and general CLI functionality
|
|
95
|
+
2. **AWS Secrets Manager Integration**: Tests for secret retrieval using different credential methods
|
|
96
|
+
3. **Output to File**: Tests for writing secrets to files
|
|
97
|
+
4. **Program Execution**: Tests for executing programs with injected environment variables
|
|
98
|
+
5. **Error Handling**: Tests for various error scenarios
|
|
99
|
+
|
|
100
|
+
### Test Utilities
|
|
101
|
+
|
|
102
|
+
The `utils/test-utils.ts` file provides helper functions:
|
|
103
|
+
|
|
104
|
+
- `LocalStackHelper`: Manages LocalStack operations (create/delete secrets, wait for readiness)
|
|
105
|
+
- `cli()`: Execute CLI commands
|
|
106
|
+
- `cliWithEnv()`: Execute CLI commands with custom environment variables
|
|
107
|
+
- `createTempFile()` / `cleanupTempFile()`: Manage temporary files
|
|
108
|
+
- `createTestProfile()` / `restoreTestProfile()`: Manage AWS profiles
|
|
109
|
+
|
|
110
|
+
### Test Secrets
|
|
111
|
+
|
|
112
|
+
The tests create and use several test secrets:
|
|
113
|
+
|
|
114
|
+
- `test-secret-basic`: Basic JSON secret with API key and database URL
|
|
115
|
+
- `test-secret-simple`: Simple string secret
|
|
116
|
+
- `test-secret-complex`: Complex JSON with nested objects and arrays
|
|
117
|
+
- `test-secret-special-chars`: Secret with special characters
|
|
118
|
+
|
|
119
|
+
## Troubleshooting
|
|
120
|
+
|
|
121
|
+
### LocalStack Not Starting
|
|
122
|
+
|
|
123
|
+
If LocalStack fails to start:
|
|
124
|
+
|
|
125
|
+
1. Check if port 4566 is available
|
|
126
|
+
2. Ensure Docker is running
|
|
127
|
+
3. Check LocalStack logs: `docker-compose logs localstack`
|
|
128
|
+
|
|
129
|
+
### awslocal Issues
|
|
130
|
+
|
|
131
|
+
If awslocal commands fail:
|
|
132
|
+
|
|
133
|
+
1. Verify awslocal is installed: `awslocal --version`
|
|
134
|
+
2. Test LocalStack connectivity: `awslocal sts get-caller-identity`
|
|
135
|
+
3. Check if LocalStack is running: `docker-compose ps`
|
|
136
|
+
|
|
137
|
+
### Test Failures
|
|
138
|
+
|
|
139
|
+
Common issues and solutions:
|
|
140
|
+
|
|
141
|
+
1. **Timeout errors**: Increase timeout in `jest.e2e.config.js`
|
|
142
|
+
2. **Permission errors**: Ensure proper file permissions for AWS credentials
|
|
143
|
+
3. **Network errors**: Check LocalStack endpoint URL and connectivity
|
|
144
|
+
|
|
145
|
+
## Continuous Integration
|
|
146
|
+
|
|
147
|
+
The e2e tests are designed to run in CI environments. Make sure to:
|
|
148
|
+
|
|
149
|
+
1. Start LocalStack before running tests
|
|
150
|
+
2. Set appropriate environment variables
|
|
151
|
+
3. Install AWS CLI in the CI environment
|
|
152
|
+
4. Allow sufficient time for LocalStack to start
|
|
153
|
+
|
|
154
|
+
## Coverage Reports
|
|
155
|
+
|
|
156
|
+
Coverage reports are generated in the `coverage-e2e/` directory:
|
|
157
|
+
|
|
158
|
+
- `coverage-e2e/lcov-report/index.html`: HTML coverage report
|
|
159
|
+
- `coverage-e2e/lcov.info`: LCOV format for CI integration
|
|
160
|
+
- `coverage-e2e/coverage-final.json`: JSON coverage data
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LocalStackHelper,
|
|
3
|
+
cli,
|
|
4
|
+
cliWithEnv,
|
|
5
|
+
createTempFile,
|
|
6
|
+
cleanupTempFile,
|
|
7
|
+
createTestProfile,
|
|
8
|
+
restoreTestProfile,
|
|
9
|
+
TestSecret,
|
|
10
|
+
CreatedSecret
|
|
11
|
+
} from './utils/test-utils';
|
|
12
|
+
import { debugLog } from './utils/debug-logger';
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import * as os from 'os';
|
|
16
|
+
|
|
17
|
+
describe('End-to-End Tests', () => {
|
|
18
|
+
let localStack: LocalStackHelper;
|
|
19
|
+
let awsDir: string | undefined;
|
|
20
|
+
|
|
21
|
+
beforeAll(async () => {
|
|
22
|
+
localStack = new LocalStackHelper();
|
|
23
|
+
await localStack.waitForLocalStack();
|
|
24
|
+
|
|
25
|
+
// Create test AWS profile
|
|
26
|
+
awsDir = createTestProfile();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterAll(async () => {
|
|
30
|
+
// Clean up all secrets created during this test run
|
|
31
|
+
await localStack.cleanupRunSecrets();
|
|
32
|
+
|
|
33
|
+
// Restore AWS profile
|
|
34
|
+
restoreTestProfile(awsDir);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Helper function to create a secret for a test
|
|
38
|
+
const createTestSecret = async (
|
|
39
|
+
secret: TestSecret,
|
|
40
|
+
region?: string
|
|
41
|
+
): Promise<CreatedSecret> => {
|
|
42
|
+
return await localStack.createSecret(secret, region);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Helper function to create LocalStack environment variables
|
|
46
|
+
const getLocalStackEnv = (overrides: Record<string, string> = {}) => ({
|
|
47
|
+
AWS_ENDPOINT_URL: process.env.LOCALSTACK_URL || 'http://localhost:4566',
|
|
48
|
+
AWS_ACCESS_KEY_ID: 'test',
|
|
49
|
+
AWS_SECRET_ACCESS_KEY: 'test',
|
|
50
|
+
AWS_DEFAULT_REGION: 'us-east-1',
|
|
51
|
+
...overrides
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('CLI Help Commands', () => {
|
|
55
|
+
test('should show general help', async () => {
|
|
56
|
+
const result = await cli(['-h']);
|
|
57
|
+
expect(result.code).toBe(0);
|
|
58
|
+
expect(result.stdout).toContain('env-secrets');
|
|
59
|
+
expect(result.stdout).toContain('pull secrets from vaults');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('should show AWS command help', async () => {
|
|
63
|
+
const result = await cli(['aws', '-h']);
|
|
64
|
+
expect(result.code).toBe(0);
|
|
65
|
+
expect(result.stdout).toContain('get secrets from AWS secrets manager');
|
|
66
|
+
expect(result.stdout).toContain('--secret');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('should show version', async () => {
|
|
70
|
+
const result = await cli(['--version']);
|
|
71
|
+
expect(result.code).toBe(0);
|
|
72
|
+
expect(result.stdout).toMatch(/^\d+\.\d+\.\d+/);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('AWS Secrets Manager Integration', () => {
|
|
77
|
+
describe('Using Default AWS Credentials', () => {
|
|
78
|
+
test('should retrieve basic JSON secret', async () => {
|
|
79
|
+
const secret = await createTestSecret({
|
|
80
|
+
name: 'test-secret-basic',
|
|
81
|
+
value:
|
|
82
|
+
'{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
|
|
83
|
+
description: 'Basic test secret with API key and database URL'
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const result = await cliWithEnv(
|
|
87
|
+
['aws', '-s', secret.prefixedName],
|
|
88
|
+
getLocalStackEnv()
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
expect(result.code).toBe(0);
|
|
92
|
+
expect(result.stderr).toBe('');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('should retrieve simple string secret', async () => {
|
|
96
|
+
const secret = await createTestSecret({
|
|
97
|
+
name: 'test-secret-simple',
|
|
98
|
+
value: 'simple-string-value',
|
|
99
|
+
description: 'Simple string secret'
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const result = await cliWithEnv(
|
|
103
|
+
['aws', '-s', secret.prefixedName],
|
|
104
|
+
getLocalStackEnv()
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
expect(result.code).toBe(0);
|
|
108
|
+
expect(result.stderr).toBe('');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('should retrieve complex JSON secret', async () => {
|
|
112
|
+
const secret = await createTestSecret({
|
|
113
|
+
name: 'test-secret-complex',
|
|
114
|
+
value:
|
|
115
|
+
'{"NESTED": {"KEY": "value"}, "ARRAY": [1, 2, 3], "BOOLEAN": true, "NUMBER": 42}',
|
|
116
|
+
description: 'Complex JSON secret'
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const result = await cliWithEnv(
|
|
120
|
+
['aws', '-s', secret.prefixedName],
|
|
121
|
+
getLocalStackEnv()
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
expect(result.code).toBe(0);
|
|
125
|
+
expect(result.stderr).toBe('');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('should retrieve secret with special characters', async () => {
|
|
129
|
+
const secret = await createTestSecret({
|
|
130
|
+
name: 'test-secret-special-chars',
|
|
131
|
+
value:
|
|
132
|
+
'{"PASSWORD": "p@ssw0rd!#$%", "URL": "https://api.example.com/v1?key=value&other=test"}',
|
|
133
|
+
description: 'Secret with special characters'
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const result = await cliWithEnv(
|
|
137
|
+
['aws', '-s', secret.prefixedName],
|
|
138
|
+
getLocalStackEnv()
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
expect(result.code).toBe(0);
|
|
142
|
+
expect(result.stderr).toBe('');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('should handle non-existent secret', async () => {
|
|
146
|
+
const result = await cliWithEnv(
|
|
147
|
+
['aws', '-s', 'non-existent-secret'],
|
|
148
|
+
getLocalStackEnv()
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
expect(result.code).toBe(0);
|
|
152
|
+
expect(result.stderr).toContain('non-existent-secret not found');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('should work with custom region', async () => {
|
|
156
|
+
const secret = await createTestSecret(
|
|
157
|
+
{
|
|
158
|
+
name: `test-secret-region-${Date.now()}`,
|
|
159
|
+
value:
|
|
160
|
+
'{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
|
|
161
|
+
description: 'Basic test secret for us-west-2'
|
|
162
|
+
},
|
|
163
|
+
'us-west-2'
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const result = await cliWithEnv(
|
|
167
|
+
['aws', '-s', secret.prefixedName, '-r', 'us-west-2'],
|
|
168
|
+
getLocalStackEnv()
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
expect(result.code).toBe(0);
|
|
172
|
+
expect(result.stderr).toBe('');
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('Using AWS Profile', () => {
|
|
177
|
+
test('should retrieve secret using default profile', async () => {
|
|
178
|
+
const secret = await createTestSecret({
|
|
179
|
+
name: 'test-secret-profile',
|
|
180
|
+
value:
|
|
181
|
+
'{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
|
|
182
|
+
description: 'Secret for profile test'
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const result = await cliWithEnv(
|
|
186
|
+
['aws', '-s', secret.prefixedName],
|
|
187
|
+
getLocalStackEnv()
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
expect(result.code).toBe(0);
|
|
191
|
+
expect(result.stderr).toBe('');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test('should retrieve secret using custom profile', async () => {
|
|
195
|
+
const secret = await createTestSecret({
|
|
196
|
+
name: 'test-secret-profile-custom',
|
|
197
|
+
value:
|
|
198
|
+
'{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
|
|
199
|
+
description: 'Secret for custom profile test'
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const result = await cliWithEnv(
|
|
203
|
+
['aws', '-s', secret.prefixedName, '-p', 'env-secrets-test'],
|
|
204
|
+
getLocalStackEnv()
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
expect(result.code).toBe(0);
|
|
208
|
+
expect(result.stderr).toBe('');
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('Output to File', () => {
|
|
213
|
+
test('should write secrets to file', async () => {
|
|
214
|
+
debugLog('Starting test...');
|
|
215
|
+
|
|
216
|
+
const secret = await createTestSecret({
|
|
217
|
+
name: `test-secret-file-${Date.now()}`,
|
|
218
|
+
value:
|
|
219
|
+
'{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
|
|
220
|
+
description: 'Secret for file output test'
|
|
221
|
+
});
|
|
222
|
+
debugLog('Secret created successfully');
|
|
223
|
+
|
|
224
|
+
const tempFile = path.join(
|
|
225
|
+
os.tmpdir(),
|
|
226
|
+
`env-secrets-test-${Date.now()}.env`
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
const result = await cliWithEnv(
|
|
230
|
+
['aws', '-s', secret.prefixedName, '-o', tempFile],
|
|
231
|
+
getLocalStackEnv()
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
debugLog('CLI result:', {
|
|
235
|
+
code: result.code,
|
|
236
|
+
stdout: result.stdout,
|
|
237
|
+
stderr: result.stderr
|
|
238
|
+
});
|
|
239
|
+
expect(result.code).toBe(0);
|
|
240
|
+
expect(result.stdout).toContain(`Secrets written to ${tempFile}`);
|
|
241
|
+
|
|
242
|
+
// Verify file contents
|
|
243
|
+
const fileContent = fs.readFileSync(tempFile, 'utf8');
|
|
244
|
+
expect(fileContent).toContain('API_KEY=secret123');
|
|
245
|
+
expect(fileContent).toContain(
|
|
246
|
+
'DATABASE_URL=postgres://localhost:5432/test'
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
cleanupTempFile(tempFile);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test('should not overwrite existing file', async () => {
|
|
253
|
+
const tempFile = createTempFile('existing content');
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const result = await cliWithEnv(
|
|
257
|
+
['aws', '-s', 'test-secret-basic', '-o', tempFile],
|
|
258
|
+
getLocalStackEnv()
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
expect(result.code).toBe(1);
|
|
262
|
+
expect(result.stderr).toContain(
|
|
263
|
+
'already exists and will not be overwritten'
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
// Verify file was not modified
|
|
267
|
+
const fileContent = fs.readFileSync(tempFile, 'utf8');
|
|
268
|
+
expect(fileContent).toBe('existing content');
|
|
269
|
+
} finally {
|
|
270
|
+
cleanupTempFile(tempFile);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe('Program Execution', () => {
|
|
276
|
+
test('should execute program with injected environment variables', async () => {
|
|
277
|
+
const secret = await createTestSecret({
|
|
278
|
+
name: `test-secret-exec-${Date.now()}`,
|
|
279
|
+
value:
|
|
280
|
+
'{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
|
|
281
|
+
description: 'Secret for program execution test'
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const result = await cliWithEnv(
|
|
285
|
+
['aws', '-s', secret.prefixedName, 'echo', '$API_KEY'],
|
|
286
|
+
getLocalStackEnv()
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
expect(result.code).toBe(0);
|
|
290
|
+
|
|
291
|
+
// In test mode, the CLI outputs the environment variables as JSON
|
|
292
|
+
const envVars = JSON.parse(result.stdout.trim());
|
|
293
|
+
expect(envVars.API_KEY).toBe('secret123');
|
|
294
|
+
expect(envVars.DATABASE_URL).toBe('postgres://localhost:5432/test');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test('should handle program execution errors gracefully', async () => {
|
|
298
|
+
const secret = await createTestSecret({
|
|
299
|
+
name: `test-secret-error-${Date.now()}`,
|
|
300
|
+
value: '{"API_KEY": "secret123"}',
|
|
301
|
+
description: 'Secret for error handling test'
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
const result = await cliWithEnv(
|
|
305
|
+
['aws', '-s', secret.prefixedName, 'nonexistent-command'],
|
|
306
|
+
getLocalStackEnv()
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
expect(result.code).toBe(0);
|
|
310
|
+
|
|
311
|
+
// In test mode, the CLI outputs the environment variables as JSON
|
|
312
|
+
const envVars = JSON.parse(result.stdout.trim());
|
|
313
|
+
expect(envVars.API_KEY).toBe('secret123');
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
describe('Error Handling', () => {
|
|
318
|
+
test('should handle invalid AWS credentials', async () => {
|
|
319
|
+
const result = await cliWithEnv(
|
|
320
|
+
['aws', '-s', 'test-secret-basic'],
|
|
321
|
+
getLocalStackEnv({
|
|
322
|
+
AWS_ACCESS_KEY_ID: 'invalid',
|
|
323
|
+
AWS_SECRET_ACCESS_KEY: 'invalid'
|
|
324
|
+
})
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
// LocalStack accepts any credentials, so this should succeed
|
|
328
|
+
expect(result.code).toBe(0);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test('should handle missing secret parameter', async () => {
|
|
332
|
+
const result = await cliWithEnv(['aws'], getLocalStackEnv());
|
|
333
|
+
|
|
334
|
+
expect(result.code).toBe(1);
|
|
335
|
+
expect(result.stderr).toContain('required option');
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
});
|
package/__e2e__/setup.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// E2E Test Setup
|
|
2
|
+
// This file runs before all e2e tests
|
|
3
|
+
|
|
4
|
+
import { LocalStackHelper, checkAwslocalInstalled } from './utils/test-utils';
|
|
5
|
+
import { debugLog, debugError } from './utils/debug-logger';
|
|
6
|
+
|
|
7
|
+
// Increase timeout for e2e tests
|
|
8
|
+
jest.setTimeout(30000);
|
|
9
|
+
|
|
10
|
+
// Global setup for e2e tests
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
debugLog('Setting up E2E test environment...');
|
|
13
|
+
|
|
14
|
+
// Check if awslocal is installed
|
|
15
|
+
await checkAwslocalInstalled();
|
|
16
|
+
|
|
17
|
+
// Check if LocalStack is available
|
|
18
|
+
const localStack = new LocalStackHelper();
|
|
19
|
+
try {
|
|
20
|
+
await localStack.waitForLocalStack();
|
|
21
|
+
debugLog('LocalStack is ready for E2E tests');
|
|
22
|
+
} catch (error) {
|
|
23
|
+
debugError(
|
|
24
|
+
'LocalStack is not available, some tests may fail:',
|
|
25
|
+
error.message
|
|
26
|
+
);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Global cleanup
|
|
32
|
+
afterAll(async () => {
|
|
33
|
+
debugLog('Cleaning up E2E test environment...');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Suppress console.log during tests unless DEBUG is set
|
|
37
|
+
// But always show console.error and console.warn for debugging
|
|
38
|
+
const originalConsoleLog = console.log;
|
|
39
|
+
const originalConsoleInfo = console.info;
|
|
40
|
+
const originalConsoleDebug = console.debug;
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
if (!process.env.DEBUG) {
|
|
44
|
+
console.log = jest.fn();
|
|
45
|
+
console.info = jest.fn();
|
|
46
|
+
console.debug = jest.fn();
|
|
47
|
+
// Keep console.error and console.warn enabled for debugging
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
if (!process.env.DEBUG) {
|
|
53
|
+
console.log = originalConsoleLog;
|
|
54
|
+
console.info = originalConsoleInfo;
|
|
55
|
+
console.debug = originalConsoleDebug;
|
|
56
|
+
// console.error and console.warn are already enabled
|
|
57
|
+
}
|
|
58
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug logging utility for e2e tests
|
|
3
|
+
* Only outputs logs when DEBUG environment variable is set
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class DebugLogger {
|
|
7
|
+
private static isDebugEnabled(): boolean {
|
|
8
|
+
return process.env.DEBUG === 'true' || process.env.DEBUG === '1';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static log(message: string, ...args: any[]): void {
|
|
12
|
+
if (DebugLogger.isDebugEnabled()) {
|
|
13
|
+
console.log(message, ...args);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static error(message: string, ...args: any[]): void {
|
|
18
|
+
// Always show errors for debugging
|
|
19
|
+
console.error(message, ...args);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static warn(message: string, ...args: any[]): void {
|
|
23
|
+
// Always show warnings for debugging
|
|
24
|
+
console.warn(message, ...args);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static info(message: string, ...args: any[]): void {
|
|
28
|
+
if (DebugLogger.isDebugEnabled()) {
|
|
29
|
+
console.info(message, ...args);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static debug(message: string, ...args: any[]): void {
|
|
34
|
+
if (DebugLogger.isDebugEnabled()) {
|
|
35
|
+
console.debug(message, ...args);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Export convenience functions
|
|
41
|
+
export const debugLog = DebugLogger.log;
|
|
42
|
+
export const debugError = DebugLogger.error;
|
|
43
|
+
export const debugWarn = DebugLogger.warn;
|
|
44
|
+
export const debugInfo = DebugLogger.info;
|
|
45
|
+
export const debugDebug = DebugLogger.debug;
|