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.
Files changed (61) hide show
  1. package/.devcontainer/devcontainer.json +33 -0
  2. package/.dockerignore +9 -0
  3. package/.eslintignore +4 -2
  4. package/.github/dependabot.yml +4 -0
  5. package/.github/workflows/build-main.yml +6 -2
  6. package/.github/workflows/deploy-docs.yml +50 -0
  7. package/.github/workflows/e2e-tests.yaml +54 -0
  8. package/.github/workflows/lint.yaml +6 -2
  9. package/.github/workflows/release.yml +13 -3
  10. package/.github/workflows/snyk.yaml +5 -1
  11. package/.github/workflows/unittests.yaml +18 -6
  12. package/.lintstagedrc +2 -7
  13. package/.prettierignore +6 -0
  14. package/AGENTS.md +149 -0
  15. package/Dockerfile +14 -0
  16. package/README.md +507 -36
  17. package/__e2e__/README.md +160 -0
  18. package/__e2e__/index.test.ts +339 -0
  19. package/__e2e__/setup.ts +58 -0
  20. package/__e2e__/utils/debug-logger.ts +45 -0
  21. package/__e2e__/utils/test-utils.ts +645 -0
  22. package/__tests__/index.test.ts +573 -31
  23. package/__tests__/vaults/secretsmanager.test.ts +460 -0
  24. package/__tests__/vaults/utils.test.ts +183 -0
  25. package/__tests__/version.test.ts +8 -0
  26. package/dist/index.js +36 -10
  27. package/dist/vaults/secretsmanager.js +44 -43
  28. package/dist/vaults/utils.js +2 -2
  29. package/docker-compose.yaml +29 -0
  30. package/docs/AWS.md +257 -0
  31. package/jest.config.js +4 -1
  32. package/jest.e2e.config.js +8 -0
  33. package/package.json +18 -10
  34. package/src/index.ts +44 -10
  35. package/src/vaults/secretsmanager.ts +48 -48
  36. package/src/vaults/utils.ts +6 -4
  37. package/website/docs/advanced-usage.mdx +399 -0
  38. package/website/docs/best-practices.mdx +416 -0
  39. package/website/docs/cli-reference.mdx +204 -0
  40. package/website/docs/examples.mdx +960 -0
  41. package/website/docs/faq.mdx +302 -0
  42. package/website/docs/index.mdx +56 -0
  43. package/website/docs/installation.mdx +30 -0
  44. package/website/docs/overview.mdx +17 -0
  45. package/website/docs/production-deployment.mdx +622 -0
  46. package/website/docs/providers/aws-secrets-manager.mdx +28 -0
  47. package/website/docs/security.mdx +122 -0
  48. package/website/docs/troubleshooting.mdx +236 -0
  49. package/website/docs/tutorials/local-dev/devcontainer-localstack.mdx +31 -0
  50. package/website/docs/tutorials/local-dev/docker-compose.mdx +22 -0
  51. package/website/docs/tutorials/local-dev/nextjs.mdx +18 -0
  52. package/website/docs/tutorials/local-dev/node-python-go.mdx +39 -0
  53. package/website/docs/tutorials/local-dev/quickstart.mdx +23 -0
  54. package/website/docusaurus.config.ts +89 -0
  55. package/website/package.json +21 -0
  56. package/website/sidebars.ts +33 -0
  57. package/website/src/css/custom.css +1 -0
  58. package/website/static/img/env-secrets.png +0 -0
  59. package/website/static/img/favicon.ico +0 -0
  60. package/website/static/img/logo.svg +4 -0
  61. package/website/yarn.lock +8764 -0
@@ -0,0 +1,460 @@
1
+ import {
2
+ SecretsManagerClient,
3
+ GetSecretValueCommand
4
+ } from '@aws-sdk/client-secrets-manager';
5
+ import { STSClient, GetCallerIdentityCommand } from '@aws-sdk/client-sts';
6
+ import { fromIni } from '@aws-sdk/credential-providers';
7
+
8
+ // Mock the AWS SDK and dependencies
9
+ jest.mock('@aws-sdk/client-secrets-manager');
10
+ jest.mock('@aws-sdk/client-sts');
11
+ jest.mock('@aws-sdk/credential-providers');
12
+ jest.mock('debug', () => {
13
+ const mockDebug = jest.fn();
14
+ mockDebug.mockReturnValue(jest.fn());
15
+ return mockDebug;
16
+ });
17
+
18
+ // Import the module under test after mocking
19
+ import { secretsmanager } from '../../src/vaults/secretsmanager';
20
+
21
+ // Type the mocked functions
22
+ const mockSecretsManagerClient = SecretsManagerClient as jest.MockedClass<
23
+ typeof SecretsManagerClient
24
+ >;
25
+ const mockSTSClient = STSClient as jest.MockedClass<typeof STSClient>;
26
+ const mockFromIni = fromIni as jest.MockedFunction<typeof fromIni>;
27
+
28
+ describe('secretsmanager', () => {
29
+ let originalEnv: NodeJS.ProcessEnv;
30
+ let mockSecretsManagerSend: jest.MockedFunction<any>;
31
+ let mockSTSSend: jest.MockedFunction<any>;
32
+
33
+ beforeEach(() => {
34
+ // Clear all mocks
35
+ jest.clearAllMocks();
36
+
37
+ // Reset process.env
38
+ originalEnv = { ...process.env };
39
+ process.env = { ...originalEnv };
40
+
41
+ // Setup AWS client mocks
42
+ mockSecretsManagerSend = jest.fn();
43
+ mockSTSSend = jest.fn();
44
+
45
+ mockSecretsManagerClient.prototype.send = mockSecretsManagerSend;
46
+ mockSTSClient.prototype.send = mockSTSSend;
47
+ });
48
+
49
+ afterEach(() => {
50
+ // Restore process.env
51
+ process.env = originalEnv;
52
+ jest.restoreAllMocks();
53
+ });
54
+
55
+ describe('AWS connection', () => {
56
+ it('should successfully connect to AWS and retrieve secrets', async () => {
57
+ // Mock successful STS connection
58
+ mockSTSSend.mockResolvedValueOnce({
59
+ Account: '123456789012',
60
+ UserId: 'test-user'
61
+ });
62
+
63
+ // Mock successful secret retrieval
64
+ mockSecretsManagerSend.mockResolvedValueOnce({
65
+ SecretString:
66
+ '{"API_KEY": "test-key", "DATABASE_URL": "postgres://localhost:5432/db"}'
67
+ });
68
+
69
+ const result = await secretsmanager({
70
+ secret: 'my-secret',
71
+ region: 'us-east-1'
72
+ });
73
+
74
+ expect(mockSTSSend).toHaveBeenCalledWith(
75
+ expect.any(GetCallerIdentityCommand)
76
+ );
77
+ expect(mockSecretsManagerSend).toHaveBeenCalledWith(
78
+ expect.any(GetSecretValueCommand)
79
+ );
80
+ expect(result).toEqual({
81
+ API_KEY: 'test-key',
82
+ DATABASE_URL: 'postgres://localhost:5432/db'
83
+ });
84
+ });
85
+
86
+ it('should return empty object when AWS connection fails', async () => {
87
+ // Mock failed STS connection
88
+ mockSTSSend.mockRejectedValueOnce(new Error('AWS credentials not found'));
89
+
90
+ const result = await secretsmanager({
91
+ secret: 'my-secret',
92
+ region: 'us-east-1'
93
+ });
94
+
95
+ expect(mockSTSSend).toHaveBeenCalledWith(
96
+ expect.any(GetCallerIdentityCommand)
97
+ );
98
+ expect(mockSecretsManagerSend).not.toHaveBeenCalled();
99
+ expect(result).toEqual({});
100
+ });
101
+ });
102
+
103
+ describe('credential handling', () => {
104
+ beforeEach(() => {
105
+ // Setup successful connection for credential tests
106
+ mockSTSSend.mockResolvedValue({
107
+ Account: '123456789012'
108
+ });
109
+ });
110
+
111
+ it('should use profile credentials when profile is provided', async () => {
112
+ const mockCredentials = jest.fn().mockResolvedValue({
113
+ accessKeyId: 'test',
114
+ secretAccessKey: 'test'
115
+ });
116
+ mockFromIni.mockReturnValueOnce(mockCredentials);
117
+
118
+ mockSecretsManagerSend.mockResolvedValueOnce({
119
+ SecretString: '{"KEY": "value"}'
120
+ });
121
+
122
+ await secretsmanager({
123
+ secret: 'test-secret',
124
+ profile: 'my-profile',
125
+ region: 'us-east-1'
126
+ });
127
+
128
+ expect(mockFromIni).toHaveBeenCalledWith({ profile: 'my-profile' });
129
+ expect(mockSecretsManagerClient).toHaveBeenCalledWith({
130
+ region: 'us-east-1',
131
+ credentials: mockCredentials
132
+ });
133
+ });
134
+
135
+ it('should use environment variables when AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are set', async () => {
136
+ process.env.AWS_ACCESS_KEY_ID = 'env-access-key';
137
+ process.env.AWS_SECRET_ACCESS_KEY = 'env-secret-key';
138
+
139
+ mockSecretsManagerSend.mockResolvedValueOnce({
140
+ SecretString: '{"KEY": "value"}'
141
+ });
142
+
143
+ await secretsmanager({
144
+ secret: 'test-secret',
145
+ region: 'us-east-1'
146
+ });
147
+
148
+ expect(mockSecretsManagerClient).toHaveBeenCalledWith({
149
+ region: 'us-east-1',
150
+ credentials: undefined
151
+ });
152
+ });
153
+
154
+ it('should use default profile when no profile or environment variables are provided', async () => {
155
+ const mockCredentials = jest.fn().mockResolvedValue({
156
+ accessKeyId: 'default',
157
+ secretAccessKey: 'default'
158
+ });
159
+ mockFromIni.mockReturnValueOnce(mockCredentials);
160
+
161
+ mockSecretsManagerSend.mockResolvedValueOnce({
162
+ SecretString: '{"KEY": "value"}'
163
+ });
164
+
165
+ await secretsmanager({
166
+ secret: 'test-secret',
167
+ region: 'us-east-1'
168
+ });
169
+
170
+ expect(mockFromIni).toHaveBeenCalledWith({ profile: 'default' });
171
+ expect(mockSecretsManagerClient).toHaveBeenCalledWith({
172
+ region: 'us-east-1',
173
+ credentials: mockCredentials
174
+ });
175
+ });
176
+ });
177
+
178
+ describe('secret retrieval', () => {
179
+ beforeEach(() => {
180
+ // Setup successful connection for secret retrieval tests
181
+ mockSTSSend.mockResolvedValue({
182
+ Account: '123456789012'
183
+ });
184
+ });
185
+
186
+ it('should retrieve and parse JSON secret successfully', async () => {
187
+ mockSecretsManagerSend.mockResolvedValueOnce({
188
+ SecretString:
189
+ '{"API_KEY": "test-api-key", "DATABASE_URL": "postgres://localhost:5432/db"}'
190
+ });
191
+
192
+ const result = await secretsmanager({
193
+ secret: 'my-secret',
194
+ region: 'us-east-1'
195
+ });
196
+
197
+ expect(result).toEqual({
198
+ API_KEY: 'test-api-key',
199
+ DATABASE_URL: 'postgres://localhost:5432/db'
200
+ });
201
+ });
202
+
203
+ it('should handle non-JSON secret values', async () => {
204
+ mockSecretsManagerSend.mockResolvedValueOnce({
205
+ SecretString: 'plain-text-secret'
206
+ });
207
+
208
+ const result = await secretsmanager({
209
+ secret: 'my-secret',
210
+ region: 'us-east-1'
211
+ });
212
+
213
+ expect(result).toEqual({});
214
+ });
215
+
216
+ it('should handle empty secret values', async () => {
217
+ mockSecretsManagerSend.mockResolvedValueOnce({
218
+ SecretString: ''
219
+ });
220
+
221
+ const result = await secretsmanager({
222
+ secret: 'my-secret',
223
+ region: 'us-east-1'
224
+ });
225
+
226
+ expect(result).toEqual({});
227
+ });
228
+
229
+ it('should handle undefined secret values', async () => {
230
+ mockSecretsManagerSend.mockResolvedValueOnce({
231
+ SecretString: undefined
232
+ });
233
+
234
+ const result = await secretsmanager({
235
+ secret: 'my-secret',
236
+ region: 'us-east-1'
237
+ });
238
+
239
+ expect(result).toEqual({});
240
+ });
241
+
242
+ it('should handle malformed JSON in secret values', async () => {
243
+ mockSecretsManagerSend.mockResolvedValueOnce({
244
+ SecretString: '{"invalid": json}'
245
+ });
246
+
247
+ const result = await secretsmanager({
248
+ secret: 'my-secret',
249
+ region: 'us-east-1'
250
+ });
251
+
252
+ expect(result).toEqual({});
253
+ });
254
+
255
+ it('should handle secrets with special characters', async () => {
256
+ mockSecretsManagerSend.mockResolvedValueOnce({
257
+ SecretString: '{"SPECIAL_KEY": "value with spaces & symbols!@#$%"}'
258
+ });
259
+
260
+ const result = await secretsmanager({
261
+ secret: 'my-secret',
262
+ region: 'us-east-1'
263
+ });
264
+
265
+ expect(result).toEqual({
266
+ SPECIAL_KEY: 'value with spaces & symbols!@#$%'
267
+ });
268
+ });
269
+
270
+ it('should handle secrets with numeric and boolean values', async () => {
271
+ mockSecretsManagerSend.mockResolvedValueOnce({
272
+ SecretString: '{"PORT": 5432, "DEBUG": true, "TIMEOUT": 30000}'
273
+ });
274
+
275
+ const result = await secretsmanager({
276
+ secret: 'my-secret',
277
+ region: 'us-east-1'
278
+ });
279
+
280
+ expect(result).toEqual({
281
+ PORT: 5432,
282
+ DEBUG: true,
283
+ TIMEOUT: 30000
284
+ });
285
+ });
286
+ });
287
+
288
+ describe('error handling', () => {
289
+ beforeEach(() => {
290
+ // Setup successful connection for error handling tests
291
+ mockSTSSend.mockResolvedValue({
292
+ Account: '123456789012'
293
+ });
294
+ });
295
+
296
+ it('should handle ResourceNotFoundException', async () => {
297
+ const error = new Error('Secret not found');
298
+ (error as any).name = 'ResourceNotFoundException';
299
+
300
+ mockSecretsManagerSend.mockRejectedValueOnce(error);
301
+
302
+ const result = await secretsmanager({
303
+ secret: 'non-existent-secret',
304
+ region: 'us-east-1'
305
+ });
306
+
307
+ expect(result).toEqual({});
308
+ });
309
+
310
+ it('should handle ConfigError', async () => {
311
+ const error = new Error('Invalid configuration');
312
+ (error as any).name = 'ConfigError';
313
+ (error as any).message = 'Invalid AWS configuration';
314
+
315
+ mockSecretsManagerSend.mockRejectedValueOnce(error);
316
+
317
+ const result = await secretsmanager({
318
+ secret: 'my-secret',
319
+ region: 'us-east-1'
320
+ });
321
+
322
+ expect(result).toEqual({});
323
+ });
324
+
325
+ it('should handle generic AWS errors', async () => {
326
+ const error = new Error('Generic AWS error');
327
+ (error as any).name = 'GenericError';
328
+
329
+ mockSecretsManagerSend.mockRejectedValueOnce(error);
330
+
331
+ const result = await secretsmanager({
332
+ secret: 'my-secret',
333
+ region: 'us-east-1'
334
+ });
335
+
336
+ expect(result).toEqual({});
337
+ });
338
+
339
+ it('should handle errors without name property', async () => {
340
+ const error = new Error('Error without name');
341
+
342
+ mockSecretsManagerSend.mockRejectedValueOnce(error);
343
+
344
+ const result = await secretsmanager({
345
+ secret: 'my-secret',
346
+ region: 'us-east-1'
347
+ });
348
+
349
+ expect(result).toEqual({});
350
+ });
351
+
352
+ it('should handle non-Error objects', async () => {
353
+ const error = 'String error';
354
+
355
+ mockSecretsManagerSend.mockRejectedValueOnce(error);
356
+
357
+ const result = await secretsmanager({
358
+ secret: 'my-secret',
359
+ region: 'us-east-1'
360
+ });
361
+
362
+ expect(result).toEqual({});
363
+ });
364
+ });
365
+
366
+ describe('region handling', () => {
367
+ beforeEach(() => {
368
+ mockSTSSend.mockResolvedValue({
369
+ Account: '123456789012'
370
+ });
371
+ });
372
+
373
+ it('should use provided region', async () => {
374
+ mockSecretsManagerSend.mockResolvedValueOnce({
375
+ SecretString: '{"KEY": "value"}'
376
+ });
377
+
378
+ await secretsmanager({
379
+ secret: 'my-secret',
380
+ region: 'ap-southeast-1'
381
+ });
382
+
383
+ expect(mockSecretsManagerClient).toHaveBeenCalledWith({
384
+ region: 'ap-southeast-1',
385
+ credentials: undefined
386
+ });
387
+ });
388
+
389
+ it('should handle undefined region', async () => {
390
+ mockSecretsManagerSend.mockResolvedValueOnce({
391
+ SecretString: '{"KEY": "value"}'
392
+ });
393
+
394
+ await secretsmanager({
395
+ secret: 'my-secret'
396
+ });
397
+
398
+ expect(mockSecretsManagerClient).toHaveBeenCalledWith({
399
+ region: undefined,
400
+ credentials: undefined
401
+ });
402
+ });
403
+ });
404
+
405
+ describe('integration scenarios', () => {
406
+ it('should handle complete successful flow with profile credentials', async () => {
407
+ const mockCredentials = jest.fn().mockResolvedValue({
408
+ accessKeyId: 'test',
409
+ secretAccessKey: 'test'
410
+ });
411
+ mockFromIni.mockReturnValueOnce(mockCredentials);
412
+
413
+ mockSTSSend.mockResolvedValueOnce({
414
+ Account: '123456789012',
415
+ UserId: 'test-user'
416
+ });
417
+
418
+ mockSecretsManagerSend.mockResolvedValueOnce({
419
+ SecretString: '{"API_KEY": "test-key", "DB_PASSWORD": "secret123"}'
420
+ });
421
+
422
+ const result = await secretsmanager({
423
+ secret: 'my-app-secrets',
424
+ profile: 'production',
425
+ region: 'us-west-2'
426
+ });
427
+
428
+ expect(mockFromIni).toHaveBeenCalledWith({ profile: 'production' });
429
+ expect(mockSTSClient).toHaveBeenCalledWith({ region: 'us-west-2' });
430
+ expect(mockSecretsManagerClient).toHaveBeenCalledWith({
431
+ region: 'us-west-2',
432
+ credentials: mockCredentials
433
+ });
434
+ expect(result).toEqual({
435
+ API_KEY: 'test-key',
436
+ DB_PASSWORD: 'secret123'
437
+ });
438
+ });
439
+
440
+ it('should handle complete flow with environment variables', async () => {
441
+ process.env.AWS_ACCESS_KEY_ID = 'env-key';
442
+ process.env.AWS_SECRET_ACCESS_KEY = 'env-secret';
443
+
444
+ mockSTSSend.mockResolvedValueOnce({
445
+ Account: '123456789012'
446
+ });
447
+
448
+ mockSecretsManagerSend.mockResolvedValueOnce({
449
+ SecretString: '{"ENV_VAR": "env-value"}'
450
+ });
451
+
452
+ const result = await secretsmanager({
453
+ secret: 'env-secrets',
454
+ region: 'eu-central-1'
455
+ });
456
+
457
+ expect(result).toEqual({ ENV_VAR: 'env-value' });
458
+ });
459
+ });
460
+ });
@@ -0,0 +1,183 @@
1
+ import * as os from 'os';
2
+ import {
3
+ replaceWithAstrisk,
4
+ objectToExport,
5
+ objectToEnv
6
+ } from '../../src/vaults/utils';
7
+
8
+ describe('vaults utils', () => {
9
+ describe('replaceWithAstrisk', () => {
10
+ test('should return undefined for undefined input', () => {
11
+ expect(replaceWithAstrisk(undefined)).toBeUndefined();
12
+ });
13
+
14
+ test('should return undefined for empty string', () => {
15
+ expect(replaceWithAstrisk('')).toBeUndefined();
16
+ });
17
+
18
+ test('should mask middle characters for strings longer than 4 characters', () => {
19
+ expect(replaceWithAstrisk('password123')).toBe('p******d123');
20
+ expect(replaceWithAstrisk('secretkey')).toBe('s****tkey');
21
+ expect(replaceWithAstrisk('verylongpassword')).toBe('v***********word');
22
+ });
23
+
24
+ test('should not mask characters for strings 4 characters or shorter', () => {
25
+ expect(replaceWithAstrisk('1234')).toBe('1234');
26
+ expect(replaceWithAstrisk('abc')).toBe('abc');
27
+ expect(replaceWithAstrisk('a')).toBe('a');
28
+ });
29
+
30
+ test('should preserve first and last 4 characters for longer strings', () => {
31
+ expect(replaceWithAstrisk('abcdefghijklmnop')).toBe('a***********mnop');
32
+ expect(replaceWithAstrisk('1234567890123456')).toBe('1***********3456');
33
+ });
34
+
35
+ test('should handle special characters and unicode', () => {
36
+ expect(replaceWithAstrisk('p@ssw0rd!')).toBe('p****0rd!');
37
+ expect(replaceWithAstrisk('🔑secret🔒')).toBe('🔑*****t🔒');
38
+ });
39
+ });
40
+
41
+ describe('objectToExport', () => {
42
+ test('should convert object to env vars', () => {
43
+ const obj = {
44
+ API_KEY: 'abc123',
45
+ DATABASE_URL: 'postgres://localhost:5432/db',
46
+ DEBUG: 'true'
47
+ };
48
+
49
+ const result = objectToExport(obj);
50
+ const expected = `API_KEY=abc123${os.EOL}DATABASE_URL=postgres://localhost:5432/db${os.EOL}DEBUG=true${os.EOL}`;
51
+
52
+ expect(result).toBe(expected);
53
+ });
54
+
55
+ test('should handle empty object', () => {
56
+ const obj = {};
57
+ const result = objectToExport(obj);
58
+ expect(result).toBe('');
59
+ });
60
+
61
+ test('should handle object with various value types', () => {
62
+ const obj = {
63
+ STRING: 'hello',
64
+ NUMBER: 42,
65
+ BOOLEAN: true,
66
+ NULL: null,
67
+ UNDEFINED: undefined
68
+ };
69
+
70
+ const result = objectToExport(obj);
71
+ const expected = `STRING=hello${os.EOL}NUMBER=42${os.EOL}BOOLEAN=true${os.EOL}NULL=null${os.EOL}UNDEFINED=undefined${os.EOL}`;
72
+
73
+ expect(result).toBe(expected);
74
+ });
75
+
76
+ test('should handle object with special characters in keys and values', () => {
77
+ const obj = {
78
+ 'API-KEY': 'abc-123',
79
+ DATABASE_URL: 'postgres://user:pass@localhost:5432/db',
80
+ DEBUG_MODE: 'true'
81
+ };
82
+
83
+ const result = objectToExport(obj);
84
+ const expected = `API-KEY=abc-123${os.EOL}DATABASE_URL=postgres://user:pass@localhost:5432/db${os.EOL}DEBUG_MODE=true${os.EOL}`;
85
+
86
+ expect(result).toBe(expected);
87
+ });
88
+ });
89
+
90
+ describe('objectToEnv', () => {
91
+ let originalEnv: NodeJS.ProcessEnv;
92
+
93
+ beforeEach(() => {
94
+ originalEnv = { ...process.env };
95
+ });
96
+
97
+ afterEach(() => {
98
+ process.env = originalEnv;
99
+ });
100
+
101
+ test('should set environment variables from object', () => {
102
+ const obj = {
103
+ API_KEY: 'abc123',
104
+ DATABASE_URL: 'postgres://localhost:5432/db',
105
+ DEBUG: 'true'
106
+ };
107
+
108
+ const result = objectToEnv(obj);
109
+
110
+ expect(process.env.API_KEY).toBe('abc123');
111
+ expect(process.env.DATABASE_URL).toBe('postgres://localhost:5432/db');
112
+ expect(process.env.DEBUG).toBe('true');
113
+ expect(result).toEqual([
114
+ 'abc123',
115
+ 'postgres://localhost:5432/db',
116
+ 'true'
117
+ ]);
118
+ });
119
+
120
+ test('should handle empty object', () => {
121
+ const obj = {};
122
+ const result = objectToEnv(obj);
123
+
124
+ expect(result).toEqual([]);
125
+ });
126
+
127
+ test('should handle object with various value types', () => {
128
+ const obj = {
129
+ STRING: 'hello',
130
+ NUMBER: 42,
131
+ BOOLEAN: true,
132
+ NULL: null,
133
+ UNDEFINED: undefined
134
+ };
135
+
136
+ const result = objectToEnv(obj);
137
+
138
+ expect(process.env.STRING).toBe('hello');
139
+ expect(process.env.NUMBER).toBe('42');
140
+ expect(process.env.BOOLEAN).toBe('true');
141
+ expect(process.env.NULL).toBe('null');
142
+ expect(process.env.UNDEFINED).toBe('undefined');
143
+ expect(result).toEqual(['hello', '42', 'true', 'null', 'undefined']);
144
+ });
145
+
146
+ test('should overwrite existing environment variables', () => {
147
+ // Set initial environment variable
148
+ process.env.EXISTING_VAR = 'old_value';
149
+
150
+ const obj = {
151
+ EXISTING_VAR: 'new_value',
152
+ NEW_VAR: 'new_var_value'
153
+ };
154
+
155
+ const result = objectToEnv(obj);
156
+
157
+ expect(process.env.EXISTING_VAR).toBe('new_value');
158
+ expect(process.env.NEW_VAR).toBe('new_var_value');
159
+ expect(result).toEqual(['new_value', 'new_var_value']);
160
+ });
161
+
162
+ test('should handle object with special characters in keys and values', () => {
163
+ const obj = {
164
+ 'API-KEY': 'abc-123',
165
+ DATABASE_URL: 'postgres://user:pass@localhost:5432/db',
166
+ DEBUG_MODE: 'true'
167
+ };
168
+
169
+ const result = objectToEnv(obj);
170
+
171
+ expect(process.env['API-KEY']).toBe('abc-123');
172
+ expect(process.env['DATABASE_URL']).toBe(
173
+ 'postgres://user:pass@localhost:5432/db'
174
+ );
175
+ expect(process.env['DEBUG_MODE']).toBe('true');
176
+ expect(result).toEqual([
177
+ 'abc-123',
178
+ 'postgres://user:pass@localhost:5432/db',
179
+ 'true'
180
+ ]);
181
+ });
182
+ });
183
+ });
@@ -0,0 +1,8 @@
1
+ import { LIB_VERSION } from '../src/version';
2
+ import packageJson from '../package.json';
3
+
4
+ describe('LIB_VERSION', () => {
5
+ it('should match the version in package.json', () => {
6
+ expect(LIB_VERSION).toBe(packageJson.version);
7
+ });
8
+ });