Compare commits

...
6 changed files with 57 additions and 22 deletions
@@ -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));
},
);
@@ -73,9 +74,11 @@ it("releases cleanup when cancellation arrives after capture completes", async (
controller.abort();
const result = await pending;
expect(result.ok).toBe(true);
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 () => {
@@ -122,11 +125,10 @@ it("closes an Electron inspection session when cleanup is cancelled", async () =
operation: "inspect_electron_page",
},
});
expect(browser.commands.map(({ method }) => method)).toContain(
"Page.disable",
);
const methods = browser.commands.map(({ method }) => method);
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);
});
@@ -7,6 +7,7 @@ import { projectAnalysisError } from "../../../src/domain/analysisErrorProjectio
import { evmInterfaceSchema } from "../../../src/domain/evm/evmInterface.js";
import { EvmoleInterfaceProvider } from "../../../src/evm/EvmoleInterfaceProvider.js";
import { spawnOwnedProviderProcess } from "../../../src/process/ProviderProcess.js";
import { cleanupOwnedProcessGroup } from "../../../src/process/ProcessOwnership.js";
import { waitForProviderProcessReady } from "../../fixtures/providerProcess.js";
import { PrivateRuntimeRoot } from "../../../src/process/PrivateRuntimeRoot.js";
import {
@@ -128,6 +129,7 @@ it.runIf(!unsupportedHost)(
async () => {
const { path } = await fixture();
let ownedPath = "";
let failCleanupReport = true;
const provider = new EvmoleInterfaceProvider({}, async (spawn) => {
ownedPath = spawn.cwd ?? "";
const launched = await spawnOwnedProviderProcess({
@@ -135,24 +137,48 @@ it.runIf(!unsupportedHost)(
command: process.execPath,
arguments: ["-e", 'process.stdout.write("x".repeat(1048577))'],
});
const ownership = launched.ownership;
if (ownership === undefined)
throw new Error("Owned launcher must expose process identity");
const cleanup =
launched.cleanup ?? (() => cleanupOwnedProcessGroup(ownership));
return {
...launched,
cleanup: async () => {
await launched.cleanup?.();
throw new Error("injected post-cleanup reporting failure");
const result = await cleanup();
if (failCleanupReport)
throw new Error("injected post-cleanup reporting failure");
return result;
},
};
});
onTestFinished(async () => {
failCleanupReport = false;
await provider.close();
});
const result = await provider.inspect({ path, encoding: "hex" });
if (result.ok) throw new Error("Expected compounded lifecycle failure");
expect(result.error).toMatchObject({
cleanupIncomplete: true,
diagnostics: {
reason: "injected post-cleanup reporting failure",
previous_error: { failure_kind: "output-limit" },
captured_output: { truncated: true, stderr: "" },
expect(projectAnalysisError(result.error)).toMatchObject({
code: "cleanup_incomplete",
details: {
diagnostics: {
reason: "Owned EVM worker cleanup could not be confirmed",
previous_error: {
code: "cleanup_incomplete",
details: {
diagnostics: {
reason: "injected post-cleanup reporting failure",
previous_error: { failure_kind: "output-limit" },
captured_output: { truncated: true, stderr: "" },
},
},
},
},
},
});
await access(ownedPath);
failCleanupReport = false;
await provider.close();
await expect(access(ownedPath)).rejects.toMatchObject({ code: "ENOENT" });
},
);
@@ -157,6 +157,8 @@ describe("Qwen Code profile setup", () => {
expect(await setup.clientNeedsConfigure(client, {}, command)).toBe(false);
expect(await setup.skillNeedsInstall([client.name])).toBe(false);
expect(await setup.installSkill([client.name])).toBe("unchanged");
const userFile = join(skill, "user-note.txt");
await writeFile(userFile, "caller-owned skill notes\n");
expect(
(
await readClientRegistrationStatuses(
@@ -173,7 +175,12 @@ describe("Qwen Code profile setup", () => {
expect(registered).toEqual(client);
expect((await uninstall.removeClient(client)).status).toBe("removed");
expect((await uninstall.removeSkill()).status).toBe("removed");
await expect(access(skill)).rejects.toMatchObject({ code: "ENOENT" });
await expect(access(join(skill, "SKILL.md"))).rejects.toMatchObject({
code: "ENOENT",
});
expect(await readFile(userFile, "utf8")).toBe(
"caller-owned skill notes\n",
);
if (customHome)
await expect(access(join(home, ".qwen"))).rejects.toMatchObject({
code: "ENOENT",
@@ -324,7 +324,7 @@ it("cancels a waiting request promptly while keeping later launches behind the a
await assertFirmwareCleanup(fixture.launches);
});
it("retains an uncertain workspace and lets the next operation create a new root", async () => {
it("retains an uncertain worker's workspace and blocks later launches", async () => {
const fixture = await firmwareFixture("cleanup-failure");
const first = await fixture.service.execute("inspect_firmware_regions", {
path: fixture.path,
@@ -335,18 +335,15 @@ it("retains an uncertain workspace and lets the next operation create a new root
});
const workspace = fixture.launches[0]?.cwd;
expect(workspace).toBeDefined();
if (workspace === undefined) return;
if (workspace === undefined) throw new Error("Expected retained workspace");
await access(workspace);
const acquiredLaunches = [...fixture.launches];
const next = await fixture.service.execute("inspect_firmware_regions", {
path: fixture.path,
});
expect(next).toMatchObject({ ok: false, error: { cleanupIncomplete: true } });
expect(fixture.launches).toHaveLength(2);
const nextWorkspace = fixture.launches[1]?.cwd;
expect(nextWorkspace).toBeDefined();
expect(nextWorkspace).not.toBe(workspace);
expect(fixture.launches).toEqual(acquiredLaunches);
await access(workspace);
if (nextWorkspace !== undefined) await access(nextWorkspace);
});
it("accepts another build on the verified release line and reports it", async () => {
+1
View File
@@ -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);
},
+2
View File
@@ -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: {