From 6b2f40652f17114adf9406f3f1e835489d604838 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 16 Feb 2026 01:00:25 +0000 Subject: [PATCH] perf(test): consolidate daemon test entrypoints --- src/daemon/arg-split.test.ts | 36 -------- src/daemon/paths.test.ts | 35 -------- src/daemon/service-env.test.ts | 33 +++++++ src/daemon/systemd-availability.test.ts | 35 -------- src/daemon/systemd-unit.test.ts | 37 -------- src/daemon/systemd.test.ts | 112 +++++++++++++++++++++++- 6 files changed, 143 insertions(+), 145 deletions(-) delete mode 100644 src/daemon/arg-split.test.ts delete mode 100644 src/daemon/paths.test.ts delete mode 100644 src/daemon/systemd-availability.test.ts delete mode 100644 src/daemon/systemd-unit.test.ts diff --git a/src/daemon/arg-split.test.ts b/src/daemon/arg-split.test.ts deleted file mode 100644 index f9b8c89448e..00000000000 --- a/src/daemon/arg-split.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { splitArgsPreservingQuotes } from "./arg-split.js"; - -describe("splitArgsPreservingQuotes", () => { - it("splits on whitespace outside quotes", () => { - expect(splitArgsPreservingQuotes('/usr/bin/openclaw gateway start --name "My Bot"')).toEqual([ - "/usr/bin/openclaw", - "gateway", - "start", - "--name", - "My Bot", - ]); - }); - - it("supports systemd-style backslash escaping", () => { - expect( - splitArgsPreservingQuotes('openclaw --name "My \\"Bot\\"" --foo bar', { - escapeMode: "backslash", - }), - ).toEqual(["openclaw", "--name", 'My "Bot"', "--foo", "bar"]); - }); - - it("supports schtasks-style escaped quotes while preserving other backslashes", () => { - expect( - splitArgsPreservingQuotes('openclaw --path "C:\\\\Program Files\\\\OpenClaw"', { - escapeMode: "backslash-quote-only", - }), - ).toEqual(["openclaw", "--path", "C:\\\\Program Files\\\\OpenClaw"]); - - expect( - splitArgsPreservingQuotes('openclaw --label "My \\"Quoted\\" Name"', { - escapeMode: "backslash-quote-only", - }), - ).toEqual(["openclaw", "--label", 'My "Quoted" Name']); - }); -}); diff --git a/src/daemon/paths.test.ts b/src/daemon/paths.test.ts deleted file mode 100644 index 5bcdb22cf69..00000000000 --- a/src/daemon/paths.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import path from "node:path"; -import { describe, expect, it } from "vitest"; -import { resolveGatewayStateDir } from "./paths.js"; - -describe("resolveGatewayStateDir", () => { - it("uses the default state dir when no overrides are set", () => { - const env = { HOME: "/Users/test" }; - expect(resolveGatewayStateDir(env)).toBe(path.join("/Users/test", ".openclaw")); - }); - - it("appends the profile suffix when set", () => { - const env = { HOME: "/Users/test", OPENCLAW_PROFILE: "rescue" }; - expect(resolveGatewayStateDir(env)).toBe(path.join("/Users/test", ".openclaw-rescue")); - }); - - it("treats default profiles as the base state dir", () => { - const env = { HOME: "/Users/test", OPENCLAW_PROFILE: "Default" }; - expect(resolveGatewayStateDir(env)).toBe(path.join("/Users/test", ".openclaw")); - }); - - it("uses OPENCLAW_STATE_DIR when provided", () => { - const env = { HOME: "/Users/test", OPENCLAW_STATE_DIR: "/var/lib/openclaw" }; - expect(resolveGatewayStateDir(env)).toBe(path.resolve("/var/lib/openclaw")); - }); - - it("expands ~ in OPENCLAW_STATE_DIR", () => { - const env = { HOME: "/Users/test", OPENCLAW_STATE_DIR: "~/openclaw-state" }; - expect(resolveGatewayStateDir(env)).toBe(path.resolve("/Users/test/openclaw-state")); - }); - - it("preserves Windows absolute paths without HOME", () => { - const env = { OPENCLAW_STATE_DIR: "C:\\State\\openclaw" }; - expect(resolveGatewayStateDir(env)).toBe("C:\\State\\openclaw"); - }); -}); diff --git a/src/daemon/service-env.test.ts b/src/daemon/service-env.test.ts index 7ead6b2ec66..6a3b6a9399f 100644 --- a/src/daemon/service-env.test.ts +++ b/src/daemon/service-env.test.ts @@ -1,5 +1,6 @@ import path from "node:path"; import { describe, expect, it } from "vitest"; +import { resolveGatewayStateDir } from "./paths.js"; import { buildMinimalServicePath, buildNodeServiceEnvironment, @@ -254,3 +255,35 @@ describe("buildNodeServiceEnvironment", () => { expect(env.HOME).toBe("/home/user"); }); }); + +describe("resolveGatewayStateDir", () => { + it("uses the default state dir when no overrides are set", () => { + const env = { HOME: "/Users/test" }; + expect(resolveGatewayStateDir(env)).toBe(path.join("/Users/test", ".openclaw")); + }); + + it("appends the profile suffix when set", () => { + const env = { HOME: "/Users/test", OPENCLAW_PROFILE: "rescue" }; + expect(resolveGatewayStateDir(env)).toBe(path.join("/Users/test", ".openclaw-rescue")); + }); + + it("treats default profiles as the base state dir", () => { + const env = { HOME: "/Users/test", OPENCLAW_PROFILE: "Default" }; + expect(resolveGatewayStateDir(env)).toBe(path.join("/Users/test", ".openclaw")); + }); + + it("uses OPENCLAW_STATE_DIR when provided", () => { + const env = { HOME: "/Users/test", OPENCLAW_STATE_DIR: "/var/lib/openclaw" }; + expect(resolveGatewayStateDir(env)).toBe(path.resolve("/var/lib/openclaw")); + }); + + it("expands ~ in OPENCLAW_STATE_DIR", () => { + const env = { HOME: "/Users/test", OPENCLAW_STATE_DIR: "~/openclaw-state" }; + expect(resolveGatewayStateDir(env)).toBe(path.resolve("/Users/test/openclaw-state")); + }); + + it("preserves Windows absolute paths without HOME", () => { + const env = { OPENCLAW_STATE_DIR: "C:\\State\\openclaw" }; + expect(resolveGatewayStateDir(env)).toBe("C:\\State\\openclaw"); + }); +}); diff --git a/src/daemon/systemd-availability.test.ts b/src/daemon/systemd-availability.test.ts deleted file mode 100644 index 4897084ce52..00000000000 --- a/src/daemon/systemd-availability.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; - -const execFileMock = vi.hoisted(() => vi.fn()); - -vi.mock("node:child_process", () => ({ - execFile: execFileMock, -})); - -import { isSystemdUserServiceAvailable } from "./systemd.js"; - -describe("systemd availability", () => { - beforeEach(() => { - execFileMock.mockReset(); - }); - - it("returns true when systemctl --user succeeds", async () => { - execFileMock.mockImplementation((_cmd, _args, _opts, cb) => { - cb(null, "", ""); - }); - await expect(isSystemdUserServiceAvailable()).resolves.toBe(true); - }); - - it("returns false when systemd user bus is unavailable", async () => { - execFileMock.mockImplementation((_cmd, _args, _opts, cb) => { - const err = new Error("Failed to connect to bus") as Error & { - stderr?: string; - code?: number; - }; - err.stderr = "Failed to connect to bus"; - err.code = 1; - cb(err, "", ""); - }); - await expect(isSystemdUserServiceAvailable()).resolves.toBe(false); - }); -}); diff --git a/src/daemon/systemd-unit.test.ts b/src/daemon/systemd-unit.test.ts deleted file mode 100644 index c671763409f..00000000000 --- a/src/daemon/systemd-unit.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { parseSystemdExecStart } from "./systemd-unit.js"; - -describe("parseSystemdExecStart", () => { - it("splits on whitespace outside quotes", () => { - const execStart = "/usr/bin/openclaw gateway start --foo bar"; - expect(parseSystemdExecStart(execStart)).toEqual([ - "/usr/bin/openclaw", - "gateway", - "start", - "--foo", - "bar", - ]); - }); - - it("preserves quoted arguments", () => { - const execStart = '/usr/bin/openclaw gateway start --name "My Bot"'; - expect(parseSystemdExecStart(execStart)).toEqual([ - "/usr/bin/openclaw", - "gateway", - "start", - "--name", - "My Bot", - ]); - }); - - it("parses path arguments", () => { - const execStart = "/usr/bin/openclaw gateway start --path /tmp/openclaw"; - expect(parseSystemdExecStart(execStart)).toEqual([ - "/usr/bin/openclaw", - "gateway", - "start", - "--path", - "/tmp/openclaw", - ]); - }); -}); diff --git a/src/daemon/systemd.test.ts b/src/daemon/systemd.test.ts index 692a445e5a9..a5be470af47 100644 --- a/src/daemon/systemd.test.ts +++ b/src/daemon/systemd.test.ts @@ -1,5 +1,44 @@ -import { describe, expect, it } from "vitest"; -import { parseSystemdShow, resolveSystemdUserUnitPath } from "./systemd.js"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const execFileMock = vi.hoisted(() => vi.fn()); + +vi.mock("node:child_process", () => ({ + execFile: execFileMock, +})); + +import { splitArgsPreservingQuotes } from "./arg-split.js"; +import { parseSystemdExecStart } from "./systemd-unit.js"; +import { + isSystemdUserServiceAvailable, + parseSystemdShow, + resolveSystemdUserUnitPath, +} from "./systemd.js"; + +describe("systemd availability", () => { + beforeEach(() => { + execFileMock.mockReset(); + }); + + it("returns true when systemctl --user succeeds", async () => { + execFileMock.mockImplementation((_cmd, _args, _opts, cb) => { + cb(null, "", ""); + }); + await expect(isSystemdUserServiceAvailable()).resolves.toBe(true); + }); + + it("returns false when systemd user bus is unavailable", async () => { + execFileMock.mockImplementation((_cmd, _args, _opts, cb) => { + const err = new Error("Failed to connect to bus") as Error & { + stderr?: string; + code?: number; + }; + err.stderr = "Failed to connect to bus"; + err.code = 1; + cb(err, "", ""); + }); + await expect(isSystemdUserServiceAvailable()).resolves.toBe(false); + }); +}); describe("systemd runtime parsing", () => { it("parses active state details", () => { @@ -93,3 +132,72 @@ describe("resolveSystemdUserUnitPath", () => { ); }); }); + +describe("splitArgsPreservingQuotes", () => { + it("splits on whitespace outside quotes", () => { + expect(splitArgsPreservingQuotes('/usr/bin/openclaw gateway start --name "My Bot"')).toEqual([ + "/usr/bin/openclaw", + "gateway", + "start", + "--name", + "My Bot", + ]); + }); + + it("supports systemd-style backslash escaping", () => { + expect( + splitArgsPreservingQuotes('openclaw --name "My \\"Bot\\"" --foo bar', { + escapeMode: "backslash", + }), + ).toEqual(["openclaw", "--name", 'My "Bot"', "--foo", "bar"]); + }); + + it("supports schtasks-style escaped quotes while preserving other backslashes", () => { + expect( + splitArgsPreservingQuotes('openclaw --path "C:\\\\Program Files\\\\OpenClaw"', { + escapeMode: "backslash-quote-only", + }), + ).toEqual(["openclaw", "--path", "C:\\\\Program Files\\\\OpenClaw"]); + + expect( + splitArgsPreservingQuotes('openclaw --label "My \\"Quoted\\" Name"', { + escapeMode: "backslash-quote-only", + }), + ).toEqual(["openclaw", "--label", 'My "Quoted" Name']); + }); +}); + +describe("parseSystemdExecStart", () => { + it("splits on whitespace outside quotes", () => { + const execStart = "/usr/bin/openclaw gateway start --foo bar"; + expect(parseSystemdExecStart(execStart)).toEqual([ + "/usr/bin/openclaw", + "gateway", + "start", + "--foo", + "bar", + ]); + }); + + it("preserves quoted arguments", () => { + const execStart = '/usr/bin/openclaw gateway start --name "My Bot"'; + expect(parseSystemdExecStart(execStart)).toEqual([ + "/usr/bin/openclaw", + "gateway", + "start", + "--name", + "My Bot", + ]); + }); + + it("parses path arguments", () => { + const execStart = "/usr/bin/openclaw gateway start --path /tmp/openclaw"; + expect(parseSystemdExecStart(execStart)).toEqual([ + "/usr/bin/openclaw", + "gateway", + "start", + "--path", + "/tmp/openclaw", + ]); + }); +});