Files
wehub-resource-sync 70bf21e064
Deploy (to testing) and Test Playground Preview Worker / Deploy Playground Preview Worker (testing) (push) Has been skipped
Deploy Workers Shared Staging / Deploy Workers Shared Staging (push) Failing after 0s
Prerelease / build (push) Has been skipped
Handle Changesets / Handle Changesets (push) Has been cancelled
Semgrep OSS scan / semgrep-oss (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:11 +08:00

88 lines
2.6 KiB
TypeScript

import { spawn } from "node:child_process";
import { EventEmitter } from "node:events";
import { beforeEach, describe, it, vi } from "vitest";
import { dockerBuild } from "../src/build";
vi.mock("node:child_process");
vi.mock("node:fs");
/**
* Creates a fake child process that emits a `close` event with the given exit code.
*
* @param exitCode - The exit code the fake process should emit.
* @returns A minimal child-process-like object accepted by `spawn` callers.
*/
function createFakeChildProcess(exitCode: number): ReturnType<typeof spawn> {
const emitter = new EventEmitter();
// Simulate async close so listeners are registered before the event fires.
process.nextTick(() => emitter.emit("close", exitCode));
return Object.assign(emitter, {
pid: 1234,
stdin: null,
unref: vi.fn(),
}) as unknown as ReturnType<typeof spawn>;
}
describe("dockerBuild", () => {
beforeEach(() => {
vi.mocked(spawn).mockReset();
});
it("throws a clear error without 'before building' when Docker is not running", async ({
expect,
}) => {
// The first spawn call is for `docker info` (the verification check).
// Return a fake process that exits with code 1 to simulate Docker not running.
vi.mocked(spawn).mockReturnValue(createFakeChildProcess(1));
try {
await dockerBuild("docker", {
buildCmd: ["build", "-t", "test"],
dockerfile: "FROM node:18",
});
expect.unreachable("Expected dockerBuild to throw");
} catch (error) {
const message = (error as Error).message;
expect(message).toContain(
"The Docker CLI is needed to build the image but could not be launched."
);
expect(message).not.toContain("before building");
}
});
it("skips Docker verification when verifyDockerIsRunning is false", async ({
expect,
}) => {
const fakeProcess = new EventEmitter();
const fakeStdin = { write: vi.fn(), end: vi.fn() };
Object.assign(fakeProcess, {
pid: 1234,
stdin: fakeStdin,
unref: vi.fn(),
});
vi.mocked(spawn).mockReturnValue(
fakeProcess as unknown as ReturnType<typeof spawn>
);
const resultPromise = dockerBuild("docker", {
buildCmd: ["build", "-t", "test"],
dockerfile: "FROM node:18",
verifyDockerIsRunning: false,
});
// The promise should resolve without calling docker info first.
// spawn should only be called once (for the actual build, not for docker info).
expect(spawn).toHaveBeenCalledTimes(1);
expect(spawn).toHaveBeenCalledWith(
"docker",
["build", "-t", "test"],
expect.any(Object)
);
// Simulate successful build
process.nextTick(() => fakeProcess.emit("exit", 0));
const result = await resultPromise;
await result.ready;
});
});