|
1 | | -import { EventEmitter } from "node:events"; |
2 | | -import { describe, expect, it } from "vitest"; |
3 | | -import { z } from "zod/v4"; |
| 1 | +import { fork, type ChildProcess } from "node:child_process"; |
| 2 | +import { fileURLToPath } from "node:url"; |
| 3 | +import { afterEach, describe, expect, it } from "vitest"; |
4 | 4 | import { ZodIpcConnection } from "../src/v3/zodIpc.js"; |
| 5 | +import { ChildToParent, ParentToChild } from "./fixtures/zodIpcCatalog.js"; |
5 | 6 |
|
6 | | -// A pair of in-memory endpoints that serialize packets the same way Node's default |
7 | | -// ("json") IPC serialization does, so keys with `undefined` values are dropped in transit. |
8 | | -function createIpcPair() { |
9 | | - const parentEvents = new EventEmitter(); |
10 | | - const childEvents = new EventEmitter(); |
| 7 | +const childPath = fileURLToPath(new URL("./fixtures/zodIpcChild.ts", import.meta.url)); |
11 | 8 |
|
12 | | - const endpoint = (inbox: EventEmitter, outbox: EventEmitter) => ({ |
13 | | - connected: true, |
14 | | - send: (message: unknown) => { |
15 | | - const serialized = JSON.stringify(message); |
16 | | - setImmediate(() => outbox.emit("message", JSON.parse(serialized))); |
17 | | - return true; |
18 | | - }, |
19 | | - on: (event: "message", listener: (message: any) => void) => { |
20 | | - inbox.on(event, listener); |
21 | | - }, |
22 | | - }); |
23 | | - |
24 | | - return { |
25 | | - parent: endpoint(parentEvents, childEvents), |
26 | | - child: endpoint(childEvents, parentEvents), |
27 | | - }; |
28 | | -} |
29 | | - |
30 | | -const ParentToChild = { |
31 | | - FLUSH: { |
32 | | - message: z.object({ timeoutInMs: z.number() }), |
33 | | - callback: z.void(), |
34 | | - }, |
35 | | - PING: { |
36 | | - message: z.object({ value: z.string() }), |
37 | | - callback: z.object({ echoed: z.string() }), |
38 | | - }, |
39 | | -}; |
| 9 | +let child: ChildProcess | undefined; |
40 | 10 |
|
41 | | -const ChildToParent = {}; |
| 11 | +afterEach(() => { |
| 12 | + child?.kill(); |
| 13 | + child = undefined; |
| 14 | +}); |
42 | 15 |
|
43 | | -function createConnections() { |
44 | | - const { parent, child } = createIpcPair(); |
| 16 | +// Node's default IPC serialization is JSON, so an ack with `message: undefined` |
| 17 | +// arrives without the `message` key, exactly as it does between a worker and its task run process. |
| 18 | +function forkChild() { |
| 19 | + child = fork(childPath, { execArgv: ["--import", "tsx"], stdio: "inherit" }); |
45 | 20 |
|
46 | | - const parentConnection = new ZodIpcConnection({ |
| 21 | + return new ZodIpcConnection({ |
47 | 22 | listenSchema: ChildToParent, |
48 | 23 | emitSchema: ParentToChild, |
49 | | - process: parent, |
50 | | - }); |
51 | | - |
52 | | - new ZodIpcConnection({ |
53 | | - listenSchema: ParentToChild, |
54 | | - emitSchema: ChildToParent, |
55 | 24 | process: child, |
56 | | - handlers: { |
57 | | - FLUSH: async () => {}, |
58 | | - PING: async ({ value }) => ({ echoed: value }), |
59 | | - }, |
60 | 25 | }); |
61 | | - |
62 | | - return parentConnection; |
63 | 26 | } |
64 | 27 |
|
65 | 28 | describe("ZodIpcConnection", () => { |
66 | | - it("resolves sendWithAck for a void callback after the ack crosses a JSON boundary", async () => { |
67 | | - const connection = createConnections(); |
| 29 | + it("resolves sendWithAck for a void callback", async () => { |
| 30 | + const connection = forkChild(); |
68 | 31 |
|
69 | | - await expect(connection.sendWithAck("FLUSH", { timeoutInMs: 1000 }, 1000)).resolves.toBe( |
| 32 | + await expect(connection.sendWithAck("FLUSH", { timeoutInMs: 1000 }, 2000)).resolves.toBe( |
70 | 33 | undefined |
71 | 34 | ); |
72 | 35 | }); |
73 | 36 |
|
74 | 37 | it("resolves sendWithAck with the callback payload", async () => { |
75 | | - const connection = createConnections(); |
| 38 | + const connection = forkChild(); |
76 | 39 |
|
77 | | - await expect(connection.sendWithAck("PING", { value: "hello" }, 1000)).resolves.toEqual({ |
| 40 | + await expect(connection.sendWithAck("PING", { value: "hello" }, 2000)).resolves.toEqual({ |
78 | 41 | echoed: "hello", |
79 | 42 | }); |
80 | 43 | }); |
|
0 commit comments