mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(test): guard discord acp smoke cli args
This commit is contained in:
@@ -136,6 +136,26 @@ const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
||||
const DEFAULT_OPENCLAW_CLI_TIMEOUT_MS = 60_000;
|
||||
const DISCORD_RESPONSE_BODY_MAX_BYTES = 1024 * 1024;
|
||||
const WEBHOOK_CLEANUP_TIMEOUT_MS = 10_000;
|
||||
const BOOLEAN_OPTIONS = new Set(["--help", "-h", "--json"]);
|
||||
const VALUE_OPTIONS = new Set([
|
||||
"--channel",
|
||||
"--driver",
|
||||
"--token",
|
||||
"--token-prefix",
|
||||
"--bot-token",
|
||||
"--bot-token-prefix",
|
||||
"--agent",
|
||||
"--mention",
|
||||
"--instruction",
|
||||
"--timeout-ms",
|
||||
"--poll-ms",
|
||||
"--state-dir",
|
||||
"--openclaw-bin",
|
||||
]);
|
||||
|
||||
class CliArgumentError extends Error {
|
||||
override name = "CliArgumentError";
|
||||
}
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
@@ -237,21 +257,48 @@ function resolveStateDir(): string {
|
||||
return path.join(home, ".openclaw");
|
||||
}
|
||||
|
||||
function resolveArg(flag: string): string | undefined {
|
||||
const argv = process.argv.slice(2);
|
||||
function resolveArg(flag: string, argv: string[]): string | undefined {
|
||||
const eq = argv.find((entry) => entry.startsWith(`${flag}=`));
|
||||
if (eq) {
|
||||
return eq.slice(flag.length + 1);
|
||||
const value = eq.slice(flag.length + 1);
|
||||
if (!value) {
|
||||
throw new CliArgumentError(`${flag} requires a value`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
const idx = argv.indexOf(flag);
|
||||
if (idx >= 0 && idx + 1 < argv.length) {
|
||||
return argv[idx + 1];
|
||||
if (idx < 0) {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
const value = argv[idx + 1];
|
||||
if (!value || value.startsWith("--")) {
|
||||
throw new CliArgumentError(`${flag} requires a value`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function hasFlag(flag: string): boolean {
|
||||
return process.argv.slice(2).includes(flag);
|
||||
function hasFlag(flag: string, argv = process.argv.slice(2)): boolean {
|
||||
return argv.includes(flag);
|
||||
}
|
||||
|
||||
function validateCliArgs(argv: string[]): void {
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const arg = argv[index] ?? "";
|
||||
if (BOOLEAN_OPTIONS.has(arg)) {
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("--") && arg.includes("=")) {
|
||||
const [flag] = arg.split("=", 1);
|
||||
if (VALUE_OPTIONS.has(flag)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (VALUE_OPTIONS.has(arg)) {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
throw new CliArgumentError(`Unknown argument: ${arg}`);
|
||||
}
|
||||
}
|
||||
|
||||
function parseDriverMode(raw: string): DriverMode {
|
||||
@@ -314,43 +361,54 @@ function usage(): string {
|
||||
);
|
||||
}
|
||||
|
||||
function parseArgs(): Args {
|
||||
const channelId = resolveArg("--channel") || process.env.OPENCLAW_DISCORD_SMOKE_CHANNEL_ID || "";
|
||||
function parseArgs(argv = process.argv.slice(2)): Args {
|
||||
validateCliArgs(argv);
|
||||
const channelId =
|
||||
resolveArg("--channel", argv) || process.env.OPENCLAW_DISCORD_SMOKE_CHANNEL_ID || "";
|
||||
const driverModeRaw =
|
||||
resolveArg("--driver") || process.env.OPENCLAW_DISCORD_SMOKE_DRIVER || "token";
|
||||
resolveArg("--driver", argv) || process.env.OPENCLAW_DISCORD_SMOKE_DRIVER || "token";
|
||||
const driverMode = parseDriverMode(driverModeRaw);
|
||||
const driverToken =
|
||||
resolveArg("--token") || process.env.OPENCLAW_DISCORD_SMOKE_DRIVER_TOKEN || "";
|
||||
resolveArg("--token", argv) || process.env.OPENCLAW_DISCORD_SMOKE_DRIVER_TOKEN || "";
|
||||
const driverTokenPrefix =
|
||||
resolveArg("--token-prefix") || process.env.OPENCLAW_DISCORD_SMOKE_DRIVER_TOKEN_PREFIX || "Bot";
|
||||
resolveArg("--token-prefix", argv) ||
|
||||
process.env.OPENCLAW_DISCORD_SMOKE_DRIVER_TOKEN_PREFIX ||
|
||||
"Bot";
|
||||
const botToken =
|
||||
resolveArg("--bot-token") ||
|
||||
resolveArg("--bot-token", argv) ||
|
||||
process.env.OPENCLAW_DISCORD_SMOKE_BOT_TOKEN ||
|
||||
process.env.DISCORD_BOT_TOKEN ||
|
||||
"";
|
||||
const botTokenPrefix =
|
||||
resolveArg("--bot-token-prefix") ||
|
||||
resolveArg("--bot-token-prefix", argv) ||
|
||||
process.env.OPENCLAW_DISCORD_SMOKE_BOT_TOKEN_PREFIX ||
|
||||
"Bot";
|
||||
const targetAgent = resolveArg("--agent") || process.env.OPENCLAW_DISCORD_SMOKE_AGENT || "codex";
|
||||
const targetAgent =
|
||||
resolveArg("--agent", argv) || process.env.OPENCLAW_DISCORD_SMOKE_AGENT || "codex";
|
||||
const mentionUserId =
|
||||
resolveArg("--mention") || process.env.OPENCLAW_DISCORD_SMOKE_MENTION_USER_ID || undefined;
|
||||
resolveArg("--mention", argv) ||
|
||||
process.env.OPENCLAW_DISCORD_SMOKE_MENTION_USER_ID ||
|
||||
undefined;
|
||||
const instruction =
|
||||
resolveArg("--instruction") || process.env.OPENCLAW_DISCORD_SMOKE_INSTRUCTION || undefined;
|
||||
resolveArg("--instruction", argv) ||
|
||||
process.env.OPENCLAW_DISCORD_SMOKE_INSTRUCTION ||
|
||||
undefined;
|
||||
const timeoutMs = parseNumber(
|
||||
resolveArg("--timeout-ms") || process.env.OPENCLAW_DISCORD_SMOKE_TIMEOUT_MS,
|
||||
resolveArg("--timeout-ms", argv) || process.env.OPENCLAW_DISCORD_SMOKE_TIMEOUT_MS,
|
||||
240_000,
|
||||
"--timeout-ms",
|
||||
);
|
||||
const pollMs = parseNumber(
|
||||
resolveArg("--poll-ms") || process.env.OPENCLAW_DISCORD_SMOKE_POLL_MS,
|
||||
resolveArg("--poll-ms", argv) || process.env.OPENCLAW_DISCORD_SMOKE_POLL_MS,
|
||||
1_500,
|
||||
"--poll-ms",
|
||||
);
|
||||
const stateDir = path.resolve(resolveArg("--state-dir") || resolveStateDir());
|
||||
const stateDir = path.resolve(resolveArg("--state-dir", argv) || resolveStateDir());
|
||||
const openclawBin =
|
||||
resolveArg("--openclaw-bin") || process.env.OPENCLAW_DISCORD_SMOKE_OPENCLAW_BIN || "openclaw";
|
||||
const json = hasFlag("--json");
|
||||
resolveArg("--openclaw-bin", argv) ||
|
||||
process.env.OPENCLAW_DISCORD_SMOKE_OPENCLAW_BIN ||
|
||||
"openclaw";
|
||||
const json = hasFlag("--json", argv);
|
||||
|
||||
if (!channelId) {
|
||||
throw new Error(usage());
|
||||
@@ -736,10 +794,10 @@ function printOutput(params: { json: boolean; payload: SuccessResult | FailureRe
|
||||
}
|
||||
}
|
||||
|
||||
async function run(): Promise<SuccessResult | FailureResult> {
|
||||
async function run(argv = process.argv.slice(2)): Promise<SuccessResult | FailureResult> {
|
||||
let args: Args;
|
||||
try {
|
||||
args = parseArgs();
|
||||
args = parseArgs(argv);
|
||||
} catch (err) {
|
||||
return {
|
||||
ok: false,
|
||||
@@ -1032,12 +1090,18 @@ async function run(): Promise<SuccessResult | FailureResult> {
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<number> {
|
||||
if (hasFlag("--help") || hasFlag("-h")) {
|
||||
async function main(argv = process.argv.slice(2)): Promise<number> {
|
||||
try {
|
||||
validateCliArgs(argv);
|
||||
} catch (err) {
|
||||
writeStderrLine(safeErrorMessage(err));
|
||||
return 1;
|
||||
}
|
||||
if (hasFlag("--help", argv) || hasFlag("-h", argv)) {
|
||||
writeStdoutLine(usage());
|
||||
return 0;
|
||||
}
|
||||
const result = await run().catch(
|
||||
const result = await run(argv).catch(
|
||||
(err: unknown): FailureResult => ({
|
||||
ok: false,
|
||||
stage: "unexpected",
|
||||
@@ -1046,7 +1110,7 @@ async function main(): Promise<number> {
|
||||
}),
|
||||
);
|
||||
printOutput({
|
||||
json: hasFlag("--json"),
|
||||
json: hasFlag("--json", argv),
|
||||
payload: result,
|
||||
});
|
||||
return result.ok ? 0 : 1;
|
||||
@@ -1054,6 +1118,7 @@ async function main(): Promise<number> {
|
||||
|
||||
export const testing = {
|
||||
parseDriverMode,
|
||||
parseArgs,
|
||||
parseNumber,
|
||||
DISCORD_RESPONSE_BODY_MAX_BYTES,
|
||||
redactDiscordApiPath,
|
||||
|
||||
@@ -79,6 +79,50 @@ describe("script-specific dev tooling hardening", () => {
|
||||
expect(() => discordSmokeTesting.parseDriverMode("curl")).toThrow(/Invalid --driver/u);
|
||||
});
|
||||
|
||||
it("rejects unknown Discord smoke args before live Discord/OpenClaw work", () => {
|
||||
expect(() => discordSmokeTesting.parseArgs(["--wat"])).toThrow("Unknown argument: --wat");
|
||||
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
["--import", "tsx", "scripts/dev/discord-acp-plain-language-smoke.ts", "--wat"],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
encoding: "utf8",
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe(1);
|
||||
expect(result.stdout).toBe("");
|
||||
expect(result.stderr.trim()).toBe("Unknown argument: --wat");
|
||||
});
|
||||
|
||||
it("prints Discord smoke usage without starting live validation", () => {
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
["--import", "tsx", "scripts/dev/discord-acp-plain-language-smoke.ts", "--help"],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
encoding: "utf8",
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout).toContain("Usage: bun scripts/dev/discord-acp-plain-language-smoke.ts");
|
||||
expect(result.stderr).toBe("");
|
||||
});
|
||||
|
||||
it("rejects missing Discord smoke option values before env fallbacks", () => {
|
||||
expect(() => discordSmokeTesting.parseArgs(["--channel"])).toThrow(
|
||||
"--channel requires a value",
|
||||
);
|
||||
expect(() => discordSmokeTesting.parseArgs(["--channel="])).toThrow(
|
||||
"--channel requires a value",
|
||||
);
|
||||
expect(() => discordSmokeTesting.parseArgs(["--channel", "--json"])).toThrow(
|
||||
"--channel requires a value",
|
||||
);
|
||||
});
|
||||
|
||||
it("redacts Discord webhook tokens from API paths", () => {
|
||||
const token = "webhook-secret-token-abcdef123456"; // pragma: allowlist secret
|
||||
const apiPath = `/webhooks/123/${token}?wait=true`;
|
||||
|
||||
Reference in New Issue
Block a user