From c2a254f2fda9756720aa0d5aa712def6e85759d8 Mon Sep 17 00:00:00 2001 From: Ardis Lu Date: Mon, 7 Sep 2026 22:12:56 -0700 Subject: [PATCH] Refactor hardcoded `childProcess.execSync` into dependency injection This change enables concurrent tests without inter-test thrashing on the `childProcess` mocks. --- src/command.js | 13 +++++++++---- test/index.test.js | 12 ++++++------ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/command.js b/src/command.js index adea1c9..3b57107 100644 --- a/src/command.js +++ b/src/command.js @@ -103,13 +103,17 @@ export async function encrypt(options = {}) { * @property {GetPrfOptions} [getPrfOptions] Options for the passkey page and server. * @property {Logger} [logger] Logger to use to record diagnostic information. The default value is `{}` (i.e., * drop all logs). + * @property {(command: string) => unknown} [exec] Function to call if there are additional arguments passed in the + * command after `decrypt`. The default value is + * {@link https://nodejs.org/api/child_process.html#child_processexecsynccommand-options|`childProcess.execSync`} + * with the option `{ stdio: 'inherit' }`. */ /** * Decrypt an envpass encrypted .env file using a passkey. * @param {DecryptOptions} [options] Options to configure the decryption. - * @param {{ args?: Array }} [command] Excess arguments passed to the command, which will be executed as - * a child process after decryption. + * @param {{ args?: Array }} [command] Excess arguments passed to the command, which will be passed on + * to the `exec` function. * @returns {Promise} `Promise` resolving when the decryption is complete. */ export async function decrypt(options = {}, { args = [] } = {}) { @@ -119,7 +123,8 @@ export async function decrypt(options = {}, { args = [] } = {}) { injectInProcess = false, notEncryptedValue = 'ignore', getPrfOptions = { port: undefined }, - logger = {} + logger = {}, + exec = command => childProcess.execSync(command, { stdio: 'inherit' }) } = options; const env = /** @type {Record} */ ( @@ -174,6 +179,6 @@ export async function decrypt(options = {}, { args = [] } = {}) { } if (args.length > 0) { logger.debug?.('Follow-on command found, executing in child process.'); - childProcess.execSync(args.join(' '), { stdio: 'inherit' }); + exec(args.join(' ')); } } \ No newline at end of file diff --git a/test/index.test.js b/test/index.test.js index 79aeeae..92c66d6 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -165,19 +165,19 @@ suite('decrypt', () => { logger.assertCounts({ debug: 3, info: 0, warn: 0, error: 0 }); }); test('passes through excess args to execute', async (t) => { - const execMock = t.mock.method(childProcess, 'execSync', () => { }); const envFile = await setupEnv(t, STD_ENV); + /** @type {(command: string) => void} */ + const exec = command => deepStrictEqual(command, 'test test test'); - await decrypt({ inFile: envFile }, { args: ['test', 'test', 'test'] }); - deepStrictEqual(execMock.mock.calls.length, 1); + await decrypt({ inFile: envFile, exec }, { args: ['test', 'test', 'test'] }); }); test('passes through excess args to execute (with logs)', async (t) => { - const execMock = t.mock.method(childProcess, 'execSync', () => { }); const envFile = await setupEnv(t, STD_ENV); + /** @type {(command: string) => void} */ + const exec = command => deepStrictEqual(command, 'test test test'); const logger = new MockLogger(); - await decrypt({ inFile: envFile, logger }, { args: ['test', 'test', 'test'] }); - deepStrictEqual(execMock.mock.calls.length, 1); + await decrypt({ inFile: envFile, exec, logger }, { args: ['test', 'test', 'test'] }); logger.assertCounts({ debug: 4, info: 0, warn: 0, error: 0 }); }); }); -- 2.47.3