fix(ghidra): keep earlier LaunchSupport warnings when a later helper fails
d4bd65c53moved a failed LaunchSupport helper's output into the nested `command_outcome`, but the cause projection also returned early for those failures and dropped `launch_support`. A `-vmargs` failure therefore lost the warning that the successful `-envvars` helper had already reported. Keep the retained helper outputs and skip only the launcher outcome that the nested cause now owns. Update the Ghidra launch and Hopper startup boundary expectations to current main: failed helpers report through `cause.command_outcome`, and bridge startup results carry the launched `processId` (599a8ee11), asserted against the exact retried process. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
59c71be89e
commit
7346397106
|
|
@ -263,7 +263,6 @@ const causeLaunchOutcome = (
|
|||
redact: (value: string, incomplete?: boolean) => string,
|
||||
): Record<string, JsonValue> => {
|
||||
if (!(cause instanceof GhidraLaunchError)) return {};
|
||||
if (cause.cause instanceof GhidraLaunchSupportError) return {};
|
||||
const launchSupport =
|
||||
cause.launchSupport.length === 0
|
||||
? {}
|
||||
|
|
@ -273,6 +272,9 @@ const causeLaunchOutcome = (
|
|||
stderr: redact(output.stderr),
|
||||
})),
|
||||
};
|
||||
// A failed LaunchSupport helper reports its own command outcome in the
|
||||
// nested cause; earlier helper warnings still belong to this launch.
|
||||
if (cause.cause instanceof GhidraLaunchSupportError) return launchSupport;
|
||||
const output = cause.capturedOutput;
|
||||
if (output === undefined) return launchSupport;
|
||||
const stdout = boundedUtf8Text(
|
||||
|
|
|
|||
|
|
@ -188,9 +188,13 @@ describe.skipIf(process.platform === "win32")(
|
|||
const cause = failure.diagnostics.failure_cause;
|
||||
if (mode === "env-failure") {
|
||||
expect(cause).toMatchObject({
|
||||
launch_outcome: {
|
||||
exit_code: 3,
|
||||
stderr: expect.stringContaining(`-envvars: ${warning}`),
|
||||
cause: {
|
||||
name: "GhidraLaunchSupportError",
|
||||
command_outcome: {
|
||||
killed: false,
|
||||
stderr: expect.stringContaining(`-envvars: ${warning}`),
|
||||
},
|
||||
cause: { code: 3 },
|
||||
},
|
||||
});
|
||||
} else {
|
||||
|
|
@ -205,9 +209,13 @@ describe.skipIf(process.platform === "win32")(
|
|||
}
|
||||
if (mode === "vm-failure")
|
||||
expect(cause).toMatchObject({
|
||||
launch_outcome: {
|
||||
exit_code: 3,
|
||||
stderr: expect.stringContaining(`-vmargs: ${warning}`),
|
||||
cause: {
|
||||
name: "GhidraLaunchSupportError",
|
||||
command_outcome: {
|
||||
killed: false,
|
||||
stderr: expect.stringContaining(`-vmargs: ${warning}`),
|
||||
},
|
||||
cause: { code: 3 },
|
||||
},
|
||||
});
|
||||
if (mode === "vm-malformed")
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class CancelThenFixtureLauncher implements BridgeLauncher {
|
|||
readonly firstLaunch = new Promise<ChildProcess>((resolve) => {
|
||||
this.#firstLaunchReady = resolve;
|
||||
});
|
||||
retriedProcess: ChildProcess | undefined;
|
||||
|
||||
async launch(session: BridgeSession) {
|
||||
this.#launches += 1;
|
||||
|
|
@ -49,6 +50,7 @@ class CancelThenFixtureLauncher implements BridgeLauncher {
|
|||
: new FixtureLauncher().launch(session));
|
||||
if (this.#launches === 1 && launched.ok)
|
||||
this.#firstLaunchReady?.(launched.value.process);
|
||||
else if (launched.ok) this.retriedProcess = launched.value.process;
|
||||
return launched;
|
||||
}
|
||||
}
|
||||
|
|
@ -256,9 +258,17 @@ describe("HopperClient startup failures", () => {
|
|||
expect(
|
||||
firstProcess.exitCode !== null || firstProcess.signalCode !== null,
|
||||
).toBe(true);
|
||||
await expect(client.start()).resolves.toEqual({
|
||||
const restarted = await client.start();
|
||||
const retriedProcess = launcher.retriedProcess;
|
||||
expect(retriedProcess).toBeDefined();
|
||||
expect(retriedProcess).not.toBe(firstProcess);
|
||||
expect(restarted).toEqual({
|
||||
ok: true,
|
||||
value: { name: "REA Hopper bridge", version: "1.0.0" },
|
||||
value: {
|
||||
name: "REA Hopper bridge",
|
||||
version: "1.0.0",
|
||||
processId: retriedProcess?.pid,
|
||||
},
|
||||
});
|
||||
await expect(
|
||||
client.callTool("echo", { value: "retried" }),
|
||||
|
|
@ -282,11 +292,16 @@ describe("HopperClient startup failures", () => {
|
|||
error: { _tag: "HopperCancelledError" },
|
||||
});
|
||||
expect(launcher.processes).toHaveLength(0);
|
||||
await expect(client.start()).resolves.toEqual({
|
||||
ok: true,
|
||||
value: { name: "REA Hopper bridge", version: "1.0.0" },
|
||||
});
|
||||
const started = await client.start();
|
||||
expect(launcher.processes).toHaveLength(1);
|
||||
expect(started).toEqual({
|
||||
ok: true,
|
||||
value: {
|
||||
name: "REA Hopper bridge",
|
||||
version: "1.0.0",
|
||||
processId: launcher.processes[0]?.pid,
|
||||
},
|
||||
});
|
||||
await expect(
|
||||
client.callTool("echo", { value: "retried" }),
|
||||
).resolves.toEqual({
|
||||
|
|
|
|||
Loading…
Reference in New Issue