refactor(shutdown): coordinate worker drain handshake

This commit is contained in:
ZacharyZhang-NY committed 2026-04-24 00:44:06 -04:00
1 parent 9ee1425a71
commit 2ddc67cc3d
8 files changed
+286 -86

No files matched your search

@@ -285,4 +285,62 @@ describe("worker completion boundary", () => {
expect.objectContaining({ type: "run-finished", runId: NEXT_RUN_ID }),
);
});
it("reports shutdown-ready only after parent acknowledges run completion", async () => {
const parentPort = new EventEmitter() as EventEmitter & {
postMessage: ReturnType<typeof vi.fn>;
};
parentPort.postMessage = vi.fn();
Object.defineProperty(process, "parentPort", {
configurable: true,
value: parentPort,
});
const exitSpy = vi.spyOn(process, "exit").mockImplementation((() => undefined) as never);
vi.doMock("@main/worker/runtime", () => ({
WorkerRunner: class TestWorkerRunner {
constructor(
private readonly deps: {
post: (message: unknown) => void;
},
) {}
async run(job: RunJob): Promise<void> {
this.deps.post(runFinishedMessage(job.runId));
}
cancel(): void {}
steer(): boolean {
return false;
}
async cancelAll(): Promise<void> {}
},
}));
await import("@main/worker/entry/index");
parentPort.emit("message", { data: { type: "enqueue-run", payload: runJob(RUN_ID) } });
await Promise.resolve();
await Promise.resolve();
parentPort.emit("message", { data: { type: "shutdown" } });
await Promise.resolve();
await Promise.resolve();
expect(parentPort.postMessage).toHaveBeenCalledWith(
expect.objectContaining({ type: "run-finished", runId: RUN_ID }),
);
expect(parentPort.postMessage).not.toHaveBeenCalledWith({ type: "shutdown-ready" });
parentPort.emit("message", { data: { type: "run-completion-ack", runId: RUN_ID } });
await Promise.resolve();
await Promise.resolve();
expect(parentPort.postMessage).toHaveBeenCalledWith({ type: "shutdown-ready" });
parentPort.emit("message", { data: { type: "shutdown-exit" } });
expect(exitSpy).toHaveBeenCalledWith(0);
});
});
@@ -0,0 +1,86 @@
import { EventEmitter } from "node:events";
import { describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
forkMock: vi.fn(),
}));
vi.mock("electron", () => ({
app: { isPackaged: false },
utilityProcess: { fork: mocks.forkMock },
}));
import { createWorkerService } from "../../src/main/worker-service";
function createWorker() {
const worker = new EventEmitter() as EventEmitter & {
stdout: EventEmitter;
stderr: EventEmitter;
postMessage: ReturnType<typeof vi.fn>;
};
worker.stdout = new EventEmitter();
worker.stderr = new EventEmitter();
worker.postMessage = vi.fn();
return worker;
}
describe("worker service shutdown", () => {
it("keeps completed run outcomes when cleaning shutdown runs", async () => {
const worker = createWorker();
mocks.forkMock.mockReturnValueOnce(worker);
const finishRun = vi.fn();
const db = {
projection: {
snapshot: {
listSnapshot: vi.fn(() => ({
runs: [
{ id: "run-succeeded", status: "running", taskId: null, workspaceId: null },
{ id: "run-queued", status: "queued", taskId: null, workspaceId: null },
],
})),
},
},
core: {
runs: {
getRun: vi.fn((id: string) => ({
id,
status: id === "run-succeeded" ? "succeeded" : "queued",
})),
finishRun,
},
workspaces: { releaseWorkspaceLock: vi.fn() },
tasks: { releaseTaskLock: vi.fn() },
},
};
const service = createWorkerService({
db,
logger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
workerPath: "/tmp/worker.js",
isQuitting: () => false,
publishDomainChanged: vi.fn(),
emitEvent: vi.fn(),
wakeAgentIfPossible: vi.fn(() => null),
dispatchRunToWorker: vi.fn(),
processRunCompletion: vi.fn(),
onRunStarted: vi.fn(),
onRunCompleted: vi.fn(),
notify: vi.fn(),
budgetService: undefined,
sessionService: undefined,
getRuntimeBridge: vi.fn(() => null),
getRuntimeMessageInjector: vi.fn(() => null),
} as never);
service.createWorker();
const shutdown = service.shutdown();
worker.emit("exit", 0);
await shutdown;
expect(worker.postMessage).toHaveBeenCalledWith({ type: "shutdown" });
expect(finishRun).toHaveBeenCalledTimes(1);
expect(finishRun).toHaveBeenCalledWith(expect.objectContaining({
runId: "run-queued",
status: "interrupted",
}));
});
});