Skip to content

Commit

Permalink
feat: add TypeScript logic to update taskcat
Browse files Browse the repository at this point in the history
Add logic in the TypeScript version of the application to update taskcat
to its latest version.
  • Loading branch information
ShahradR authored Oct 1, 2021
1 parent eb60e92 commit 37756d9
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 0 deletions.
160 changes: 160 additions & 0 deletions __tests__/post-entrypoint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { PostEntrypointImpl } from "../src/post-entrypoint";
import { TaskcatArtifactManagerImpl } from "../src/taskcat-artifact-manager";
import { ChildProcessMock } from "./mocks/childProcessMock";
import { Readable } from "stream";
import { InputOptions } from "@actions/core";

describe("the PostEntrypoint class", () => {
describe("the main function", () => {
Expand Down Expand Up @@ -233,5 +234,164 @@ describe("the PostEntrypoint class", () => {
anyObject()
);
});
it("should update taskcat when the update_taskcat parameter is passed", () => {
expect.assertions(2);

/**
* Mock the ChildProcess class. This is returned by child_process.spawn,
* and contains information about the running process itself, including
* the stdin, stdout, and stderr streams used to verify taskcat's output.
*
* This is different from the ChildProcess interface defined in
* src/interfaces.js we use throughout the application—that is a
* representation of the whole child_process Node.JS module, and exports
* this class. We mock the module itself below.
*/
const cp = mockDeep<cp.ChildProcess>();

/**
* This mock represents the child_process module. We configure it to
* return the ChildProcess object created above when we call the "spawn"
* function. This will contain the streams we run our unit tests against.
*/
const childProcessMock = mockDeep<ChildProcess>();
childProcessMock.spawn.mockReturnValue(cp);

/**
* Mock the "Core" object, and pass dummy values for the "commands" input
* parameter. In this case, a value of "test run" should invoke "taskcat
* test run"
*/
const core = mockDeep<Core>();
core.getInput.mockReturnValue("test run");

/**
*
*/
core.getBooleanInput.mockImplementation(
(name: string, options?: InputOptions | undefined): boolean => {
if (name === "update_taskcat") return true;
return false;
}
);

// Invoke the PostEntrypointImpl.run() function with our mock values.
new PostEntrypointImpl(
mock<Artifact>(),
core,
childProcessMock,
mock<TaskcatArtifactManagerImpl>()
).run();

/**
* We verify that if the update_taskcat parameter is passed as an input,
* the application calls pip in addition to our taskcat command
*/
expect(childProcessMock.spawn).toHaveBeenNthCalledWith(
1,
"pip",
["install", "--upgrade", "taskcat"],
anyObject()
);

/**
* Verify that taskcat has been invoked. Note that we use matchers
* to ensure that some array or object has been passed. Other tests will
* be responsible for verifying the validity of the args and options
* parameters.
*/
expect(childProcessMock.spawn).toHaveBeenNthCalledWith(
2,
"taskcat",
anyArray(),
anyObject()
);
});

it("should redirect standard and error outputs to the console from the pip update and taskcat commands", async () => {
expect.assertions(5);

// Create real Readable streams (versus the mocks created in other tests).
// We can simulate output from taskcat by pushing data to these streams.
const pipStdout = new Readable();
const pipStderr = new Readable();

const pipCp: cp.ChildProcess = new ChildProcessMock(
0,
pipStdout,
pipStderr
);

const taskcatStdout = new Readable();
const taskcatStderr = new Readable();

const taskcatCp: cp.ChildProcess = new ChildProcessMock(
0,
taskcatStdout,
taskcatStderr
);

const childProcessMock = mock<ChildProcess>();
childProcessMock.spawn.mockImplementation(
(
command: string,
args: readonly string[],
options: cp.SpawnOptions
): cp.ChildProcess => {
if (command === "pip") return pipCp;
else if (command === "taskcat") return taskcatCp;
else throw Error("This branch should not be reached");
}
);

const core = mockDeep<Core>();
core.getInput.mockReturnValue("test run");
core.getBooleanInput.mockReturnValue(true);

new PostEntrypointImpl(
mock<Artifact>(),
core,
childProcessMock,
mock<TaskcatArtifactManagerImpl>()
).run();

// Push data to the different streams. Note that we have to end the
// stream with `null`, to let it know we're done pushing data.
pipStdout.push("Output from pip's stdout");
pipStdout.push(null);

pipStderr.push("Output from pip's stderr");
pipStderr.push(null);

// Push data to the different streams. Note that we have to end the
// stream with `null`, to let it know we're done pushing data.
taskcatStdout.push("Output from taskcat's stdout");
taskcatStdout.push(null);

taskcatStderr.push("Output from taskcat's stderr");
taskcatStderr.push(null);

// Delay for 10 milliseconds, to give time for the code to receive and
// process the stdout and stderr data we just pushed.
await sleep(10);
expect(core.info).toHaveBeenNthCalledWith(
1,
"Received commands: test run"
);
expect(core.info).toHaveBeenNthCalledWith(2, "Output from pip's stdout");
expect(core.info).toHaveBeenNthCalledWith(3, "Output from pip's stderr");
expect(core.info).toHaveBeenNthCalledWith(
4,
"Output from taskcat's stdout"
);
expect(core.info).toHaveBeenNthCalledWith(
5,
"Output from taskcat's stderr"
);

function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
});
});
});
16 changes: 16 additions & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#! /usr/bin/node

/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({

Expand Down Expand Up @@ -13867,7 +13869,21 @@ var PostEntrypointImpl = /** @class */ (function () {
var _this = this;
var awsAccountId = this._core.getInput("aws-account-id");
var taskcatCommands = this._core.getInput("commands");
var updateTaskcat = this._core.getBooleanInput("update_taskcat");
this._core.info("Received commands: " + taskcatCommands);
if (updateTaskcat) {
var updateTaskcatChild = this._cp.spawn("pip", ["install", "--upgrade", "taskcat"], {
stdio: ["ignore", "pipe", "pipe"],
});
updateTaskcatChild.stdout.setEncoding("utf-8");
updateTaskcatChild.stderr.setEncoding("utf-8");
updateTaskcatChild.stdout.on("data", function (data) {
_this._core.info(data);
});
updateTaskcatChild.stderr.on("data", function (data) {
_this._core.info(data);
});
}
var newList = taskcatCommands.split(" ");
var child = this._cp.spawn("taskcat", newList, {
stdio: ["ignore", "pipe", "pipe"],
Expand Down
22 changes: 22 additions & 0 deletions src/post-entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,30 @@ export class PostEntrypointImpl implements PostEntrypoint {
public run(): void {
const awsAccountId = this._core.getInput("aws-account-id");
const taskcatCommands = this._core.getInput("commands");
const updateTaskcat: boolean = this._core.getBooleanInput("update_taskcat");
this._core.info("Received commands: " + taskcatCommands);

if (updateTaskcat) {
const updateTaskcatChild = this._cp.spawn(
"pip",
["install", "--upgrade", "taskcat"],
{
stdio: ["ignore", "pipe", "pipe"],
}
);

updateTaskcatChild.stdout.setEncoding("utf-8");
updateTaskcatChild.stderr.setEncoding("utf-8");

updateTaskcatChild.stdout.on("data", (data) => {
this._core.info(data);
});

updateTaskcatChild.stderr.on("data", (data) => {
this._core.info(data);
});
}

const newList = taskcatCommands.split(" ");

const child = this._cp.spawn("taskcat", newList, {
Expand Down

0 comments on commit 37756d9

Please sign in to comment.