test: tighten daemon lifecycle assertions

This commit is contained in:
Peter Steinberger
2026-05-11 03:42:31 +01:00
parent 00bb0dde4d
commit 4dac591fcf

View File

@@ -55,6 +55,29 @@ const loadConfig = vi.hoisted(() => vi.fn(() => ({})));
const recoverInstalledLaunchAgent = vi.hoisted(() => vi.fn());
const repairLoadedGatewayServiceForStart = vi.hoisted(() => vi.fn());
function requireMockCallArg(
mockFn: { mock: { calls: unknown[][] } },
label: string,
index = 0,
): Record<string, unknown> {
const arg = mockFn.mock.calls[index]?.[0] as Record<string, unknown> | undefined;
if (!arg) {
throw new Error(`expected ${label} call #${index + 1}`);
}
return arg;
}
async function expectRestartError(
promise: Promise<unknown>,
): Promise<Error & { hints?: string[] }> {
try {
await promise;
} catch (error) {
return error as Error & { hints?: string[] };
}
throw new Error("expected restart to fail");
}
vi.mock("../../config/config.js", () => ({
getRuntimeConfig: () => loadConfig(),
loadConfig: () => loadConfig(),
@@ -329,18 +352,22 @@ describe("runDaemonRestart health checks", () => {
await runDaemonStart({ json: true });
expect(repairLoadedGatewayServiceForStart).toHaveBeenCalledWith(
expect.objectContaining({
service,
json: true,
state: expect.objectContaining({
command: expect.objectContaining({
environment: { OPENCLAW_SERVICE_VERSION: "2026.4.24" },
}),
}),
issues: [expect.objectContaining({ code: "version-mismatch" })],
}),
);
const repairParams = requireMockCallArg(
repairLoadedGatewayServiceForStart,
"repairLoadedGatewayServiceForStart",
) as {
service?: unknown;
json?: unknown;
state?: { command?: { environment?: unknown } };
issues?: Array<{ code?: unknown }>;
};
expect(repairParams.service).toBe(service);
expect(repairParams.json).toBe(true);
expect(repairParams.state?.command?.environment).toEqual({
OPENCLAW_SERVICE_VERSION: "2026.4.24",
});
expect(repairParams.issues).toHaveLength(1);
expect(repairParams.issues?.[0]?.code).toBe("version-mismatch");
});
it("kills stale gateway pids and retries restart", async () => {
@@ -398,13 +425,12 @@ describe("runDaemonRestart health checks", () => {
};
waitForGatewayHealthyRestart.mockResolvedValue(unhealthy);
await expect(runDaemonRestart({ json: true })).rejects.toMatchObject({
message: "Gateway restart timed out after 60s waiting for health checks.",
hints: [
formatCliCommand("openclaw gateway status --deep"),
formatCliCommand("openclaw doctor"),
],
});
const error = await expectRestartError(runDaemonRestart({ json: true }));
expect(error.message).toBe("Gateway restart timed out after 60s waiting for health checks.");
expect(error.hints).toEqual([
formatCliCommand("openclaw gateway status --deep"),
formatCliCommand("openclaw doctor"),
]);
expect(terminateStaleGatewayPids).not.toHaveBeenCalled();
expect(renderRestartDiagnostics).toHaveBeenCalledTimes(1);
});
@@ -420,14 +446,19 @@ describe("runDaemonRestart health checks", () => {
await runDaemonRestart({ json: true });
expect(waitForGatewayHealthyRestart).toHaveBeenCalledWith(
expect.objectContaining({
attempts: 360,
delayMs: 500,
includeUnknownListenersAsStale: true,
port: 18789,
}),
);
const waitParams = requireMockCallArg(
waitForGatewayHealthyRestart,
"waitForGatewayHealthyRestart",
) as {
attempts?: unknown;
delayMs?: unknown;
includeUnknownListenersAsStale?: unknown;
port?: unknown;
};
expect(waitParams.attempts).toBe(360);
expect(waitParams.delayMs).toBe(500);
expect(waitParams.includeUnknownListenersAsStale).toBe(true);
expect(waitParams.port).toBe(18789);
});
it("fails restart with a stopped-free message when the waiter exits early", async () => {
@@ -442,14 +473,14 @@ describe("runDaemonRestart health checks", () => {
};
waitForGatewayHealthyRestart.mockResolvedValue(unhealthy);
await expect(runDaemonRestart({ json: true })).rejects.toMatchObject({
message:
"Gateway restart failed after 13s: service stayed stopped and health checks never came up.",
hints: [
formatCliCommand("openclaw gateway status --deep"),
formatCliCommand("openclaw doctor"),
],
});
const error = await expectRestartError(runDaemonRestart({ json: true }));
expect(error.message).toBe(
"Gateway restart failed after 13s: service stayed stopped and health checks never came up.",
);
expect(error.hints).toEqual([
formatCliCommand("openclaw gateway status --deep"),
formatCliCommand("openclaw doctor"),
]);
expect(terminateStaleGatewayPids).not.toHaveBeenCalled();
expect(renderRestartDiagnostics).toHaveBeenCalledTimes(1);
});
@@ -472,12 +503,12 @@ describe("runDaemonRestart health checks", () => {
await runDaemonStop({ json: true, disable: true });
expect(runServiceStop).toHaveBeenCalledWith(
expect.objectContaining({
opts: { json: true, disable: true },
stopWhenNotLoaded: true,
}),
);
const stopParams = requireMockCallArg(runServiceStop, "runServiceStop") as {
opts?: unknown;
stopWhenNotLoaded?: unknown;
};
expect(stopParams.opts).toEqual({ json: true, disable: true });
expect(stopParams.stopWhenNotLoaded).toBe(true);
});
it("skips gateway port resolution on stop when the service manager handles the stop", async () => {