Fix built-in daemon management toggle

This commit is contained in:
Mohamed Boudra
2026-05-03 22:02:40 +07:00
parent f8d4758e6b
commit f0d96f8e5a
7 changed files with 262 additions and 15 deletions
@@ -0,0 +1,115 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { DEFAULT_DESKTOP_SETTINGS } from "../settings/desktop-settings";
import { createDaemonCommandHandlers } from "./daemon-manager";
const mocks = vi.hoisted(() => ({
settings: {
releaseChannel: "stable",
daemon: {
manageBuiltInDaemon: true,
keepRunningAfterQuit: true,
},
},
runCliJsonCommand: vi.fn(),
runCliTextCommand: vi.fn(),
spawnProcess: vi.fn(),
}));
vi.mock("electron", () => ({
app: {
getPath: vi.fn(() => "/tmp/paseo-user-data"),
getVersion: vi.fn(() => "1.2.3"),
isPackaged: false,
},
ipcMain: { handle: vi.fn() },
powerMonitor: { getSystemIdleTime: vi.fn(() => 0) },
}));
vi.mock("electron-log/main", () => ({
default: { info: vi.fn(), error: vi.fn() },
}));
vi.mock("@getpaseo/server", () => ({
resolvePaseoHome: vi.fn(() => "/tmp/paseo-home"),
spawnProcess: mocks.spawnProcess,
}));
vi.mock("../settings/desktop-settings-electron.js", () => ({
getDesktopSettingsStore: () => ({
get: async () => mocks.settings,
patch: vi.fn(),
migrateLegacyRendererSettings: vi.fn(),
}),
}));
vi.mock("./runtime-paths.js", () => ({
createNodeEntrypointInvocation: vi.fn(() => ({
command: "node",
args: [],
env: {},
})),
resolveDaemonRunnerEntrypoint: vi.fn(() => ({
entryPath: "/tmp/daemon.js",
execArgv: [],
})),
runCliJsonCommand: mocks.runCliJsonCommand,
runCliTextCommand: mocks.runCliTextCommand,
}));
function desktopSettingsWithManagement(enabled: boolean) {
return {
...DEFAULT_DESKTOP_SETTINGS,
daemon: {
...DEFAULT_DESKTOP_SETTINGS.daemon,
manageBuiltInDaemon: enabled,
},
};
}
describe("daemon-manager commands", () => {
beforeEach(() => {
mocks.settings = DEFAULT_DESKTOP_SETTINGS;
mocks.runCliJsonCommand.mockReset();
mocks.runCliTextCommand.mockReset();
mocks.spawnProcess.mockReset();
});
it("refuses start and restart while built-in daemon management is disabled", async () => {
mocks.settings = desktopSettingsWithManagement(false);
const handlers = createDaemonCommandHandlers();
await expect(handlers.start_desktop_daemon()).rejects.toThrow(
"Built-in daemon management is disabled.",
);
await expect(handlers.restart_desktop_daemon()).rejects.toThrow(
"Built-in daemon management is disabled.",
);
expect(mocks.runCliJsonCommand).not.toHaveBeenCalled();
expect(mocks.spawnProcess).not.toHaveBeenCalled();
});
it("keeps stop callable while built-in daemon management is disabled", async () => {
mocks.settings = desktopSettingsWithManagement(false);
mocks.runCliJsonCommand.mockResolvedValue({
localDaemon: "stopped",
serverId: "",
});
const handlers = createDaemonCommandHandlers();
await expect(handlers.stop_desktop_daemon()).resolves.toEqual({
serverId: "",
status: "stopped",
listen: null,
hostname: null,
pid: null,
home: "/tmp/paseo-home",
version: null,
desktopManaged: false,
error: null,
});
expect(mocks.runCliJsonCommand).toHaveBeenCalledWith(["daemon", "status", "--json"]);
});
});
@@ -38,6 +38,7 @@ import {
createDesktopSettingsCommandHandlers,
type DesktopCommandHandler,
} from "../settings/desktop-settings-commands.js";
import type { DesktopSettings } from "../settings/desktop-settings.js";
import { getDesktopSettingsStore } from "../settings/desktop-settings-electron.js";
import { isRunningUnderARM64Translation } from "../system/arm64-translation.js";
@@ -285,6 +286,12 @@ function shouldRestartForVersion(current: DesktopDaemonStatus): boolean {
return Boolean(appVersion && daemonVersion && appVersion !== daemonVersion);
}
function assertBuiltInDaemonManagementEnabled(settings: DesktopSettings): void {
if (!settings.daemon.manageBuiltInDaemon) {
throw new Error("Built-in daemon management is disabled.");
}
}
function buildStartupFailureError(
result: { code: number | null; signal: string | null; error?: Error },
stdout: string,
@@ -322,6 +329,8 @@ async function pollForRunningDaemon(): Promise<DesktopDaemonStatus> {
}
async function startDaemon(): Promise<DesktopDaemonStatus> {
assertBuiltInDaemonManagementEnabled(await getDesktopSettingsStore().get());
const current = await resolveDesktopDaemonStatus();
logDesktopDaemonLifecycle("initial status check before start", {
status: current.status,
@@ -457,6 +466,7 @@ export async function stopDesktopDaemon(): Promise<DesktopDaemonStatus> {
}
async function restartDaemon(): Promise<DesktopDaemonStatus> {
assertBuiltInDaemonManagementEnabled(await getDesktopSettingsStore().get());
await stopDesktopDaemon();
return startDaemon();
}