Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
test(imessage): exercise real stream failure
  • Loading branch information
steipete committed Jul 7, 2026
commit 4ffcce0f5eabc702a4bfb8aca9e455cbd346e70a
36 changes: 34 additions & 2 deletions extensions/imessage/src/client.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Imessage tests cover the RPC client child-process stream error handling.
// iMessage tests cover the RPC client child-process stream error handling.
import { EventEmitter } from "node:events";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

Expand All @@ -20,6 +20,7 @@ type MockChild = EventEmitter & {
write: (line: string, cb?: (err?: Error | null) => void) => boolean;
end: () => void;
};
killed: boolean;
kill: ReturnType<typeof vi.fn>;
};

Expand All @@ -36,7 +37,11 @@ function createMockChild(): MockChild {
};
stdin.end = () => {};
child.stdin = stdin;
child.kill = vi.fn();
child.killed = false;
child.kill = vi.fn(() => {
child.killed = true;
return true;
});
return child;
}

Expand Down Expand Up @@ -80,4 +85,31 @@ describe("IMessageRpcClient child stream error handling", () => {
await client.stop();
},
);

it("settles the client after a real child stdout stream failure", async () => {
const childProcess =
await vi.importActual<typeof import("node:child_process")>("node:child_process");
const realChild = childProcess.spawn(process.execPath, ["-e", "setInterval(() => {}, 1000)"], {
stdio: ["pipe", "pipe", "pipe"],
});
spawnMock.mockReturnValueOnce(realChild);
const { IMessageRpcClient } = await import("./client.js");
const client = new IMessageRpcClient({ cliPath: "imsg" });
await client.start();

try {
const pending = client.request("ping", {}, { timeoutMs: 0 });
pending.catch(() => {});
realChild.stdout.destroy(new Error("real stdout failure"));

await expect(pending).rejects.toThrow("real stdout failure");
await expect(client.waitForClose()).resolves.toBeUndefined();
expect(realChild.killed).toBe(true);
} finally {
if (!realChild.killed) {
realChild.kill("SIGTERM");
}
await client.stop();
}
});
});
Loading