Recover desktop startup from stale daemon locks
Refs #1950 Refs #1904 Refs #769
This commit is contained in:
@@ -16,6 +16,11 @@ const mocks = vi.hoisted(() => ({
|
||||
},
|
||||
runExternalCliJsonCommand: vi.fn(),
|
||||
runExternalCliTextCommand: vi.fn(),
|
||||
createNodeEntrypointInvocation: vi.fn(() => ({
|
||||
command: "node",
|
||||
args: [],
|
||||
env: {},
|
||||
})),
|
||||
spawnProcess: vi.fn(),
|
||||
logInfo: vi.fn(),
|
||||
logError: vi.fn(),
|
||||
@@ -59,11 +64,7 @@ vi.mock("../settings/desktop-settings-electron.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("./runtime-paths.js", () => ({
|
||||
createNodeEntrypointInvocation: vi.fn(() => ({
|
||||
command: "node",
|
||||
args: [],
|
||||
env: {},
|
||||
})),
|
||||
createNodeEntrypointInvocation: mocks.createNodeEntrypointInvocation,
|
||||
resolveDaemonRunnerEntrypoint: vi.fn(() => ({
|
||||
entryPath: "/tmp/daemon.js",
|
||||
execArgv: [],
|
||||
@@ -112,6 +113,8 @@ describe("daemon-manager commands", () => {
|
||||
mocks.settings = DEFAULT_DESKTOP_SETTINGS;
|
||||
mocks.runExternalCliJsonCommand.mockReset();
|
||||
mocks.runExternalCliTextCommand.mockReset();
|
||||
mocks.createNodeEntrypointInvocation.mockReset();
|
||||
mocks.createNodeEntrypointInvocation.mockReturnValue({ command: "node", args: [], env: {} });
|
||||
mocks.spawnProcess.mockReset();
|
||||
mocks.logInfo.mockReset();
|
||||
mocks.logError.mockReset();
|
||||
@@ -439,6 +442,9 @@ describe("daemon-manager commands", () => {
|
||||
expect(message).toContain("Daemon failed to start: exit code 1");
|
||||
expect(recentLogsLabel?.split(/[\\/]/).at(-1)).toBe("daemon.log");
|
||||
expect(message).toContain("recent daemon failure");
|
||||
expect(mocks.createNodeEntrypointInvocation).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ args: [] }),
|
||||
);
|
||||
expect(mocks.spawnProcess).toHaveBeenCalledWith(
|
||||
"node",
|
||||
[],
|
||||
@@ -450,6 +456,47 @@ describe("daemon-manager commands", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("passes stale lock reclaim only after a live desktop daemon is confirmed unresponsive", async () => {
|
||||
mocks.runExternalCliJsonCommand.mockResolvedValue({
|
||||
localDaemon: "unresponsive",
|
||||
connectedDaemon: "unreachable",
|
||||
serverId: "",
|
||||
pid: 7675,
|
||||
listen: "127.0.0.1:6767",
|
||||
desktopManaged: true,
|
||||
});
|
||||
mocks.spawnProcess.mockImplementation(() => {
|
||||
const child = createMockChildProcess();
|
||||
scheduleFailedStartup(child);
|
||||
return child;
|
||||
});
|
||||
|
||||
await expect(createDaemonCommandHandlers().start_desktop_daemon()).rejects.toThrow(
|
||||
"Daemon failed to start: exit code 1",
|
||||
);
|
||||
|
||||
expect(mocks.createNodeEntrypointInvocation).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ args: ["--reclaim-stale-pid-lock"] }),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not pass stale lock reclaim when the status command fails", async () => {
|
||||
mocks.runExternalCliJsonCommand.mockRejectedValue(new Error("status command failed"));
|
||||
mocks.spawnProcess.mockImplementation(() => {
|
||||
const child = createMockChildProcess();
|
||||
scheduleFailedStartup(child);
|
||||
return child;
|
||||
});
|
||||
|
||||
await expect(createDaemonCommandHandlers().start_desktop_daemon()).rejects.toThrow(
|
||||
"Daemon failed to start: exit code 1",
|
||||
);
|
||||
|
||||
expect(mocks.createNodeEntrypointInvocation).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ args: [] }),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns the Electron main-process log tail from electron-log", () => {
|
||||
writeFileSync(
|
||||
mocks.appLogPath,
|
||||
|
||||
@@ -385,10 +385,12 @@ async function startDaemon(): Promise<DesktopDaemonStatus> {
|
||||
}
|
||||
|
||||
const daemonRunner = resolveDaemonRunnerEntrypoint();
|
||||
const reclaimStalePidLock =
|
||||
current.status === "errored" && current.desktopManaged && current.error === null;
|
||||
const invocation = createNodeEntrypointInvocation({
|
||||
entrypoint: daemonRunner,
|
||||
argvMode: "node-script",
|
||||
args: [],
|
||||
args: reclaimStalePidLock ? ["--reclaim-stale-pid-lock"] : [],
|
||||
baseEnv: process.env,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user