fix(meeting-bot): preserve spawn failure diagnostics (#107614)

This commit is contained in:
xingzhou
2026-07-19 16:17:46 +08:00
committed by GitHub
parent 59124313f4
commit 7b3a8dba14
2 changed files with 94 additions and 2 deletions

View File

@@ -163,6 +163,93 @@ describe("google-meet node host bridge sessions", () => {
}
});
it("rejects Chrome launch when the command exits by signal", async () => {
const originalPlatform = process.platform;
vi.mocked(spawnSync).mockImplementationOnce(() => ({
pid: 123,
output: [null, "", ""],
stdout: "",
stderr: "",
status: null,
signal: "SIGTERM",
}));
Object.defineProperty(process, "platform", { configurable: true, value: "darwin" });
try {
await expect(
handleGoogleMeetNodeHostCommand(
JSON.stringify({
action: "start",
url: "https://meet.google.com/xyz-abcd-uvw",
mode: "transcribe",
}),
),
).rejects.toThrow("failed to launch Chrome for Meet: terminated by SIGTERM");
} finally {
Object.defineProperty(process, "platform", { configurable: true, value: originalPlatform });
}
});
it("preserves timeout diagnostics when Chrome launch stderr is empty", async () => {
const originalPlatform = process.platform;
const error = Object.assign(new Error("spawnSync open ETIMEDOUT"), { code: "ETIMEDOUT" });
vi.mocked(spawnSync).mockImplementationOnce(() => ({
pid: 123,
output: [null, "", ""],
stdout: "",
stderr: "",
status: null,
signal: "SIGTERM",
error,
}));
Object.defineProperty(process, "platform", { configurable: true, value: "darwin" });
try {
await expect(
handleGoogleMeetNodeHostCommand(
JSON.stringify({
action: "start",
url: "https://meet.google.com/xyz-abcd-uvw",
mode: "transcribe",
}),
),
).rejects.toThrow("failed to launch Chrome for Meet: spawnSync open ETIMEDOUT");
} finally {
Object.defineProperty(process, "platform", { configurable: true, value: originalPlatform });
}
});
it("preserves timeout diagnostics when Chrome launch also writes stderr", async () => {
const originalPlatform = process.platform;
const error = Object.assign(new Error("spawnSync open ETIMEDOUT"), { code: "ETIMEDOUT" });
vi.mocked(spawnSync).mockImplementationOnce(() => ({
pid: 123,
output: [null, "", "child warning"],
stdout: "",
stderr: "child warning",
status: null,
signal: "SIGTERM",
error,
}));
Object.defineProperty(process, "platform", { configurable: true, value: "darwin" });
try {
await expect(
handleGoogleMeetNodeHostCommand(
JSON.stringify({
action: "start",
url: "https://meet.google.com/xyz-abcd-uvw",
mode: "transcribe",
}),
),
).rejects.toThrow(
"failed to launch Chrome for Meet: spawnSync open ETIMEDOUT: child warning",
);
} finally {
Object.defineProperty(process, "platform", { configurable: true, value: originalPlatform });
}
});
it("clears output playback without closing the active bridge when the old output exits", async () => {
const originalPlatform = process.platform;
children.length = 0;

View File

@@ -81,10 +81,15 @@ function runCommandWithTimeout(argv: string[], timeoutMs: number) {
throw new Error("command must not be empty");
}
const result = spawnSync(command, args, { encoding: "utf8", timeout: timeoutMs });
const errorMessage = result.error ? formatErrorMessage(result.error) : "";
const stderr =
errorMessage && result.stderr
? `${errorMessage}: ${result.stderr}`
: errorMessage || result.stderr || (result.signal ? `terminated by ${result.signal}` : "");
return {
code: typeof result.status === "number" ? result.status : result.error ? 1 : 0,
code: typeof result.status === "number" ? result.status : 1,
stdout: result.stdout ?? "",
stderr: result.stderr ?? (result.error ? formatErrorMessage(result.error) : ""),
stderr,
};
}