Skip to content

Commit

Permalink
Merge pull request #199 from ShahradR/feature/update-taskcat-typescript
Browse files Browse the repository at this point in the history
feat: add TypeScript logic to update taskcat
  • Loading branch information
ShahradR authored Oct 1, 2021
2 parents eb60e92 + 37756d9 commit 69ec384
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 69ec384

Please sign in to comment.