From 1f3ce75844fe0a331add272db35dad480a223118 Mon Sep 17 00:00:00 2001 From: Nick Fyson Date: Mon, 7 Sep 2020 15:17:43 +0100 Subject: [PATCH] add initial suite of tests for exec_wrapper --- lib/exec_wrapper.test.js | 102 ++++++++++++++++++++++ lib/exec_wrapper.test.js.map | 1 + src/exec_wrapper.test.ts | 158 +++++++++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 lib/exec_wrapper.test.js create mode 100644 lib/exec_wrapper.test.js.map create mode 100644 src/exec_wrapper.test.ts diff --git a/lib/exec_wrapper.test.js b/lib/exec_wrapper.test.js new file mode 100644 index 000000000..b0ceebc73 --- /dev/null +++ b/lib/exec_wrapper.test.js @@ -0,0 +1,102 @@ +"use strict"; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const exec = __importStar(require("@actions/exec")); +const ava_1 = __importDefault(require("ava")); +const exec_wrapper_1 = require("./exec_wrapper"); +const testing_utils_1 = require("./testing-utils"); +// import fs from 'fs'; +// import { exec_wrapper } from './exec_wrapper'; +// const matchers: [[number, RegExp, string]] = [ +// [-999, +// new RegExp("match this string"), +// 'No source code was found. CUSTOM ERROR MESSAGE HERE'], +// ]; +testing_utils_1.setupTests(ava_1.default); +ava_1.default('matchers are never applied if non-error exit', async (t) => { + const testCommand = buildDummyCommand("foo bar\\nblort qux", "foo bar\\nblort qux", '', 0); + const matchers = [[123, new RegExp("foo bar"), 'error!!!']]; + t.deepEqual(await exec.exec(testCommand), 0); + t.deepEqual(await exec_wrapper_1.exec_wrapper(testCommand, [], matchers), 0); +}); +ava_1.default('regex matchers are applied to stdout for non-zero exit code', async (t) => { + const testCommand = buildDummyCommand("foo bar\\nblort qux", '', '', 1); + const matchers = [[123, new RegExp("foo bar"), '🦄']]; + await t.throwsAsync(exec.exec(testCommand), { instanceOf: Error, message: 'The process \'node\' failed with exit code 1' }); + await t.throwsAsync(exec_wrapper_1.exec_wrapper(testCommand, [], matchers), { instanceOf: Error, message: '🦄' }); +}); +ava_1.default('regex matchers are applied to stderr for non-zero exit code', async (t) => { + const testCommand = buildDummyCommand("non matching string", 'foo bar\\nblort qux', '', 1); + const matchers = [[123, new RegExp("foo bar"), '🦄']]; + await t.throwsAsync(exec.exec(testCommand), { instanceOf: Error, message: 'The process \'node\' failed with exit code 1' }); + await t.throwsAsync(exec_wrapper_1.exec_wrapper(testCommand, [], matchers), { instanceOf: Error, message: '🦄' }); +}); +ava_1.default('matcher returns correct error message when multiple matchers defined', async (t) => { + const testCommand = buildDummyCommand("non matching string", 'foo bar\\nblort qux', '', 1); + const matchers = [[456, new RegExp("lorem ipsum"), '😩'], + [123, new RegExp("foo bar"), '🦄'], + [789, new RegExp("blah blah"), '🤦‍♂️']]; + await t.throwsAsync(exec.exec(testCommand), { instanceOf: Error, message: 'The process \'node\' failed with exit code 1' }); + await t.throwsAsync(exec_wrapper_1.exec_wrapper(testCommand, [], matchers), { instanceOf: Error, message: '🦄' }); +}); +ava_1.default('matcher returns first match to regex when multiple matches', async (t) => { + const testCommand = buildDummyCommand("non matching string", 'foo bar\\nblort qux', '', 1); + const matchers = [[123, new RegExp("foo bar"), '🦄'], + [789, new RegExp("blah blah"), '🤦‍♂️'], + [987, new RegExp("foo bar"), '🚫']]; + await t.throwsAsync(exec.exec(testCommand), { instanceOf: Error, message: 'The process \'node\' failed with exit code 1' }); + await t.throwsAsync(exec_wrapper_1.exec_wrapper(testCommand, [], matchers), { instanceOf: Error, message: '🦄' }); +}); +ava_1.default('exit code matchers are applied', async (t) => { + const testCommand = buildDummyCommand("non matching string", 'foo bar\\nblort qux', '', 123); + const matchers = [[123, new RegExp("this will not match"), '🦄']]; + await t.throwsAsync(exec.exec(testCommand), { instanceOf: Error, message: 'The process \'node\' failed with exit code 123' }); + await t.throwsAsync(exec_wrapper_1.exec_wrapper(testCommand, [], matchers), { instanceOf: Error, message: '🦄' }); +}); +ava_1.default('exec_wrapper respects the ignoreReturnValue option', async (t) => { + const testCommand = buildDummyCommand("standard output", 'error output', '', 199); + await t.throwsAsync(exec_wrapper_1.exec_wrapper(testCommand, [], [], { ignoreReturnCode: false }), { instanceOf: Error }); + t.deepEqual(await exec_wrapper_1.exec_wrapper(testCommand, [], [], { ignoreReturnCode: true }), 199); +}); +ava_1.default('exec_wrapper preserves behavior of provided listeners', async (t) => { + let stdoutExpected = 'standard output'; + let stderrExpected = 'error output'; + let stdoutActual = ''; + let stderrActual = ''; + let listeners = { + stdout: (data) => { + stdoutActual += data.toString(); + }, + stderr: (data) => { + stderrActual += data.toString(); + } + }; + const testCommand = buildDummyCommand(stdoutExpected, stderrExpected, '', 0); + t.deepEqual(await exec_wrapper_1.exec_wrapper(testCommand, [], [], { listeners: listeners }), 0); + t.deepEqual(stdoutActual, stdoutExpected + "\n"); + t.deepEqual(stderrActual, stderrExpected + "\n"); +}); +function buildDummyCommand(stdoutContents, stderrContents, desiredErrorMessage, desiredExitCode) { + let command = ''; + if (stdoutContents) + command += 'console.log(\\"' + stdoutContents + '\\");'; + if (stderrContents) + command += 'console.error(\\"' + stderrContents + '\\");'; + if (command.length === 0) + throw new Error("Must provide contents for either stdout or stderr"); + if (desiredErrorMessage) + command += 'throw new Error(\\"' + desiredErrorMessage + '\\");'; + if (desiredExitCode) + command += 'process.exitCode = ' + desiredExitCode + ';'; + return 'node -e "' + command + '"'; +} +//# sourceMappingURL=exec_wrapper.test.js.map \ No newline at end of file diff --git a/lib/exec_wrapper.test.js.map b/lib/exec_wrapper.test.js.map new file mode 100644 index 000000000..64c41c8be --- /dev/null +++ b/lib/exec_wrapper.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"exec_wrapper.test.js","sourceRoot":"","sources":["../src/exec_wrapper.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/exec_wrapper.test.ts b/src/exec_wrapper.test.ts new file mode 100644 index 000000000..036551a99 --- /dev/null +++ b/src/exec_wrapper.test.ts @@ -0,0 +1,158 @@ +import * as exec from '@actions/exec'; +import test from 'ava'; + +import { exec_wrapper, ErrorMatcher } from './exec_wrapper'; +import {setupTests} from './testing-utils'; +// import fs from 'fs'; + +// import { exec_wrapper } from './exec_wrapper'; + +// const matchers: [[number, RegExp, string]] = [ +// [-999, +// new RegExp("match this string"), +// 'No source code was found. CUSTOM ERROR MESSAGE HERE'], +// ]; + +setupTests(test); + +test('matchers are never applied if non-error exit', async t => { + + const testCommand = buildDummyCommand("foo bar\\nblort qux", "foo bar\\nblort qux", '', 0); + + const matchers: ErrorMatcher[] = [[123, new RegExp("foo bar"), 'error!!!']]; + + t.deepEqual(await exec.exec(testCommand), 0); + + t.deepEqual(await exec_wrapper(testCommand, [], matchers), 0); + +}); + +test('regex matchers are applied to stdout for non-zero exit code', async t => { + + const testCommand = buildDummyCommand("foo bar\\nblort qux", '', '', 1); + + const matchers: ErrorMatcher[] = [[123, new RegExp("foo bar"), '🦄']]; + + await t.throwsAsync(exec.exec(testCommand), {instanceOf: Error, message: 'The process \'node\' failed with exit code 1'}); + + await t.throwsAsync( + exec_wrapper(testCommand, [], matchers), + {instanceOf: Error, message: '🦄'} + ); + +}); + +test('regex matchers are applied to stderr for non-zero exit code', async t => { + + const testCommand = buildDummyCommand("non matching string", 'foo bar\\nblort qux', '', 1); + + const matchers: ErrorMatcher[] = [[123, new RegExp("foo bar"), '🦄']]; + + await t.throwsAsync(exec.exec(testCommand), {instanceOf: Error, message: 'The process \'node\' failed with exit code 1'}); + + await t.throwsAsync( + exec_wrapper(testCommand, [], matchers), + {instanceOf: Error, message: '🦄'} + ); + +}); + +test('matcher returns correct error message when multiple matchers defined', async t => { + + const testCommand = buildDummyCommand("non matching string", 'foo bar\\nblort qux', '', 1); + + const matchers: ErrorMatcher[] = [[456, new RegExp("lorem ipsum"), '😩'], + [123, new RegExp("foo bar"), '🦄'], + [789, new RegExp("blah blah"), '🤦‍♂️']]; + + await t.throwsAsync(exec.exec(testCommand), {instanceOf: Error, message: 'The process \'node\' failed with exit code 1'}); + + await t.throwsAsync( + exec_wrapper(testCommand, [], matchers), + {instanceOf: Error, message: '🦄'} + ); + +}); + +test('matcher returns first match to regex when multiple matches', async t => { + + const testCommand = buildDummyCommand("non matching string", 'foo bar\\nblort qux', '', 1); + + const matchers: ErrorMatcher[] = [[123, new RegExp("foo bar"), '🦄'], + [789, new RegExp("blah blah"), '🤦‍♂️'], + [987, new RegExp("foo bar"), '🚫']]; + + await t.throwsAsync(exec.exec(testCommand), {instanceOf: Error, message: 'The process \'node\' failed with exit code 1'}); + + await t.throwsAsync( + exec_wrapper(testCommand, [], matchers), + {instanceOf: Error, message: '🦄'} + ); + +}); + +test('exit code matchers are applied', async t => { + + const testCommand = buildDummyCommand("non matching string", 'foo bar\\nblort qux', '', 123); + + const matchers: ErrorMatcher[] = [[123, new RegExp("this will not match"), '🦄']]; + + await t.throwsAsync(exec.exec(testCommand), {instanceOf: Error, message: 'The process \'node\' failed with exit code 123'}); + + await t.throwsAsync( + exec_wrapper(testCommand, [], matchers), + {instanceOf: Error, message: '🦄'} + ); + +}); + +test('exec_wrapper respects the ignoreReturnValue option', async t => { + const testCommand = buildDummyCommand("standard output", 'error output', '', 199); + + await t.throwsAsync(exec_wrapper(testCommand, [], [], {ignoreReturnCode: false}), {instanceOf: Error}); + + t.deepEqual(await exec_wrapper(testCommand, [], [], {ignoreReturnCode: true}), 199); + +}); + +test('exec_wrapper preserves behavior of provided listeners', async t => { + + let stdoutExpected = 'standard output'; + let stderrExpected = 'error output'; + + let stdoutActual = ''; + let stderrActual = ''; + + let listeners = { + stdout: (data: Buffer) => { + stdoutActual += data.toString(); + }, + stderr: (data: Buffer) => { + stderrActual += data.toString(); + } + }; + + const testCommand = buildDummyCommand(stdoutExpected, stderrExpected, '', 0); + + t.deepEqual(await exec_wrapper(testCommand, [], [], {listeners: listeners}), 0); + + t.deepEqual(stdoutActual, stdoutExpected + "\n"); + t.deepEqual(stderrActual, stderrExpected + "\n"); + +}); + +function buildDummyCommand(stdoutContents: string, stderrContents: string, + desiredErrorMessage?: string, desiredExitCode?: number): string { + + let command = ''; + + if (stdoutContents) command += 'console.log(\\"' + stdoutContents + '\\");'; + if (stderrContents) command += 'console.error(\\"' + stderrContents + '\\");'; + + if (command.length === 0) throw new Error("Must provide contents for either stdout or stderr"); + + if (desiredErrorMessage) command += 'throw new Error(\\"' + desiredErrorMessage + '\\");'; + if (desiredExitCode) command += 'process.exitCode = ' + desiredExitCode + ';'; + + return 'node -e "' + command + '"'; +}