import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { createClawdbotTools } from "./clawdbot-tools.js"; vi.mock("./tools/gateway.js", () => ({ callGatewayTool: vi.fn(async () => ({ ok: true })), })); describe("gateway tool", () => { it("schedules SIGUSR1 restart", async () => { vi.useFakeTimers(); const kill = vi.spyOn(process, "kill").mockImplementation(() => true); const previousStateDir = process.env.CLAWDBOT_STATE_DIR; const stateDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-test-")); process.env.CLAWDBOT_STATE_DIR = stateDir; try { const tool = createClawdbotTools({ config: { commands: { restart: true } }, }).find((candidate) => candidate.name === "gateway"); expect(tool).toBeDefined(); if (!tool) throw new Error("missing gateway tool"); const result = await tool.execute("call1", { action: "restart", delayMs: 0, }); expect(result.details).toMatchObject({ ok: true, pid: process.pid, signal: "SIGUSR1", delayMs: 0, }); const sentinelPath = path.join(stateDir, "restart-sentinel.json"); const raw = await fs.readFile(sentinelPath, "utf-8"); const parsed = JSON.parse(raw) as { payload?: { kind?: string; doctorHint?: string | null }; }; expect(parsed.payload?.kind).toBe("restart"); expect(parsed.payload?.doctorHint).toBe( "Run: clawdbot doctor --non-interactive", ); expect(kill).not.toHaveBeenCalled(); await vi.runAllTimersAsync(); expect(kill).toHaveBeenCalledWith(process.pid, "SIGUSR1"); } finally { kill.mockRestore(); vi.useRealTimers(); if (previousStateDir === undefined) { delete process.env.CLAWDBOT_STATE_DIR; } else { process.env.CLAWDBOT_STATE_DIR = previousStateDir; } } }); it("passes config.apply through gateway call", async () => { const { callGatewayTool } = await import("./tools/gateway.js"); const tool = createClawdbotTools({ agentSessionKey: "agent:main:whatsapp:dm:+15555550123", }).find((candidate) => candidate.name === "gateway"); expect(tool).toBeDefined(); if (!tool) throw new Error("missing gateway tool"); const raw = '{\n agents: { defaults: { workspace: "~/clawd" } }\n}\n'; await tool.execute("call2", { action: "config.apply", raw, }); expect(callGatewayTool).toHaveBeenCalledWith( "config.apply", expect.any(Object), expect.objectContaining({ raw: raw.trim(), sessionKey: "agent:main:whatsapp:dm:+15555550123", }), ); }); it("passes update.run through gateway call", async () => { const { callGatewayTool } = await import("./tools/gateway.js"); const tool = createClawdbotTools({ agentSessionKey: "agent:main:whatsapp:dm:+15555550123", }).find((candidate) => candidate.name === "gateway"); expect(tool).toBeDefined(); if (!tool) throw new Error("missing gateway tool"); await tool.execute("call3", { action: "update.run", note: "test update", }); expect(callGatewayTool).toHaveBeenCalledWith( "update.run", expect.any(Object), expect.objectContaining({ note: "test update", sessionKey: "agent:main:whatsapp:dm:+15555550123", }), ); }); });