diff --git a/tests/driver/driver.ts b/tests/driver/driver.ts index f43f21636..478ca93b4 100644 --- a/tests/driver/driver.ts +++ b/tests/driver/driver.ts @@ -21,6 +21,8 @@ export interface SetupSettings { trunkVersion?: string; } +export type CustomExecOptions = ExecOptions & { stdin?: string }; + const execFilePromise = util.promisify(execFile); const TEMP_SUBDIR = "tmp"; @@ -378,13 +380,41 @@ export abstract class GenericTrunkDriver { * Run a command inside the sandbox test repo. * @param bin command to run * @param args arguments to run - * @param execOptions + * @param execOptions options to pass the stdin to exec */ - async run(bin: string, args: string[], execOptions?: ExecOptions) { - return await execFilePromise(bin, args, { + async run(bin: string, args: string[], execOptions?: CustomExecOptions) { + const exec = execFile(bin, args, { cwd: this.sandboxPath, env: executionEnv(this.sandboxPath ?? ""), ...execOptions, }); + exec.stdin?.write(execOptions?.stdin ?? ""); + exec.stdin?.end(); + + return await new Promise<{ stdout: string; stderr: string }>((resolve, reject) => { + let stdout = ""; + let stderr = ""; + + exec.stdout?.on("data", (chunk: string) => { + stdout += chunk; + }); + + exec.stderr?.on("data", (chunk: string) => { + stderr += chunk; + }); + + exec.on("error", (err: any) => { + // trunk-ignore(eslint/@typescript-eslint/prefer-promise-reject-errors) + reject(err); + }); + exec.on("exit", (code: number) => { + if (code === 0) { + resolve({ stdout, stderr }); + } else { + // trunk-ignore(eslint/@typescript-eslint/prefer-promise-reject-errors) + reject({ error: new Error(`Process exited with code ${code}`), code, stdout, stderr }); + } + }); + }); } } diff --git a/tests/driver/tool_driver.ts b/tests/driver/tool_driver.ts index 091e4d4fb..ff578b068 100644 --- a/tests/driver/tool_driver.ts +++ b/tests/driver/tool_driver.ts @@ -195,16 +195,17 @@ lint: /**** Execution methods ****/ - async runTool(command: string[]): Promise { + async runTool(command: string[], stdin?: string): Promise { const tools_subdir = fs.existsSync(path.resolve(this.sandboxPath ?? "", ".trunk/dev-tools")) ? "dev-tools" : "tools"; try { if (process.platform == "win32") { - const { stdout, stderr } = await this.run("powershell", [ - `.trunk/${tools_subdir}/${command[0]}.bat`, - ...command.slice(1), - ]); + const { stdout, stderr } = await this.run( + "powershell", + [`.trunk/${tools_subdir}/${command[0]}.bat`, ...command.slice(1)], + { stdin }, + ); return { exitCode: 0, stdout, @@ -215,6 +216,7 @@ lint: const { stdout, stderr } = await this.run( `.trunk/${tools_subdir}/${command[0]}`, command.slice(1), + { stdin }, ); return { exitCode: 0, diff --git a/tests/index.ts b/tests/index.ts index e15dfe4e4..b08deac33 100644 --- a/tests/index.ts +++ b/tests/index.ts @@ -267,6 +267,7 @@ interface ToolTestConfig { expectedOut?: string; expectedErr?: string; expectedExitCode?: number; + stdin?: string; } export const makeToolTestConfig = ({ @@ -274,11 +275,13 @@ export const makeToolTestConfig = ({ expectedOut = "", expectedErr = "", expectedExitCode = 0, + stdin = "", }: ToolTestConfig) => ({ command, expectedOut, expectedErr, expectedExitCode, + stdin, }); export const toolTest = ({ @@ -298,9 +301,9 @@ export const toolTest = ({ }) => { describe(`Testing tool ${toolName}`, () => { const driver = setupTrunkToolDriver(dirName, {}, toolName, toolVersion, preCheck); - testConfigs.forEach(({ command, expectedOut, expectedErr, expectedExitCode }) => { + testConfigs.forEach(({ command, expectedOut, expectedErr, expectedExitCode, stdin }) => { conditionalTest(skipTestIf(toolVersion), command.join(" "), async () => { - const { stdout, stderr, exitCode } = await driver.runTool(command); + const { stdout, stderr, exitCode } = await driver.runTool(command, stdin); expect(stdout).toContain(expectedOut); expect(stderr).toContain(expectedErr); expect(exitCode).toEqual(expectedExitCode); diff --git a/tools/diff-so-fancy/diff_so_fancy.test.ts b/tools/diff-so-fancy/diff_so_fancy.test.ts index 34f2d8caa..457174c53 100644 --- a/tools/diff-so-fancy/diff_so_fancy.test.ts +++ b/tools/diff-so-fancy/diff_so_fancy.test.ts @@ -1,7 +1,34 @@ -import { toolInstallTest } from "tests"; +import { makeToolTestConfig, toolTest } from "tests"; -// TODO(Tyler): tool def is missing healthcheck -toolInstallTest({ +const sampleDiff = `diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml +index dc4f1f8..fb8fe54 100644 +--- a/.trunk/trunk.yaml ++++ b/.trunk/trunk.yaml +@@ -13,3 +13,6 @@ lint: + - node_modules/** + - .trunk/configs/** + - .gitattributes ++tools: ++ enabled: ++ - diff-so-fancy@1.4.3`; + +let exitCode = 25; +if (process.platform === "darwin") { + exitCode = 102; +} else if (process.platform === "win32") { + exitCode = 0; +} +// diff-so-fancy returns a nonzero exit code for its version command, so we have to +// use the custom test constructor. +toolTest({ toolName: "diff-so-fancy", toolVersion: "1.4.3", + testConfigs: [ + makeToolTestConfig({ + command: ["diff-so-fancy"], + expectedExitCode: exitCode, + expectedOut: "modified:", + stdin: sampleDiff, + }), + ], }); diff --git a/tools/difft/difft.test.ts b/tools/difft/difft.test.ts index fdbd2dec8..40e310e56 100644 --- a/tools/difft/difft.test.ts +++ b/tools/difft/difft.test.ts @@ -1,6 +1,5 @@ import { toolInstallTest } from "tests"; -// TODO(Tyler): tool def is missing healthcheck toolInstallTest({ toolName: "difft", toolVersion: "0.56.1", diff --git a/tools/difft/plugin.yaml b/tools/difft/plugin.yaml index eb0eb016f..ad295dc69 100644 --- a/tools/difft/plugin.yaml +++ b/tools/difft/plugin.yaml @@ -20,3 +20,6 @@ tools: download: difft known_good_version: 0.56.1 shims: [difft] + health_checks: + - command: difft --version + parse_regex: Difftastic ${semver} diff --git a/tools/gk/gk.test.ts b/tools/gk/gk.test.ts index 67b9a1896..347cfd8a9 100644 --- a/tools/gk/gk.test.ts +++ b/tools/gk/gk.test.ts @@ -1,7 +1,8 @@ -import { toolInstallTest } from "tests"; +import { makeToolTestConfig, toolTest } from "tests"; -// TODO(Tyler): tool def is missing healthcheck -toolInstallTest({ +// No version command for gk +toolTest({ toolName: "gk", toolVersion: "1.2.2", + testConfigs: [makeToolTestConfig({ command: ["gk", "-h"], expectedErr: "Usage" })], }); diff --git a/tools/goreleaser/goreleaser.test.ts b/tools/goreleaser/goreleaser.test.ts index c9781a483..6e6b8e8f8 100644 --- a/tools/goreleaser/goreleaser.test.ts +++ b/tools/goreleaser/goreleaser.test.ts @@ -1,11 +1,13 @@ -import { toolInstallTest } from "tests"; +import { makeToolTestConfig, toolTest } from "tests"; import { osTimeoutMultiplier } from "tests/utils"; // This install is quite slow on some Linux machines. jest.setTimeout(600000 * osTimeoutMultiplier); -// TODO(Tyler): tool def is missing healthcheck -toolInstallTest({ +toolTest({ toolName: "goreleaser", toolVersion: "1.25.1", + testConfigs: [ + makeToolTestConfig({ command: ["goreleaser", "--version"], expectedOut: "goreleaser" }), + ], }); diff --git a/tools/tsc/plugin.yaml b/tools/tsc/plugin.yaml index cce3dd100..1935cf23a 100644 --- a/tools/tsc/plugin.yaml +++ b/tools/tsc/plugin.yaml @@ -6,3 +6,6 @@ tools: package: typescript known_good_version: 5.2.2 shims: [tsc] + health_checks: + - command: tsc --version + parse_regex: Version ${semver} diff --git a/tools/tsc/tsc.test.ts b/tools/tsc/tsc.test.ts index 9f1c81716..f156d5040 100644 --- a/tools/tsc/tsc.test.ts +++ b/tools/tsc/tsc.test.ts @@ -1,7 +1,6 @@ import { toolInstallTest } from "tests"; import { skipOS } from "tests/utils"; -// TODO(Tyler): tool def is missing healthcheck toolInstallTest({ toolName: "tsc", toolVersion: "5.2.2", diff --git a/tools/yq/plugin.yaml b/tools/yq/plugin.yaml index 79c05943c..f3519367a 100644 --- a/tools/yq/plugin.yaml +++ b/tools/yq/plugin.yaml @@ -8,16 +8,19 @@ downloads: cpu: x86_64: 386 arm_64: arm64 - url: https://github.com/mikefarah/yq/releases/download/v${version}/yq_${os}_${cpu}.tar.gz + url: https://github.com/mikefarah/yq/releases/download/v${version}/yq_${os}_${cpu} - os: windows: windows cpu: x86_64: 386 arm_64: arm64 - url: https://github.com/mikefarah/yq/releases/download/v${version}/yq_${os}_${cpu}.zip + url: https://github.com/mikefarah/yq/releases/download/v${version}/yq_${os}_${cpu}.exe tools: definitions: - name: yq download: yq - known_good_version: 4.40.5 + known_good_version: 4.44.1 shims: [yq] + health_checks: + - command: yq --version + parse_regex: version v${semver} diff --git a/tools/yq/yq.test.ts b/tools/yq/yq.test.ts index f2318f455..c256e557a 100644 --- a/tools/yq/yq.test.ts +++ b/tools/yq/yq.test.ts @@ -1,7 +1,7 @@ import { toolInstallTest } from "tests"; -// TODO(Tyler): tool def is missing healthcheck +// The binary name varies by platform so we can't roll this into a health_check as-is. toolInstallTest({ toolName: "yq", - toolVersion: "4.40.5", + toolVersion: "4.44.1", });