Files
Moltbot/src/cli/profile.test.ts
Marcus Castro 456bd58740 fix(paths): structurally resolve home dir to prevent Windows path bugs (#12125)
* fix(paths): structurally resolve home dir to prevent Windows path bugs

Extract resolveRawHomeDir as a private function and gate the public
resolveEffectiveHomeDir through a single path.resolve() exit point.
This makes it structurally impossible for unresolved paths (missing
drive letter on Windows) to escape the function, regardless of how
many return paths exist in the raw lookup logic.

Simplify resolveRequiredHomeDir to only resolve the process.cwd()
fallback, since resolveEffectiveHomeDir now returns resolved values.

Fix shortenMeta in tool-meta.ts: the colon-based split for file:line
patterns (e.g. file.txt:12) conflicts with Windows drive letters
(C:\...) because indexOf(":") matches the drive colon first.
shortenHomeInString already handles file:line patterns correctly via
split/join, so the colon split was both unnecessary and harmful.

Update test assertions across all affected files to use path.resolve()
in expected values and input strings so they match the now-correct
resolved output on both Unix and Windows.

Fixes #12119

* fix(changelog): add paths Windows fix entry (#12125)

---------

Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-08 20:06:29 -05:00

164 lines
5.4 KiB
TypeScript

import path from "node:path";
import { describe, expect, it } from "vitest";
import { formatCliCommand } from "./command-format.js";
import { applyCliProfileEnv, parseCliProfileArgs } from "./profile.js";
describe("parseCliProfileArgs", () => {
it("leaves gateway --dev for subcommands", () => {
const res = parseCliProfileArgs([
"node",
"openclaw",
"gateway",
"--dev",
"--allow-unconfigured",
]);
if (!res.ok) {
throw new Error(res.error);
}
expect(res.profile).toBeNull();
expect(res.argv).toEqual(["node", "openclaw", "gateway", "--dev", "--allow-unconfigured"]);
});
it("still accepts global --dev before subcommand", () => {
const res = parseCliProfileArgs(["node", "openclaw", "--dev", "gateway"]);
if (!res.ok) {
throw new Error(res.error);
}
expect(res.profile).toBe("dev");
expect(res.argv).toEqual(["node", "openclaw", "gateway"]);
});
it("parses --profile value and strips it", () => {
const res = parseCliProfileArgs(["node", "openclaw", "--profile", "work", "status"]);
if (!res.ok) {
throw new Error(res.error);
}
expect(res.profile).toBe("work");
expect(res.argv).toEqual(["node", "openclaw", "status"]);
});
it("rejects missing profile value", () => {
const res = parseCliProfileArgs(["node", "openclaw", "--profile"]);
expect(res.ok).toBe(false);
});
it("rejects combining --dev with --profile (dev first)", () => {
const res = parseCliProfileArgs(["node", "openclaw", "--dev", "--profile", "work", "status"]);
expect(res.ok).toBe(false);
});
it("rejects combining --dev with --profile (profile first)", () => {
const res = parseCliProfileArgs(["node", "openclaw", "--profile", "work", "--dev", "status"]);
expect(res.ok).toBe(false);
});
});
describe("applyCliProfileEnv", () => {
it("fills env defaults for dev profile", () => {
const env: Record<string, string | undefined> = {};
applyCliProfileEnv({
profile: "dev",
env,
homedir: () => "/home/peter",
});
const expectedStateDir = path.join(path.resolve("/home/peter"), ".openclaw-dev");
expect(env.OPENCLAW_PROFILE).toBe("dev");
expect(env.OPENCLAW_STATE_DIR).toBe(expectedStateDir);
expect(env.OPENCLAW_CONFIG_PATH).toBe(path.join(expectedStateDir, "openclaw.json"));
expect(env.OPENCLAW_GATEWAY_PORT).toBe("19001");
});
it("does not override explicit env values", () => {
const env: Record<string, string | undefined> = {
OPENCLAW_STATE_DIR: "/custom",
OPENCLAW_GATEWAY_PORT: "19099",
};
applyCliProfileEnv({
profile: "dev",
env,
homedir: () => "/home/peter",
});
expect(env.OPENCLAW_STATE_DIR).toBe("/custom");
expect(env.OPENCLAW_GATEWAY_PORT).toBe("19099");
expect(env.OPENCLAW_CONFIG_PATH).toBe(path.join("/custom", "openclaw.json"));
});
it("uses OPENCLAW_HOME when deriving profile state dir", () => {
const env: Record<string, string | undefined> = {
OPENCLAW_HOME: "/srv/openclaw-home",
HOME: "/home/other",
};
applyCliProfileEnv({
profile: "work",
env,
homedir: () => "/home/fallback",
});
const resolvedHome = path.resolve("/srv/openclaw-home");
expect(env.OPENCLAW_STATE_DIR).toBe(path.join(resolvedHome, ".openclaw-work"));
expect(env.OPENCLAW_CONFIG_PATH).toBe(
path.join(resolvedHome, ".openclaw-work", "openclaw.json"),
);
});
});
describe("formatCliCommand", () => {
it("returns command unchanged when no profile is set", () => {
expect(formatCliCommand("openclaw doctor --fix", {})).toBe("openclaw doctor --fix");
});
it("returns command unchanged when profile is default", () => {
expect(formatCliCommand("openclaw doctor --fix", { OPENCLAW_PROFILE: "default" })).toBe(
"openclaw doctor --fix",
);
});
it("returns command unchanged when profile is Default (case-insensitive)", () => {
expect(formatCliCommand("openclaw doctor --fix", { OPENCLAW_PROFILE: "Default" })).toBe(
"openclaw doctor --fix",
);
});
it("returns command unchanged when profile is invalid", () => {
expect(formatCliCommand("openclaw doctor --fix", { OPENCLAW_PROFILE: "bad profile" })).toBe(
"openclaw doctor --fix",
);
});
it("returns command unchanged when --profile is already present", () => {
expect(
formatCliCommand("openclaw --profile work doctor --fix", { OPENCLAW_PROFILE: "work" }),
).toBe("openclaw --profile work doctor --fix");
});
it("returns command unchanged when --dev is already present", () => {
expect(formatCliCommand("openclaw --dev doctor", { OPENCLAW_PROFILE: "dev" })).toBe(
"openclaw --dev doctor",
);
});
it("inserts --profile flag when profile is set", () => {
expect(formatCliCommand("openclaw doctor --fix", { OPENCLAW_PROFILE: "work" })).toBe(
"openclaw --profile work doctor --fix",
);
});
it("trims whitespace from profile", () => {
expect(formatCliCommand("openclaw doctor --fix", { OPENCLAW_PROFILE: " jbopenclaw " })).toBe(
"openclaw --profile jbopenclaw doctor --fix",
);
});
it("handles command with no args after openclaw", () => {
expect(formatCliCommand("openclaw", { OPENCLAW_PROFILE: "test" })).toBe(
"openclaw --profile test",
);
});
it("handles pnpm wrapper", () => {
expect(formatCliCommand("pnpm openclaw doctor", { OPENCLAW_PROFILE: "work" })).toBe(
"pnpm openclaw --profile work doctor",
);
});
});