mirror of https://github.com/openclaw/openclaw.git
187 lines
5.7 KiB
TypeScript
187 lines
5.7 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const progressSetLabel = vi.hoisted(() => vi.fn());
|
|
const withProgress = vi.hoisted(() =>
|
|
vi.fn(async (_opts, run) => run({ setLabel: progressSetLabel })),
|
|
);
|
|
const loadConfig = vi.hoisted(() => vi.fn());
|
|
const resolveGatewayInstallToken = vi.hoisted(() => vi.fn());
|
|
const buildGatewayInstallPlan = vi.hoisted(() => vi.fn());
|
|
const note = vi.hoisted(() => vi.fn());
|
|
const serviceIsLoaded = vi.hoisted(() => vi.fn(async () => false));
|
|
const serviceInstall = vi.hoisted(() => vi.fn(async () => {}));
|
|
const serviceRestart = vi.hoisted(() =>
|
|
vi.fn<() => Promise<{ outcome: "completed" } | { outcome: "scheduled" }>>(async () => ({
|
|
outcome: "completed",
|
|
})),
|
|
);
|
|
const ensureSystemdUserLingerInteractive = vi.hoisted(() => vi.fn(async () => {}));
|
|
const select = vi.hoisted(() => vi.fn(async () => "node"));
|
|
|
|
vi.mock("../cli/progress.js", () => ({
|
|
withProgress,
|
|
}));
|
|
|
|
vi.mock("../config/config.js", () => ({
|
|
loadConfig,
|
|
}));
|
|
|
|
vi.mock("./gateway-install-token.js", () => ({
|
|
resolveGatewayInstallToken,
|
|
}));
|
|
|
|
vi.mock("./daemon-install-helpers.js", () => ({
|
|
buildGatewayInstallPlan,
|
|
gatewayInstallErrorHint: vi.fn(() => "hint"),
|
|
}));
|
|
|
|
vi.mock("../terminal/note.js", () => ({
|
|
note,
|
|
}));
|
|
|
|
vi.mock("./configure.shared.js", () => ({
|
|
confirm: vi.fn(async () => true),
|
|
select,
|
|
}));
|
|
|
|
vi.mock("./daemon-runtime.js", () => ({
|
|
DEFAULT_GATEWAY_DAEMON_RUNTIME: "node",
|
|
GATEWAY_DAEMON_RUNTIME_OPTIONS: [{ value: "node", label: "Node" }],
|
|
}));
|
|
|
|
vi.mock("../daemon/service.js", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("../daemon/service.js")>();
|
|
return {
|
|
...actual,
|
|
resolveGatewayService: vi.fn(() => ({
|
|
isLoaded: serviceIsLoaded,
|
|
install: serviceInstall,
|
|
restart: serviceRestart,
|
|
})),
|
|
};
|
|
});
|
|
|
|
vi.mock("./onboard-helpers.js", () => ({
|
|
guardCancel: (value: unknown) => value,
|
|
}));
|
|
|
|
vi.mock("./systemd-linger.js", () => ({
|
|
ensureSystemdUserLingerInteractive,
|
|
}));
|
|
|
|
const { maybeInstallDaemon } = await import("./configure.daemon.js");
|
|
|
|
describe("maybeInstallDaemon", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
progressSetLabel.mockReset();
|
|
serviceIsLoaded.mockResolvedValue(false);
|
|
serviceInstall.mockResolvedValue(undefined);
|
|
serviceRestart.mockResolvedValue({ outcome: "completed" });
|
|
loadConfig.mockReturnValue({});
|
|
resolveGatewayInstallToken.mockResolvedValue({
|
|
token: undefined,
|
|
tokenRefConfigured: true,
|
|
warnings: [],
|
|
});
|
|
buildGatewayInstallPlan.mockResolvedValue({
|
|
programArguments: ["openclaw", "gateway", "run"],
|
|
workingDirectory: "/tmp",
|
|
environment: {},
|
|
});
|
|
});
|
|
|
|
it("does not serialize SecretRef token into service environment", async () => {
|
|
await maybeInstallDaemon({
|
|
runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() },
|
|
port: 18789,
|
|
});
|
|
|
|
expect(resolveGatewayInstallToken).toHaveBeenCalledTimes(1);
|
|
expect(buildGatewayInstallPlan).toHaveBeenCalledTimes(1);
|
|
expect("token" in buildGatewayInstallPlan.mock.calls[0][0]).toBe(false);
|
|
expect(serviceInstall).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("blocks install when token SecretRef is unresolved", async () => {
|
|
resolveGatewayInstallToken.mockResolvedValue({
|
|
token: undefined,
|
|
tokenRefConfigured: true,
|
|
unavailableReason: "gateway.auth.token SecretRef is configured but unresolved (boom).",
|
|
warnings: [],
|
|
});
|
|
|
|
await maybeInstallDaemon({
|
|
runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() },
|
|
port: 18789,
|
|
});
|
|
|
|
expect(note).toHaveBeenCalledWith(
|
|
expect.stringContaining("Gateway install blocked"),
|
|
"Gateway",
|
|
);
|
|
expect(buildGatewayInstallPlan).not.toHaveBeenCalled();
|
|
expect(serviceInstall).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("continues daemon install flow when service status probe throws", async () => {
|
|
serviceIsLoaded.mockRejectedValueOnce(
|
|
new Error("systemctl is-enabled unavailable: Failed to connect to bus"),
|
|
);
|
|
|
|
await expect(
|
|
maybeInstallDaemon({
|
|
runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() },
|
|
port: 18789,
|
|
}),
|
|
).resolves.toBeUndefined();
|
|
|
|
expect(serviceInstall).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("rethrows install probe failures that are not the known non-fatal Linux systemd cases", async () => {
|
|
serviceIsLoaded.mockRejectedValueOnce(
|
|
new Error("systemctl is-enabled unavailable: read-only file system"),
|
|
);
|
|
|
|
await expect(
|
|
maybeInstallDaemon({
|
|
runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() },
|
|
port: 18789,
|
|
}),
|
|
).rejects.toThrow("systemctl is-enabled unavailable: read-only file system");
|
|
|
|
expect(serviceInstall).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("continues the WSL2 daemon install flow when service status probe reports systemd unavailability", async () => {
|
|
serviceIsLoaded.mockRejectedValueOnce(
|
|
new Error("systemctl --user unavailable: Failed to connect to bus: No medium found"),
|
|
);
|
|
|
|
await expect(
|
|
maybeInstallDaemon({
|
|
runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() },
|
|
port: 18789,
|
|
}),
|
|
).resolves.toBeUndefined();
|
|
|
|
expect(serviceInstall).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("shows restart scheduled when a loaded service defers restart handoff", async () => {
|
|
serviceIsLoaded.mockResolvedValue(true);
|
|
select.mockResolvedValueOnce("restart");
|
|
serviceRestart.mockResolvedValueOnce({ outcome: "scheduled" });
|
|
|
|
await maybeInstallDaemon({
|
|
runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() },
|
|
port: 18789,
|
|
});
|
|
|
|
expect(serviceRestart).toHaveBeenCalledTimes(1);
|
|
expect(serviceInstall).not.toHaveBeenCalled();
|
|
expect(progressSetLabel).toHaveBeenLastCalledWith("Gateway service restart scheduled.");
|
|
});
|
|
});
|