Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/evm/EvmoleFailures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ export const evmInterfaceFailure = (
message: cause.message,
exit_code: cause.snapshot?.exitCode ?? null,
signal: cause.snapshot?.signal ?? null,
stdout: cause.snapshot?.stdout.text ?? null,
stderr: cause.snapshot?.stderr.text ?? null,
},
},
{ operation: "inspect_evm_interface", cause },
Expand Down
24 changes: 2 additions & 22 deletions src/evm/EvmoleInterfaceProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,28 +401,8 @@ export class EvmoleInterfaceProvider implements EvmInterfacePort {
}
if (processOwner !== undefined) {
this.#pendingCleanup = { root, process: processOwner };
root = undefined;
return err(
new ProviderCleanupError(
EVMOLE_PROVIDER_IDENTITY.id,
[
processOwner.launch.ownership?.runId ?? "owned-worker",
...(this.#pendingCleanup.root === undefined
? []
: [this.#pendingCleanup.root.path]),
],
{
reason: "Owned EVM worker cleanup could not be confirmed",
previous_error: result.ok
? null
: projectAnalysisError(result.error),
...(retainedOutput === undefined
? {}
: { captured_output: { ...retainedOutput } }),
},
{ operation: OPERATION },
),
);
// The command failure already owns this cleanup diagnostic and its resources.
return result;
}
if (root !== undefined) {
try {
Expand Down
8 changes: 8 additions & 0 deletions tests/boundary/browser/cdpCleanupCancellation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ it.each(["Page.disable", "Target.detachFromTarget"])(
expect(methods).toContain("Target.detachFromTarget");
expect(methods).not.toContain("Target.closeTarget");
expect(methods).not.toContain("Browser.close");
await vi.waitFor(() => expect(browser.activeConnectionCount()).toBe(0));
},
);

Expand Down Expand Up @@ -76,6 +77,11 @@ it("releases cleanup when cancellation arrives after capture completes", async (
expect(browser.commands.map(({ method }) => method)).toContain(
"Target.detachFromTarget",
);
await vi.waitFor(() => expect(browser.activeConnectionCount()).toBe(0));
const methods = browser.commands.map(({ method }) => method);
expect(methods).not.toContain("Target.closeTarget");
expect(methods).not.toContain("Browser.close");
expect((await fetch(`${browser.endpoint}/json/version`)).ok).toBe(true);
});

it("closes an Electron inspection session when cleanup is cancelled", async () => {
Expand Down Expand Up @@ -127,4 +133,6 @@ it("closes an Electron inspection session when cleanup is cancelled", async () =
expect(methods).toContain("Target.detachFromTarget");
expect(methods).not.toContain("Target.closeTarget");
expect(methods).not.toContain("Browser.close");
await vi.waitFor(() => expect(browser.activeConnectionCount()).toBe(0));
expect((await fetch(`${browser.endpoint}/json/version`)).ok).toBe(true);
});
46 changes: 27 additions & 19 deletions tests/boundary/evm/evmoleInterfaceProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ it.runIf(!unsupportedHost)(
const { path } = await fixture();
let ownedPath = "";
let failCleanup = true;
let launches = 0;
const provider = new EvmoleInterfaceProvider({}, async (spawn) => {
launches += 1;
ownedPath = spawn.cwd ?? "";
const launched = await spawnOwnedProviderProcess({
...spawn,
Expand Down Expand Up @@ -158,36 +160,42 @@ it.runIf(!unsupportedHost)(
if (result.ok) throw new Error("Expected compounded lifecycle failure");
expect(result.error.cleanupIncomplete).toBe(true);
// Inspect the retained error chain without diffing a megabyte of stdout.
const output = z.object({ truncated: z.boolean(), stderr: z.string() });
const cleanup = z
.object({
reason: z.string(),
previous_error: z.object({
code: z.string(),
details: z.object({
diagnostics: z.object({
reason: z.string(),
previous_error: z.object({ failure_kind: z.string() }),
captured_output: output,
}),
}),
previous_error: z
.object({
failure_kind: z.string(),
stdout: z.unknown().optional(),
stderr: z.unknown().optional(),
})
.transform(({ failure_kind, stdout, stderr }) => ({
failure_kind,
output_repeated: stdout !== undefined || stderr !== undefined,
})),
captured_output: z.object({
truncated: z.boolean(),
stderr: z.literal(""),
stdout: z.string().transform((value) => Buffer.byteLength(value)),
}),
})
.parse(projectAnalysisError(result.error).details?.["diagnostics"]);
expect(cleanup).toEqual({
reason: "Owned EVM worker cleanup could not be confirmed",
reason: "injected post-cleanup reporting failure",
previous_error: {
code: "cleanup_incomplete",
details: {
diagnostics: {
reason: "injected post-cleanup reporting failure",
previous_error: { failure_kind: "output-limit" },
captured_output: { truncated: true, stderr: "" },
},
},
failure_kind: "output-limit",
output_repeated: false,
},
captured_output: { truncated: true, stderr: "", stdout: 1048576 },
});
expect(result.error.cleanupResources).toContain(ownedPath);
await access(ownedPath);
const blocked = await provider.inspect({ path, encoding: "hex" });
expect(blocked.ok).toBe(false);
expect(launches).toBe(1);
await expect(provider.close()).rejects.toMatchObject({
cleanupIncomplete: true,
});
failCleanup = false;
await provider.close();
await expect(access(ownedPath)).rejects.toMatchObject({ code: "ENOENT" });
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/fakeCdpBrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export const startFakeCdpBrowser = async (
browserWebSocketUrl: `ws://127.0.0.1:${String(port)}/devtools/browser/fake`,
allowedOrigin: endpoint,
commands,
activeConnectionCount: () => sockets.size,
emitRawMessage(message) {
for (const socket of sockets) socket.send(message);
},
Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/fakeCdpBrowserTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export interface FakeCdpBrowser {
readonly browserWebSocketUrl: string;
readonly allowedOrigin: string;
readonly commands: readonly FakeCdpCommand[];
/** Actual WebSocket connections still held by the wire fixture. */
activeConnectionCount(): number;
/** Deliver actual malformed wire data without bypassing the production transport parser. */
emitRawMessage(message: string): void;
emitEvent(event: {
Expand Down
Loading