mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-21 05:50:55 +08:00
- Enforce oxlint and oxfmt for owned JavaScript and TypeScript with independent CI checks. - Preserve compiler import boundaries and protected fixtures while cleaning up lint findings. - Standardize source formatting and validate import, fixture, and embedded-source protections.
148 lines
5.7 KiB
TypeScript
148 lines
5.7 KiB
TypeScript
import { execFile, execFileSync } from "node:child_process";
|
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { promisify } from "node:util";
|
|
import { afterEach, expect, test, vi } from "vitest";
|
|
import {
|
|
REMOTE_COMMAND_PENDING,
|
|
sandboxCommand,
|
|
sandboxStatusCommand,
|
|
waitForSandboxCommand,
|
|
} from "../../scripts/sandbox-command.mjs";
|
|
|
|
const roots: string[] = [];
|
|
const statuses: string[] = [];
|
|
|
|
afterEach(async () => {
|
|
vi.useRealTimers();
|
|
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
await Promise.all(statuses.splice(0).map((path) => rm(path, { force: true })));
|
|
});
|
|
|
|
test("status recovery retries lost probes and pending commands until a confirmed success", async () => {
|
|
vi.useFakeTimers();
|
|
const probe = vi
|
|
.fn()
|
|
.mockRejectedValueOnce(new Error("transport disconnected"))
|
|
.mockRejectedValueOnce(
|
|
Object.assign(new Error("pending"), { remoteExitCode: REMOTE_COMMAND_PENDING }),
|
|
)
|
|
.mockResolvedValueOnce(undefined);
|
|
const onPending = vi.fn();
|
|
const recovery = waitForSandboxCommand(probe, {
|
|
deadline: Date.now() + 5000,
|
|
label: "tests",
|
|
onPending,
|
|
});
|
|
await vi.runAllTimersAsync();
|
|
await expect(recovery).resolves.toBeUndefined();
|
|
expect(probe.mock.calls).toEqual([[5000], [4000], [3000]]);
|
|
expect(onPending).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
test("status recovery preserves a confirmed remote failure after a transport failure", async () => {
|
|
vi.useFakeTimers();
|
|
const failure = Object.assign(new Error("tests failed"), { remoteExitCode: 7 });
|
|
const probe = vi.fn().mockRejectedValueOnce(new Error("disconnected")).mockRejectedValue(failure);
|
|
const recovery = expect(
|
|
waitForSandboxCommand(probe, {
|
|
deadline: Date.now() + 5000,
|
|
label: "tests",
|
|
onPending: vi.fn(),
|
|
}),
|
|
).rejects.toBe(failure);
|
|
await vi.runAllTimersAsync();
|
|
await recovery;
|
|
expect(probe).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
test("unconfirmed status probes cannot extend the original command deadline", async () => {
|
|
vi.useFakeTimers();
|
|
const disconnected = new Error("status timeout");
|
|
const probe = vi.fn().mockRejectedValue(disconnected);
|
|
const recovery = expect(
|
|
waitForSandboxCommand(probe, {
|
|
deadline: Date.now() + 1500,
|
|
label: "tests",
|
|
onPending: vi.fn(),
|
|
}),
|
|
).rejects.toMatchObject({
|
|
message: "tests did not confirm completion before its timeout",
|
|
cause: disconnected,
|
|
});
|
|
await vi.runAllTimersAsync();
|
|
await recovery;
|
|
expect(probe.mock.calls).toEqual([[1500], [500]]);
|
|
});
|
|
|
|
test("short commands stay inline and retain the remote exit contract", async () => {
|
|
const marker = `__SCRIPTC_COMMAND_TEST_${process.pid}_SHORT__`;
|
|
const prepared = sandboxCommand("sh", ["-c", "printf 'failure detail\\n' >&2; exit 7"], marker);
|
|
statuses.push(prepared.statusPath, prepared.logPath);
|
|
|
|
expect(prepared.file).toBe(false);
|
|
const output = execFileSync(prepared.argv[0], prepared.argv.slice(1), { encoding: "utf8" });
|
|
expect(output).toBe(`failure detail\n\n${marker}7\n`);
|
|
expect(await readFile(prepared.statusPath, "utf8")).toBe("7\n");
|
|
expect(await readFile(prepared.logPath, "utf8")).toBe("failure detail\n");
|
|
});
|
|
|
|
test("uploaded scripts preserve long and shell-sensitive argument bytes", async () => {
|
|
const root = await mkdtemp("/tmp/scriptc-sandbox-command-test-");
|
|
roots.push(root);
|
|
const sentinel = join(root, "unexpected");
|
|
const args = [
|
|
"x".repeat(2048),
|
|
"quote' and spaces",
|
|
`$(touch ${sentinel})`,
|
|
"backtick` dollar$ slash\\ newline\nend",
|
|
"é".repeat(512),
|
|
];
|
|
const marker = `__SCRIPTC_COMMAND_TEST_${process.pid}_LONG__`;
|
|
const prepared = sandboxCommand("printf", ["<%s>\n", ...args], marker);
|
|
statuses.push(prepared.statusPath, prepared.logPath);
|
|
expect(prepared.file).toBe(true);
|
|
expect(prepared.argv).toEqual(["sh", prepared.scriptPath]);
|
|
expect(prepared.argv.every((arg) => arg.length < 128)).toBe(true);
|
|
|
|
const localScript = join(root, "command.sh");
|
|
await writeFile(localScript, prepared.script);
|
|
const output = execFileSync("sh", [localScript], { encoding: "utf8" });
|
|
expect(output).toBe(args.map((arg) => `<${arg}>\n`).join("") + `\n${marker}0\n`);
|
|
expect(await readFile(prepared.logPath, "utf8")).toBe(args.map((arg) => `<${arg}>\n`).join(""));
|
|
await expect(readFile(sentinel)).rejects.toMatchObject({ code: "ENOENT" });
|
|
expect(await readFile(prepared.statusPath, "utf8")).toBe("0\n");
|
|
});
|
|
|
|
test("status recovery waits for a disconnected command's actual exit", async () => {
|
|
const root = await mkdtemp("/tmp/scriptc-sandbox-status-test-");
|
|
roots.push(root);
|
|
const path = join(root, "command.status");
|
|
const marker = "__STATUS_TEST__";
|
|
const pending = promisify(execFile)("sh", ["-c", sandboxStatusCommand(path, marker, 2)]);
|
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
await writeFile(path, "7\n");
|
|
expect((await pending).stdout).toBe(`\n${marker}7\n`);
|
|
|
|
await writeFile(path, "0\n");
|
|
expect(
|
|
execFileSync("sh", ["-c", sandboxStatusCommand(path, marker, 0)], { encoding: "utf8" }),
|
|
).toBe(`\n${marker}0\n`);
|
|
await writeFile(path, "125\n");
|
|
expect(
|
|
execFileSync("sh", ["-c", sandboxStatusCommand(path, marker, 0)], { encoding: "utf8" }),
|
|
).toBe(`\n${marker}125\n`);
|
|
});
|
|
|
|
test("an absent or incomplete status remains pending rather than passing", async () => {
|
|
const root = await mkdtemp("/tmp/scriptc-sandbox-status-test-");
|
|
roots.push(root);
|
|
const path = join(root, "command.status");
|
|
const marker = "__STATUS_TEST__";
|
|
const probe = () =>
|
|
execFileSync("sh", ["-c", sandboxStatusCommand(path, marker, 0)], { encoding: "utf8" });
|
|
expect(probe()).toBe(`\n${marker}${REMOTE_COMMAND_PENDING}\n`);
|
|
await writeFile(path, "");
|
|
expect(probe()).toBe(`\n${marker}${REMOTE_COMMAND_PENDING}\n`);
|
|
});
|