addons/isl/integrationTests/ejeca.test.tsxblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import type {EjecaChildProcess} from 'shared/ejeca';
b69ab319
b69ab3110import {ejeca} from 'shared/ejeca';
b69ab3111
b69ab3112describe('test running binaries', () => {
b69ab3113 it('we can get both streams and awaitables', async () => {
b69ab3114 const spawned = ejeca('node', ['-e', "console.log('uno') ; console.error('dos')"]);
b69ab3115 let streamOut = '';
b69ab3116 let streamErr = '';
b69ab3117 spawned.stdout?.on('data', data => {
b69ab3118 streamOut = data.toString();
b69ab3119 });
b69ab3120 spawned.stderr?.on('data', data => {
b69ab3121 streamErr = data.toString();
b69ab3122 });
b69ab3123 const result = await spawned;
b69ab3124 expect(result.stdout).toBe('uno');
b69ab3125 expect(streamOut).toBe('uno\n');
b69ab3126 expect(result.stderr).toBe('dos');
b69ab3127 expect(streamErr).toBe('dos\n');
b69ab3128 });
b69ab3129
b69ab3130 it('we can set pass stdin as a string', async () => {
b69ab3131 const spawned = ejeca('node', [], {input: 'console.log("hemlo")'});
b69ab3132 expect((await spawned).stdout).toBe('hemlo');
b69ab3133 });
b69ab3134
b69ab3135 it('when erroring out the command name is present', async () => {
b69ab3136 const spawned = ejeca('node', ['-', 'foo("bar")'], {input: 'babar'});
b69ab3137 await expect(spawned).rejects.toThrowErrorMatchingInlineSnapshot(
b69ab3138 `"Command \`node "-" "foo(\\"bar\\")"\` exited with non-zero status with exit code 1"`,
b69ab3139 );
b69ab3140 });
b69ab3141
b69ab3142 it('handles env var options correctly', async () => {
b69ab3143 // We use yarn for our tests, so YARN_IGNORE_PATH should always be set
b69ab3144 const input =
b69ab3145 'console.log("YARN_IGNORE_PATH" in process.env ? process.env.YARN_IGNORE_PATH : "not set")';
b69ab3146 let spawned = ejeca('node', ['-'], {input});
b69ab3147 expect((await spawned).stdout).not.toBe('babar');
b69ab3148 spawned = ejeca('node', ['-'], {input, env: {YARN_IGNORE_PATH: 'babar'}});
b69ab3149 expect((await spawned).stdout).toBe('babar');
b69ab3150 spawned = ejeca('node', ['-'], {input, env: {YARN_IGNORE_PATH: 'babar'}, extendEnv: true});
b69ab3151 expect((await spawned).stdout).toBe('babar');
b69ab3152 spawned = ejeca('node', ['-'], {
b69ab3153 input,
b69ab3154 env: {FOO: 'bar', PATH: process.env.PATH},
b69ab3155 extendEnv: false,
b69ab3156 });
b69ab3157 expect((await spawned).stdout).toBe('not set');
b69ab3158 });
b69ab3159
b69ab3160 it('can specify whether to strip the final line', async () => {
b69ab3161 const input = 'console.log("hello")';
b69ab3162 let spawned;
b69ab3163 spawned = ejeca('node', ['-'], {input});
b69ab3164 expect((await spawned).stdout).toBe('hello');
b69ab3165 spawned = ejeca('node', ['-'], {input, stripFinalNewline: true});
b69ab3166 expect((await spawned).stdout).toBe('hello');
b69ab3167 spawned = ejeca('node', ['-'], {input, stripFinalNewline: false});
b69ab3168 expect((await spawned).stdout).toBe('hello\n');
b69ab3169 });
b69ab3170
b69ab3171 it('we can specify stdin', async () => {
b69ab3172 let spawned;
b69ab3173 spawned = ejeca('node', [], {input: 'console.log("hemlo")'});
b69ab3174 expect((await spawned).stdout).toBe('hemlo');
b69ab3175 spawned = ejeca('node', [], {input: 'console.log("hemlo")', stdin: 'pipe'});
b69ab3176 expect((await spawned).stdout).toBe('hemlo');
b69ab3177 spawned = ejeca('node', [], {input: 'console.log("hemlo")', stdin: 'ignore'});
b69ab3178 expect((await spawned).stdout).toBe('');
b69ab3179 });
b69ab3180});
b69ab3181
b69ab3182describe('test killing process', () => {
b69ab3183 const sighandlerScript = `
b69ab3184const argv = process.argv;
b69ab3185const sleep = (waitTimeInMs) => new Promise(resolve => setTimeout(resolve, waitTimeInMs));
b69ab3186
b69ab3187(async function main() {
b69ab3188let exitOnSigTerm = false;
b69ab3189let delay = 0;
b69ab3190
b69ab3191if(argv.length > 2) {
b69ab3192 delay = parseInt(argv[2]);
b69ab3193 if(argv[argv.length - 1] !== "dontExitOnSigterm") {
b69ab3194 exitOnSigTerm = true;
b69ab3195 }
b69ab3196}
b69ab3197
b69ab3198process.on('SIGTERM', () => {
b69ab3199 console.log("I was asked to stop politely");
b69ab31100 if(exitOnSigTerm) {
b69ab31101 process.exit(0)
b69ab31102 }
b69ab31103});
b69ab31104
b69ab31105console.log("Hello");
b69ab31106
b69ab31107for(let i=0; i < delay; i++) {
b69ab31108 await sleep(1000);
b69ab31109}
b69ab31110
b69ab31111console.log("Goodbye");
b69ab31112})();
b69ab31113`;
b69ab31114
b69ab31115 const spawnAndKill = async (
b69ab31116 pythonArgs: string[] = [],
b69ab31117 expectedOut: string = '',
b69ab31118 killArgs: Parameters<EjecaChildProcess['kill']> = [],
b69ab31119 expectedSignal?: string,
b69ab31120 ) => {
b69ab31121 const spawned = ejeca('node', ['-', ...pythonArgs], {
b69ab31122 input: sighandlerScript,
b69ab31123 });
b69ab31124 setTimeout(() => spawned.kill(...killArgs), 1000);
b69ab31125 let outo = '';
b69ab31126 let signalo = undefined;
b69ab31127 try {
b69ab31128 outo = (await spawned).stdout;
b69ab31129 } catch (err) {
b69ab31130 if (err != null && typeof err === 'object' && 'stdout' in err && 'signal' in err) {
b69ab31131 outo = err.stdout as string;
b69ab31132 signalo = err.signal;
b69ab31133 }
b69ab31134 }
b69ab31135 expect(outo).toBe(expectedOut);
b69ab31136 expect(signalo).toBe(expectedSignal);
b69ab31137 };
b69ab31138
b69ab31139 it('kill as sends sigterm by default', async () => {
b69ab31140 await spawnAndKill(['3', 'dontExitOnSigterm'], 'Hello\nI was asked to stop politely\nGoodbye');
b69ab31141 });
b69ab31142
b69ab31143 it('sigkill can be set through force kill after a timeout', async () => {
b69ab31144 await spawnAndKill(
b69ab31145 ['4', 'dontExitOnSigterm'],
b69ab31146 'Hello\nI was asked to stop politely',
b69ab31147 ['SIGTERM', {forceKillAfterTimeout: 2000}],
b69ab31148 'SIGKILL',
b69ab31149 );
b69ab31150 });
b69ab31151
b69ab31152 it('sending sigkill just kills', async () => {
b69ab31153 await spawnAndKill(['100000000000'], 'Hello', ['SIGKILL'], 'SIGKILL');
b69ab31154 });
b69ab31155});