import { assert, describe, it } from "@effect/vitest"; import * as Cause from "effect/Cause"; import * as Effect from "effect/Effect"; import { vi } from "vite-plus/test"; import * as DesktopIpc from "./DesktopIpc.ts"; const invokeMethod: DesktopIpc.DesktopIpcMethod = { channel: "desktop.test.invoke", handler: () => Effect.void, }; const syncMethod: DesktopIpc.DesktopSyncIpcMethod = { channel: "desktop.test.sync", handler: () => Effect.void, }; function makeIpcMain( overrides: Partial = {}, ): DesktopIpc.DesktopIpcMain { return { removeHandler: vi.fn(), handle: vi.fn(), removeAllListeners: vi.fn(), on: vi.fn(), ...overrides, }; } describe("DesktopIpc", () => { it.effect("preserves invoke registration context and cause", () => Effect.gen(function* () { const cause = new Error("invoke registration failed"); const ipcMain = makeIpcMain({ handle: () => { throw cause; }, }); const ipc = DesktopIpc.make(ipcMain); const error = yield* Effect.flip(Effect.scoped(ipc.handle(invokeMethod))); assert.instanceOf(error, DesktopIpc.DesktopIpcRegistrationError); assert.strictEqual(error.handlerKind, "invoke"); assert.strictEqual(error.channel, invokeMethod.channel); assert.strictEqual(error.cause, cause); assert.include(error.message, "invoke"); assert.include(error.message, invokeMethod.channel); assert.notInclude(error.message, cause.message); }), ); it.effect("forwards the invoke sender to the method", () => Effect.gen(function* () { let listener: DesktopIpc.DesktopIpcHandleListener | undefined; const ipc = DesktopIpc.make( makeIpcMain({ handle: (_channel, registered) => { listener = registered; }, }), ); const sender = { sender: { id: 7 } }; let received: DesktopIpc.DesktopIpcInvokeEvent | undefined; yield* Effect.scoped( Effect.gen(function* () { yield* ipc.handle({ channel: "desktop.test.sender", handler: (_raw, event) => Effect.sync(() => { received = event; }), }); yield* Effect.promise(async () => listener!(sender, undefined)); }), ); assert.strictEqual(received, sender); }), ); it.effect("preserves sync unregistration context and cause in the finalizer defect", () => Effect.gen(function* () { const cause = new Error("sync unregistration failed"); let removeCount = 0; const ipcMain = makeIpcMain({ removeAllListeners: () => { removeCount += 1; if (removeCount === 2) throw cause; }, }); const ipc = DesktopIpc.make(ipcMain); const exit = yield* Effect.exit(Effect.scoped(ipc.handleSync(syncMethod))); assert.isTrue(exit._tag === "Failure"); if (exit._tag === "Success") return; const error = Cause.squash(exit.cause); assert.instanceOf(error, DesktopIpc.DesktopIpcUnregistrationError); assert.strictEqual(error.handlerKind, "sync"); assert.strictEqual(error.channel, syncMethod.channel); assert.strictEqual(error.cause, cause); assert.notInclude(error.message, cause.message); }), ); });