mirror of
https://github.com/morluto/rea.git
synced 2026-10-31 11:56:12 +00:00
* fix(electron): retain scoped IPC registration provenance * fix(electron): defer mutation analysis until scoped receiver admission
115 lines
4.7 KiB
TypeScript
115 lines
4.7 KiB
TypeScript
import { expect } from "vitest";
|
|
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { writeFixtureFiles } from "../../support/javascriptApplicationFixture.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
import { analyzeJavaScriptDirectory } from "../../support/cli/javascriptApplication.js";
|
|
|
|
cliTest.for(["CLI", "stdio MCP"] as const)(
|
|
"preserves scoped IPC allocations without global dispatch pairing through %s",
|
|
async (surface, { cli }) => {
|
|
const root = await createTestTempDirectory("rea-scoped-ipc-");
|
|
const copy = `const {BrowserWindow}=require("electron"); const window=new BrowserWindow(); window.webContents.ipc.handle("copied",()=>{});`;
|
|
await writeFixtureFiles(root, {
|
|
"copy-one.cjs": copy,
|
|
"copy-two.cjs": copy,
|
|
"main.cjs": `const { BrowserWindow, ipcMain } = require("electron");
|
|
const first = new BrowserWindow();
|
|
const second = new BrowserWindow();
|
|
first.webContents.mainFrame.ipc.handle("shared", () => "first");
|
|
second.webContents.mainFrame.ipc.handle("shared", () => "second");
|
|
ipcMain.handle("shared", () => "global");
|
|
const dynamicWindow = new BrowserWindow();
|
|
dynamicWindow.webContents.ipc.handle(dynamicChannel, () => "dynamic");
|
|
ipcMain.handle("dynamic-shadow", () => "global");
|
|
const fake = { webContents: { ipc: { handle() {} } } };
|
|
fake.webContents.ipc.handle("fake", () => {});`,
|
|
"preload.cjs": `const { ipcRenderer } = require("electron");
|
|
ipcRenderer.invoke("shared");
|
|
ipcRenderer.invoke("dynamic-shadow");`,
|
|
});
|
|
const application = await analyzeJavaScriptDirectory(surface, cli, root);
|
|
expect(application.statistics.parse_failures).toBe(0);
|
|
expect(application.summary.ipc).toMatchObject({
|
|
operations: 9,
|
|
main_handlers: 7,
|
|
renderer_transmissions: 2,
|
|
paired_renderer_transmissions: 0,
|
|
ambiguous_renderer_transmissions: 2,
|
|
});
|
|
const channels = application.graph.nodes.filter(
|
|
({ kind }) => kind === "ipc-channel",
|
|
);
|
|
const copies = application.graph.nodes.filter(
|
|
({ kind, observations }) =>
|
|
kind === "ipc-handler" &&
|
|
observations.some(({ properties }) => properties.channel === "copied"),
|
|
);
|
|
expect(copies).toHaveLength(2);
|
|
expect(
|
|
channels.filter(({ observations }) =>
|
|
observations.some(({ properties }) => properties.channel === "copied"),
|
|
),
|
|
).toHaveLength(2);
|
|
const shared = channels.filter(({ observations }) =>
|
|
observations.some(({ properties }) => properties.channel === "shared"),
|
|
);
|
|
expect(shared).toHaveLength(3);
|
|
const scoped = shared.filter(({ observations }) =>
|
|
observations.some(({ properties }) => properties.receiver !== undefined),
|
|
);
|
|
expect(scoped).toHaveLength(2);
|
|
const owners = scoped.flatMap(({ observations }) =>
|
|
observations.map(({ properties }) => properties.receiver),
|
|
);
|
|
expect(new Set(owners.map((owner) => JSON.stringify(owner))).size).toBe(2);
|
|
expect(
|
|
channels.some(({ observations }) =>
|
|
observations.some(({ properties }) => properties.channel === "fake"),
|
|
),
|
|
).toBe(false);
|
|
expect(
|
|
application.graph.edges.filter(
|
|
({ evidence }) =>
|
|
evidence.extractor.operation === "pair-ipc-literal-channel",
|
|
),
|
|
).toEqual([]);
|
|
},
|
|
);
|
|
|
|
cliTest.for(["CLI", "stdio MCP"] as const)(
|
|
"does not uniquely pair a renderer with exactly one scoped literal handler through %s",
|
|
async (surface, { cli }) => {
|
|
const root = await createTestTempDirectory("rea-single-scoped-ipc-");
|
|
await writeFixtureFiles(root, {
|
|
"main.cjs": `const { BrowserWindow } = require("electron");
|
|
const window = new BrowserWindow();
|
|
window.webContents.mainFrame.ipc.handle("only-scoped", () => "result");`,
|
|
"preload.cjs": `const { ipcRenderer } = require("electron");
|
|
ipcRenderer.invoke("only-scoped");`,
|
|
});
|
|
const application = await analyzeJavaScriptDirectory(surface, cli, root);
|
|
expect(application.summary.ipc).toMatchObject({
|
|
operations: 2,
|
|
main_handlers: 1,
|
|
renderer_transmissions: 1,
|
|
dynamic_channel_operations: 0,
|
|
paired_renderer_transmissions: 0,
|
|
ambiguous_renderer_transmissions: 1,
|
|
});
|
|
const handlers = application.graph.nodes.filter(
|
|
({ kind }) => kind === "ipc-handler",
|
|
);
|
|
expect(handlers).toHaveLength(1);
|
|
expect(handlers[0]?.observations[0]?.properties.receiver).toMatchObject({
|
|
kind: "main-frame",
|
|
});
|
|
expect(
|
|
application.graph.edges.filter(
|
|
({ evidence }) =>
|
|
evidence.extractor.operation === "pair-ipc-literal-channel",
|
|
),
|
|
).toEqual([]);
|
|
},
|
|
);
|