|
1 | 1 | // Imessage tests cover send plugin behavior. |
| 2 | +import { EventEmitter } from "node:events"; |
2 | 3 | import fs from "node:fs"; |
3 | 4 | import os from "node:os"; |
4 | 5 | import path from "node:path"; |
@@ -29,6 +30,13 @@ const IMESSAGE_TEST_CFG = { |
29 | 30 | }, |
30 | 31 | }; |
31 | 32 |
|
| 33 | +const spawnMock = vi.hoisted(() => vi.fn()); |
| 34 | + |
| 35 | +vi.mock("node:child_process", async (importOriginal) => ({ |
| 36 | + ...(await importOriginal<typeof import("node:child_process")>()), |
| 37 | + spawn: spawnMock, |
| 38 | +})); |
| 39 | + |
32 | 40 | function createClient(result: Record<string, unknown>): IMessageRpcClient { |
33 | 41 | return { |
34 | 42 | request: vi.fn(async () => result), |
@@ -79,6 +87,7 @@ describe("sendMessageIMessage receipts", () => { |
79 | 87 | vi.restoreAllMocks(); |
80 | 88 | vi.unstubAllEnvs(); |
81 | 89 | vi.useRealTimers(); |
| 90 | + spawnMock.mockReset(); |
82 | 91 | }); |
83 | 92 |
|
84 | 93 | it("attaches a text receipt for native send ids", async () => { |
@@ -1265,3 +1274,62 @@ describe("sendMessageIMessage receipts", () => { |
1265 | 1274 | expect(runCliJson).not.toHaveBeenCalled(); |
1266 | 1275 | }); |
1267 | 1276 | }); |
| 1277 | + |
| 1278 | +function mockSpawnWithStreamError(stream: "stdout" | "stderr", error: Error) { |
| 1279 | + spawnMock.mockImplementationOnce(() => { |
| 1280 | + const child = new EventEmitter() as EventEmitter & { |
| 1281 | + stdout: EventEmitter & { setEncoding: (encoding: string) => void }; |
| 1282 | + stderr: EventEmitter & { setEncoding: (encoding: string) => void }; |
| 1283 | + kill: (signal: string) => void; |
| 1284 | + }; |
| 1285 | + child.stdout = Object.assign(new EventEmitter(), { setEncoding: vi.fn() }); |
| 1286 | + child.stderr = Object.assign(new EventEmitter(), { setEncoding: vi.fn() }); |
| 1287 | + child.kill = vi.fn(); |
| 1288 | + queueMicrotask(() => { |
| 1289 | + child[stream].emit("error", error); |
| 1290 | + }); |
| 1291 | + return child; |
| 1292 | + }); |
| 1293 | +} |
| 1294 | + |
| 1295 | +describe("sendMessageIMessage CLI stream errors", () => { |
| 1296 | + beforeEach(() => { |
| 1297 | + installIMessageStateRuntimeForTest(); |
| 1298 | + resetIMessageShortIdState(); |
| 1299 | + resetPersistedIMessageEchoCacheForTest(); |
| 1300 | + }); |
| 1301 | + |
| 1302 | + afterEach(() => { |
| 1303 | + clearIMessageApprovalReactionTargetsForTest(); |
| 1304 | + resetIMessageShortIdState(); |
| 1305 | + resetPersistedIMessageEchoCacheForTest(); |
| 1306 | + vi.restoreAllMocks(); |
| 1307 | + vi.unstubAllEnvs(); |
| 1308 | + vi.useRealTimers(); |
| 1309 | + spawnMock.mockReset(); |
| 1310 | + }); |
| 1311 | + |
| 1312 | + it("rejects on stdout stream error during attachment send", async () => { |
| 1313 | + mockSpawnWithStreamError("stdout", new Error("stdout pipe broken")); |
| 1314 | + |
| 1315 | + await expect( |
| 1316 | + sendMessageIMessage("chat_guid:chat-1", "", { |
| 1317 | + config: IMESSAGE_TEST_CFG, |
| 1318 | + mediaUrl: "/tmp/image.png", |
| 1319 | + resolveAttachmentImpl: async () => ({ path: "/tmp/image.png", contentType: "image/png" }), |
| 1320 | + }), |
| 1321 | + ).rejects.toThrow("iMessage CLI stdout stream error: stdout pipe broken"); |
| 1322 | + }); |
| 1323 | + |
| 1324 | + it("rejects on stderr stream error during attachment send", async () => { |
| 1325 | + mockSpawnWithStreamError("stderr", new Error("stderr pipe broken")); |
| 1326 | + |
| 1327 | + await expect( |
| 1328 | + sendMessageIMessage("chat_guid:chat-1", "", { |
| 1329 | + config: IMESSAGE_TEST_CFG, |
| 1330 | + mediaUrl: "/tmp/image.png", |
| 1331 | + resolveAttachmentImpl: async () => ({ path: "/tmp/image.png", contentType: "image/png" }), |
| 1332 | + }), |
| 1333 | + ).rejects.toThrow("iMessage CLI stderr stream error: stderr pipe broken"); |
| 1334 | + }); |
| 1335 | +}); |
0 commit comments