test(refactor): dedupe cli and ios script scenarios
This commit is contained in:
@@ -72,49 +72,87 @@ afterEach(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("registerPreActionHooks", () => {
|
describe("registerPreActionHooks", () => {
|
||||||
function buildProgram() {
|
type CommandKey =
|
||||||
|
| "status"
|
||||||
|
| "doctor"
|
||||||
|
| "completion"
|
||||||
|
| "secrets"
|
||||||
|
| "update-status"
|
||||||
|
| "config-set"
|
||||||
|
| "agents"
|
||||||
|
| "configure"
|
||||||
|
| "onboard"
|
||||||
|
| "message-send";
|
||||||
|
|
||||||
|
function buildProgram(keys: readonly CommandKey[]) {
|
||||||
|
const enabled = new Set<CommandKey>(keys);
|
||||||
|
const has = (key: CommandKey) => enabled.has(key);
|
||||||
const program = new Command().name("openclaw");
|
const program = new Command().name("openclaw");
|
||||||
program.command("status").action(() => {});
|
if (has("status")) {
|
||||||
program.command("doctor").action(() => {});
|
program.command("status").action(() => {});
|
||||||
program.command("completion").action(() => {});
|
}
|
||||||
program.command("secrets").action(() => {});
|
if (has("doctor")) {
|
||||||
program
|
program.command("doctor").action(() => {});
|
||||||
.command("update")
|
}
|
||||||
.command("status")
|
if (has("completion")) {
|
||||||
.option("--json")
|
program.command("completion").action(() => {});
|
||||||
.action(() => {});
|
}
|
||||||
const config = program.command("config");
|
if (has("secrets")) {
|
||||||
config
|
program.command("secrets").action(() => {});
|
||||||
.command("set")
|
}
|
||||||
.argument("<path>")
|
if (has("update-status")) {
|
||||||
.argument("<value>")
|
program
|
||||||
.option("--json")
|
.command("update")
|
||||||
.action(() => {});
|
.command("status")
|
||||||
program.command("agents").action(() => {});
|
.option("--json")
|
||||||
program.command("configure").action(() => {});
|
.action(() => {});
|
||||||
program.command("onboard").action(() => {});
|
}
|
||||||
program
|
if (has("config-set")) {
|
||||||
.command("message")
|
const config = program.command("config");
|
||||||
.command("send")
|
config
|
||||||
.option("--json")
|
.command("set")
|
||||||
.action(() => {});
|
.argument("<path>")
|
||||||
|
.argument("<value>")
|
||||||
|
.option("--json")
|
||||||
|
.action(() => {});
|
||||||
|
}
|
||||||
|
if (has("agents")) {
|
||||||
|
program.command("agents").action(() => {});
|
||||||
|
}
|
||||||
|
if (has("configure")) {
|
||||||
|
program.command("configure").action(() => {});
|
||||||
|
}
|
||||||
|
if (has("onboard")) {
|
||||||
|
program.command("onboard").action(() => {});
|
||||||
|
}
|
||||||
|
if (has("message-send")) {
|
||||||
|
program
|
||||||
|
.command("message")
|
||||||
|
.command("send")
|
||||||
|
.option("--json")
|
||||||
|
.action(() => {});
|
||||||
|
}
|
||||||
registerPreActionHooks(program, "9.9.9-test");
|
registerPreActionHooks(program, "9.9.9-test");
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runCommand(
|
async function runCommand(
|
||||||
params: { parseArgv: string[]; processArgv?: string[] },
|
params: { parseArgv: string[]; processArgv?: string[] },
|
||||||
program = buildProgram(),
|
program: Command,
|
||||||
) {
|
) {
|
||||||
process.argv = params.processArgv ?? [...params.parseArgv];
|
process.argv = params.processArgv ?? [...params.parseArgv];
|
||||||
await program.parseAsync(params.parseArgv, { from: "user" });
|
await program.parseAsync(params.parseArgv, { from: "user" });
|
||||||
}
|
}
|
||||||
|
|
||||||
it("emits banner, resolves config, and enables verbose from --debug", async () => {
|
it("emits banner, resolves config, and enables verbose from --debug", async () => {
|
||||||
await runCommand({
|
const program = buildProgram(["status"]);
|
||||||
parseArgv: ["status"],
|
await runCommand(
|
||||||
processArgv: ["node", "openclaw", "status", "--debug"],
|
{
|
||||||
});
|
parseArgv: ["status"],
|
||||||
|
processArgv: ["node", "openclaw", "status", "--debug"],
|
||||||
|
},
|
||||||
|
program,
|
||||||
|
);
|
||||||
|
|
||||||
expect(emitCliBannerMock).toHaveBeenCalledWith("9.9.9-test");
|
expect(emitCliBannerMock).toHaveBeenCalledWith("9.9.9-test");
|
||||||
expect(setVerboseMock).toHaveBeenCalledWith(true);
|
expect(setVerboseMock).toHaveBeenCalledWith(true);
|
||||||
@@ -127,10 +165,14 @@ describe("registerPreActionHooks", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("loads plugin registry for plugin-required commands", async () => {
|
it("loads plugin registry for plugin-required commands", async () => {
|
||||||
await runCommand({
|
const program = buildProgram(["message-send"]);
|
||||||
parseArgv: ["message", "send"],
|
await runCommand(
|
||||||
processArgv: ["node", "openclaw", "message", "send"],
|
{
|
||||||
});
|
parseArgv: ["message", "send"],
|
||||||
|
processArgv: ["node", "openclaw", "message", "send"],
|
||||||
|
},
|
||||||
|
program,
|
||||||
|
);
|
||||||
|
|
||||||
expect(setVerboseMock).toHaveBeenCalledWith(false);
|
expect(setVerboseMock).toHaveBeenCalledWith(false);
|
||||||
expect(process.env.NODE_NO_WARNINGS).toBe("1");
|
expect(process.env.NODE_NO_WARNINGS).toBe("1");
|
||||||
@@ -143,7 +185,7 @@ describe("registerPreActionHooks", () => {
|
|||||||
|
|
||||||
it("loads plugin registry for configure/onboard/agents commands", async () => {
|
it("loads plugin registry for configure/onboard/agents commands", async () => {
|
||||||
const commands = ["configure", "onboard", "agents"] as const;
|
const commands = ["configure", "onboard", "agents"] as const;
|
||||||
const program = buildProgram();
|
const program = buildProgram(["configure", "onboard", "agents"]);
|
||||||
for (const command of commands) {
|
for (const command of commands) {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
await runCommand(
|
await runCommand(
|
||||||
@@ -158,7 +200,7 @@ describe("registerPreActionHooks", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("skips config guard for doctor, completion, and secrets commands", async () => {
|
it("skips config guard for doctor, completion, and secrets commands", async () => {
|
||||||
const program = buildProgram();
|
const program = buildProgram(["doctor", "completion", "secrets"]);
|
||||||
await runCommand(
|
await runCommand(
|
||||||
{
|
{
|
||||||
parseArgv: ["doctor"],
|
parseArgv: ["doctor"],
|
||||||
@@ -185,10 +227,14 @@ describe("registerPreActionHooks", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("skips preaction work when argv indicates help/version", async () => {
|
it("skips preaction work when argv indicates help/version", async () => {
|
||||||
await runCommand({
|
const program = buildProgram(["status"]);
|
||||||
parseArgv: ["status"],
|
await runCommand(
|
||||||
processArgv: ["node", "openclaw", "--version"],
|
{
|
||||||
});
|
parseArgv: ["status"],
|
||||||
|
processArgv: ["node", "openclaw", "--version"],
|
||||||
|
},
|
||||||
|
program,
|
||||||
|
);
|
||||||
|
|
||||||
expect(emitCliBannerMock).not.toHaveBeenCalled();
|
expect(emitCliBannerMock).not.toHaveBeenCalled();
|
||||||
expect(setVerboseMock).not.toHaveBeenCalled();
|
expect(setVerboseMock).not.toHaveBeenCalled();
|
||||||
@@ -197,17 +243,21 @@ describe("registerPreActionHooks", () => {
|
|||||||
|
|
||||||
it("hides banner when OPENCLAW_HIDE_BANNER is truthy", async () => {
|
it("hides banner when OPENCLAW_HIDE_BANNER is truthy", async () => {
|
||||||
process.env.OPENCLAW_HIDE_BANNER = "1";
|
process.env.OPENCLAW_HIDE_BANNER = "1";
|
||||||
await runCommand({
|
const program = buildProgram(["status"]);
|
||||||
parseArgv: ["status"],
|
await runCommand(
|
||||||
processArgv: ["node", "openclaw", "status"],
|
{
|
||||||
});
|
parseArgv: ["status"],
|
||||||
|
processArgv: ["node", "openclaw", "status"],
|
||||||
|
},
|
||||||
|
program,
|
||||||
|
);
|
||||||
|
|
||||||
expect(emitCliBannerMock).not.toHaveBeenCalled();
|
expect(emitCliBannerMock).not.toHaveBeenCalled();
|
||||||
expect(ensureConfigReadyMock).toHaveBeenCalledTimes(1);
|
expect(ensureConfigReadyMock).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("suppresses doctor stdout for any --json output command", async () => {
|
it("suppresses doctor stdout for any --json output command", async () => {
|
||||||
const program = buildProgram();
|
const program = buildProgram(["message-send", "update-status"]);
|
||||||
await runCommand(
|
await runCommand(
|
||||||
{
|
{
|
||||||
parseArgv: ["message", "send", "--json"],
|
parseArgv: ["message", "send", "--json"],
|
||||||
@@ -240,10 +290,14 @@ describe("registerPreActionHooks", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("does not treat config set --json (strict-parse alias) as json output mode", async () => {
|
it("does not treat config set --json (strict-parse alias) as json output mode", async () => {
|
||||||
await runCommand({
|
const program = buildProgram(["config-set"]);
|
||||||
parseArgv: ["config", "set", "gateway.auth.mode", "{bad", "--json"],
|
await runCommand(
|
||||||
processArgv: ["node", "openclaw", "config", "set", "gateway.auth.mode", "{bad", "--json"],
|
{
|
||||||
});
|
parseArgv: ["config", "set", "gateway.auth.mode", "{bad", "--json"],
|
||||||
|
processArgv: ["node", "openclaw", "config", "set", "gateway.auth.mode", "{bad", "--json"],
|
||||||
|
},
|
||||||
|
program,
|
||||||
|
);
|
||||||
|
|
||||||
expect(ensureConfigReadyMock).toHaveBeenCalledWith({
|
expect(ensureConfigReadyMock).toHaveBeenCalledWith({
|
||||||
runtime: runtimeMock,
|
runtime: runtimeMock,
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import path from "node:path";
|
|||||||
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
const SCRIPT = path.join(process.cwd(), "scripts", "ios-team-id.sh");
|
const SCRIPT = path.join(process.cwd(), "scripts", "ios-team-id.sh");
|
||||||
|
const BASH_BIN = process.platform === "win32" ? "bash" : "/bin/bash";
|
||||||
|
const BASE_PATH = process.env.PATH ?? "/usr/bin:/bin";
|
||||||
|
const BASE_LANG = process.env.LANG ?? "C";
|
||||||
let fixtureRoot = "";
|
let fixtureRoot = "";
|
||||||
let sharedBinDir = "";
|
let sharedBinDir = "";
|
||||||
let caseId = 0;
|
let caseId = 0;
|
||||||
@@ -25,13 +28,13 @@ function runScript(
|
|||||||
} {
|
} {
|
||||||
const binDir = path.join(homeDir, "bin");
|
const binDir = path.join(homeDir, "bin");
|
||||||
const env = {
|
const env = {
|
||||||
...process.env,
|
|
||||||
HOME: homeDir,
|
HOME: homeDir,
|
||||||
PATH: `${binDir}:${sharedBinDir}:${process.env.PATH ?? ""}`,
|
PATH: `${binDir}:${sharedBinDir}:${BASE_PATH}`,
|
||||||
|
LANG: BASE_LANG,
|
||||||
...extraEnv,
|
...extraEnv,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const stdout = execFileSync("bash", [SCRIPT], {
|
const stdout = execFileSync(BASH_BIN, [SCRIPT], {
|
||||||
env,
|
env,
|
||||||
encoding: "utf8",
|
encoding: "utf8",
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
@@ -109,19 +112,20 @@ exit 1`,
|
|||||||
return { homeDir, binDir };
|
return { homeDir, binDir };
|
||||||
}
|
}
|
||||||
|
|
||||||
it("falls back to Xcode-managed provisioning profiles when preference teams are empty", async () => {
|
it("resolves fallback and preferred team IDs from provisioning profiles", async () => {
|
||||||
const { homeDir } = await createHomeDir();
|
const { homeDir } = await createHomeDir();
|
||||||
await mkdir(path.join(homeDir, "Library", "MobileDevice", "Provisioning Profiles"), {
|
const profilesDir = path.join(homeDir, "Library", "MobileDevice", "Provisioning Profiles");
|
||||||
recursive: true,
|
await mkdir(profilesDir, { recursive: true });
|
||||||
});
|
await writeFile(path.join(profilesDir, "one.mobileprovision"), "stub1");
|
||||||
await writeFile(
|
|
||||||
path.join(homeDir, "Library", "MobileDevice", "Provisioning Profiles", "one.mobileprovision"),
|
|
||||||
"stub",
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = runScript(homeDir);
|
const fallbackResult = runScript(homeDir);
|
||||||
expect(result.ok).toBe(true);
|
expect(fallbackResult.ok).toBe(true);
|
||||||
expect(result.stdout).toBe("AAAAA11111");
|
expect(fallbackResult.stdout).toBe("AAAAA11111");
|
||||||
|
|
||||||
|
await writeFile(path.join(profilesDir, "two.mobileprovision"), "stub2");
|
||||||
|
const preferredResult = runScript(homeDir, { IOS_PREFERRED_TEAM_ID: "BBBBB22222" });
|
||||||
|
expect(preferredResult.ok).toBe(true);
|
||||||
|
expect(preferredResult.stdout).toBe("BBBBB22222");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("prints actionable guidance when Xcode account exists but no Team ID is resolvable", async () => {
|
it("prints actionable guidance when Xcode account exists but no Team ID is resolvable", async () => {
|
||||||
@@ -148,25 +152,6 @@ exit 1`,
|
|||||||
expect(result.stderr).toContain("IOS_DEVELOPMENT_TEAM");
|
expect(result.stderr).toContain("IOS_DEVELOPMENT_TEAM");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("honors IOS_PREFERRED_TEAM_ID when multiple profile teams are available", async () => {
|
|
||||||
const { homeDir } = await createHomeDir();
|
|
||||||
await mkdir(path.join(homeDir, "Library", "MobileDevice", "Provisioning Profiles"), {
|
|
||||||
recursive: true,
|
|
||||||
});
|
|
||||||
await writeFile(
|
|
||||||
path.join(homeDir, "Library", "MobileDevice", "Provisioning Profiles", "one.mobileprovision"),
|
|
||||||
"stub1",
|
|
||||||
);
|
|
||||||
await writeFile(
|
|
||||||
path.join(homeDir, "Library", "MobileDevice", "Provisioning Profiles", "two.mobileprovision"),
|
|
||||||
"stub2",
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = runScript(homeDir, { IOS_PREFERRED_TEAM_ID: "BBBBB22222" });
|
|
||||||
expect(result.ok).toBe(true);
|
|
||||||
expect(result.stdout).toBe("BBBBB22222");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("matches preferred team IDs even when parser output uses CRLF line endings", async () => {
|
it("matches preferred team IDs even when parser output uses CRLF line endings", async () => {
|
||||||
const { homeDir, binDir } = await createHomeDir();
|
const { homeDir, binDir } = await createHomeDir();
|
||||||
await writeExecutable(
|
await writeExecutable(
|
||||||
|
|||||||
Reference in New Issue
Block a user