Skip to content

Commit

Permalink
Merge pull request #1710 from lshadler/exec-debugging-and-handle
Browse files Browse the repository at this point in the history
fix: add debugging and error handling to exec
  • Loading branch information
hipstersmoothie authored Jan 12, 2021
2 parents 0713aef + fb0cf35 commit 46d0898
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 58 deletions.
Empty file added packages/cli/auto
Empty file.
156 changes: 122 additions & 34 deletions plugins/exec/__tests__/exec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,31 @@ import { execSync } from "child_process";

import Exec from "../src";

const getMockAuto = () => {
const hooks = makeHooks();

return {
hooks,
logger: {
verbose: {
info: jest.fn(),
},
veryVerbose: {
info: jest.fn(),
},
log: {
error: jest.fn(),
},
},
};
};

const execSpy = execSync as jest.Mock;
jest.mock("child_process");
execSpy.mockReturnValue("");

describe("Exec Plugin", () => {
beforeEach(() => {
execSpy.mockReturnValue("");
execSpy.mockClear();
});

Expand All @@ -33,30 +52,97 @@ describe("Exec Plugin", () => {

test("should handle SyncHook hooks", async () => {
const plugins = new Exec({ beforeRun: "echo foo" });
const hooks = makeHooks();

plugins.apply({ hooks } as Auto);
await hooks.beforeRun.promise({} as any);
const mockAuto = getMockAuto();

plugins.apply(mockAuto);
await mockAuto.hooks.beforeRun.promise({} as any);

expect(execSpy).toHaveBeenCalledWith("echo foo", expect.anything());
expect(mockAuto.logger.verbose.info).toHaveBeenCalledWith(
"Running command: echo foo"
);
});

test("logs an E2BIG error with special handling", async () => {
const mockExit = jest.spyOn(process, "exit").mockImplementation(() => {});
execSpy.mockImplementation(() => {
const error = new Error("E2BIG");
error.code = "E2BIG";

throw error;
});

const plugins = new Exec({ beforeRun: "echo foo" });

const mockAuto = getMockAuto();

plugins.apply(mockAuto);
await mockAuto.hooks.beforeRun.promise({} as any);

expect(execSpy).toHaveBeenCalledWith("echo foo", expect.anything());
expect(mockAuto.logger.verbose.info).toHaveBeenCalledWith(
"Running command: echo foo"
);
expect(mockAuto.logger.log.error).toHaveBeenCalledTimes(1);
expect(mockAuto.logger.log.error.mock.calls[0][0]).toStrictEqual(
expect.stringContaining("Received E2BIG from execSync.")
);
expect(mockExit).toHaveBeenCalledWith(1);
});

test("logs other errors normally", async () => {
const mockExit = jest.spyOn(process, "exit").mockImplementation(() => {});
execSpy.mockImplementation(() => {
const error = new Error("random error");
error.code = "random";

throw error;
});

const plugins = new Exec({ beforeRun: "echo foo" });

const mockAuto = getMockAuto();

plugins.apply(mockAuto);
await mockAuto.hooks.beforeRun.promise({} as any);

expect(execSpy).toHaveBeenCalledWith("echo foo", expect.anything());
expect(mockAuto.logger.verbose.info).toHaveBeenCalledWith(
"Running command: echo foo"
);

expect(mockAuto.logger.log.error).toHaveBeenCalledTimes(1);
expect(mockAuto.logger.log.error.mock.calls[0][0].message).toStrictEqual(
expect.stringContaining("random error")
);
expect(mockExit).toHaveBeenCalledWith(1);
});

test("should handle AsyncParallelHook hooks", async () => {
const plugins = new Exec({ version: "echo bar" });
const hooks = makeHooks();

plugins.apply({ hooks } as Auto);
await hooks.version.promise({ bump: SEMVER.patch });
const mockAuto = getMockAuto();

plugins.apply(mockAuto);
await mockAuto.hooks.version.promise({ bump: SEMVER.patch });
expect(mockAuto.logger.verbose.info).toHaveBeenCalledWith(
"Running command: echo bar"
);

expect(execSpy).toHaveBeenCalledWith("echo bar", expect.anything());
});

test("should handle AsyncSeriesHook hooks w/args", async () => {
const plugins = new Exec({ beforeShipIt: "echo baz" });
const hooks = makeHooks();

plugins.apply({ hooks } as Auto);
await hooks.beforeShipIt.promise({ releaseType: "latest" });
const mockAuto = getMockAuto();

plugins.apply(mockAuto);
await mockAuto.hooks.beforeShipIt.promise({ releaseType: "latest" });
expect(mockAuto.logger.verbose.info).toHaveBeenCalledWith(
"Running command: echo baz"
);

expect(execSpy).toHaveBeenCalledWith(
"echo baz",
Expand All @@ -70,12 +156,13 @@ describe("Exec Plugin", () => {
const plugins = new Exec({
getRepository: `echo "{ "repo": "foo", "owner": "bar" }"`,
});
const hooks = makeHooks();

execSpy.mockReturnValueOnce('{ "repo": "foo", "owner": "bar" }');
plugins.apply({ hooks } as Auto);
const mockAuto = getMockAuto();

plugins.apply(mockAuto);

expect(await hooks.getRepository.promise()).toStrictEqual({
expect(await mockAuto.hooks.getRepository.promise()).toStrictEqual({
repo: "foo",
owner: "bar",
});
Expand All @@ -87,13 +174,13 @@ describe("Exec Plugin", () => {
const plugins = new Exec({
canary: `echo ${canaryVersion}`,
});
const hooks = makeHooks();

const mockAuto = getMockAuto();
plugins.apply(mockAuto);
execSpy.mockReturnValueOnce(canaryVersion);
plugins.apply({ hooks } as Auto);

expect(
await hooks.canary.promise({
await mockAuto.hooks.canary.promise({
bump: SEMVER.patch,
canaryIdentifier: "-canary",
})
Expand All @@ -105,13 +192,13 @@ describe("Exec Plugin", () => {
const plugins = new Exec({
canary: `echo ${JSON.stringify(canaryVersion)}`,
});
const hooks = makeHooks();

const mockAuto = getMockAuto();
execSpy.mockReturnValueOnce(JSON.stringify(canaryVersion));
plugins.apply({ hooks } as Auto);
plugins.apply(mockAuto);

expect(
await hooks.canary.promise({
await mockAuto.hooks.canary.promise({
bump: SEMVER.patch,
canaryIdentifier: "-canary",
})
Expand All @@ -124,12 +211,12 @@ describe("Exec Plugin", () => {
const plugins = new Exec({
onCreateRelease: { createChangelogTitle: "echo here" },
});
const hooks = makeHooks();
const mockAuto = getMockAuto();
const releaseHooks = makeReleaseHooks();

execSpy.mockReturnValueOnce("here");
plugins.apply({ hooks } as Auto);
hooks.onCreateRelease.call({ hooks: releaseHooks } as any);
plugins.apply(mockAuto);
mockAuto.hooks.onCreateRelease.call({ hooks: releaseHooks } as any);

expect(await releaseHooks.createChangelogTitle.promise()).toBe("here");
});
Expand All @@ -140,12 +227,12 @@ describe("Exec Plugin", () => {
const plugins = new Exec({
onCreateLogParse: { omitCommit: "echo true" },
});
const hooks = makeHooks();
const mockAuto = getMockAuto();
const logParsehooks = makeLogParseHooks();

execSpy.mockReturnValueOnce("true");
plugins.apply({ hooks } as Auto);
hooks.onCreateLogParse.call({ hooks: logParsehooks } as any);
plugins.apply(mockAuto);
mockAuto.hooks.onCreateLogParse.call({ hooks: logParsehooks } as any);

expect(
await logParsehooks.omitCommit.promise({
Expand All @@ -162,11 +249,12 @@ describe("Exec Plugin", () => {
const plugins = new Exec({
onCreateLogParse: { omitCommit: "echo" },
});
const hooks = makeHooks();

const mockAuto = getMockAuto();
const logParsehooks = makeLogParseHooks();

plugins.apply({ hooks } as Auto);
hooks.onCreateLogParse.call({ hooks: logParsehooks } as any);
plugins.apply(mockAuto);
mockAuto.hooks.onCreateLogParse.call({ hooks: logParsehooks } as any);

expect(
await logParsehooks.omitCommit.promise({
Expand All @@ -185,15 +273,15 @@ describe("Exec Plugin", () => {
const plugins = new Exec({
onCreateChangelog: { omitReleaseNotes: "echo true" },
});
const hooks = makeHooks();
const mockAuto = getMockAuto();
const changelogHooks = makeChangelogHooks();

execSpy.mockReturnValueOnce("true");
plugins.apply({ hooks } as Auto);
hooks.onCreateChangelog.call(
{ hooks: changelogHooks } as any,
{ bump: SEMVER.patch }
);
plugins.apply(mockAuto);

mockAuto.hooks.onCreateChangelog.call({ hooks: changelogHooks } as any, {
bump: SEMVER.patch,
});

expect(
await changelogHooks.omitReleaseNotes.promise({
Expand Down
1 change: 1 addition & 0 deletions plugins/exec/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
},
"dependencies": {
"@auto-it/core": "link:../../packages/core",
"endent": "^2.0.1",
"fp-ts": "^2.5.3",
"fromentries": "^1.2.0",
"io-ts": "^2.1.2",
Expand Down
Loading

0 comments on commit 46d0898

Please sign in to comment.