mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 11:55:47 -06:00
fix(test): guard dev smoke cli args
This commit is contained in:
@@ -15,13 +15,15 @@ function writeStderrLine(message: string): void {
|
||||
}
|
||||
|
||||
function writeUsage(): void {
|
||||
writeStderrLine(
|
||||
"Usage: bun scripts/dev/gateway-smoke.ts --url <wss://host[:port]> --token <gateway.auth.token>\n" +
|
||||
"Or set env: OPENCLAW_GATEWAY_URL / OPENCLAW_GATEWAY_TOKEN",
|
||||
);
|
||||
writeStderrLine(usage());
|
||||
}
|
||||
|
||||
type GatewaySmokeClient = ReturnType<typeof createGatewayWsClient>;
|
||||
type GatewaySmokeCliOptions = {
|
||||
help: boolean;
|
||||
token?: string;
|
||||
urlRaw?: string;
|
||||
};
|
||||
|
||||
type GatewaySmokeDeps = {
|
||||
createClient?: typeof createGatewayWsClient;
|
||||
@@ -29,6 +31,54 @@ type GatewaySmokeDeps = {
|
||||
stdout?: (message: string) => void;
|
||||
};
|
||||
|
||||
class GatewaySmokeArgError extends Error {}
|
||||
|
||||
const BOOLEAN_FLAGS = new Set(["--help", "-h"]);
|
||||
const VALUE_FLAGS = new Set(["--url", "--token"]);
|
||||
|
||||
function usage(): string {
|
||||
return [
|
||||
"Usage: bun scripts/dev/gateway-smoke.ts --url <wss://host[:port]> --token <gateway.auth.token>",
|
||||
"Or set env: OPENCLAW_GATEWAY_URL / OPENCLAW_GATEWAY_TOKEN",
|
||||
"",
|
||||
"Options:",
|
||||
" --url <url> Gateway websocket URL",
|
||||
" --token <token> Gateway auth token",
|
||||
" -h, --help Show this help",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function validateArgs(argv: readonly string[]): void {
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const arg = argv[index] ?? "";
|
||||
if (BOOLEAN_FLAGS.has(arg)) {
|
||||
continue;
|
||||
}
|
||||
if (VALUE_FLAGS.has(arg)) {
|
||||
const value = argv[index + 1];
|
||||
if (!value || value.startsWith("--")) {
|
||||
throw new GatewaySmokeArgError(`${arg} requires a value`);
|
||||
}
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
throw new GatewaySmokeArgError(`Unknown argument: ${arg}`);
|
||||
}
|
||||
}
|
||||
|
||||
function parseGatewaySmokeCli(
|
||||
argv = process.argv.slice(2),
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): GatewaySmokeCliOptions {
|
||||
validateArgs(argv);
|
||||
const { get: getArg, has } = createArgReader([...argv]);
|
||||
return {
|
||||
help: has("--help") || has("-h"),
|
||||
token: getArg("--token") ?? env.OPENCLAW_GATEWAY_TOKEN,
|
||||
urlRaw: getArg("--url") ?? env.OPENCLAW_GATEWAY_URL,
|
||||
};
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
@@ -158,14 +208,25 @@ export async function runGatewaySmoke(
|
||||
}
|
||||
|
||||
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
||||
const { get: getArg } = createArgReader();
|
||||
const urlRaw = getArg("--url") ?? process.env.OPENCLAW_GATEWAY_URL;
|
||||
const token = getArg("--token") ?? process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
let cli: GatewaySmokeCliOptions;
|
||||
try {
|
||||
cli = parseGatewaySmokeCli();
|
||||
} catch (error) {
|
||||
writeStderrLine(error instanceof Error ? error.message : String(error));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!urlRaw || !token) {
|
||||
if (cli.help) {
|
||||
writeStdoutLine(usage());
|
||||
} else if (!cli.urlRaw || !cli.token) {
|
||||
writeUsage();
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
process.exitCode = await runGatewaySmoke({ token, urlRaw });
|
||||
process.exitCode = await runGatewaySmoke({ token: cli.token, urlRaw: cli.urlRaw });
|
||||
}
|
||||
}
|
||||
|
||||
export const testing = {
|
||||
parseGatewaySmokeCli,
|
||||
usage,
|
||||
};
|
||||
|
||||
@@ -17,12 +17,57 @@ function writeStderrLine(message: string): void {
|
||||
process.stderr.write(`${message}\n`);
|
||||
}
|
||||
|
||||
function usage(): string {
|
||||
return [
|
||||
"Usage: bun scripts/dev/ios-node-e2e.ts --url <wss://host[:port]> --token <gateway.auth.token> [options]",
|
||||
"Or set env: OPENCLAW_GATEWAY_URL / OPENCLAW_GATEWAY_TOKEN",
|
||||
"",
|
||||
"Options:",
|
||||
" --node <id|name-substring> Select a connected iOS node",
|
||||
" --wait-seconds <n> Seconds to wait for an iOS node (default: 25)",
|
||||
" --dangerous Include camera/screen commands",
|
||||
" --json Print JSON results",
|
||||
" -h, --help Show this help",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
const argv = process.argv.slice(2);
|
||||
const getArg = (flag: string) => {
|
||||
const index = argv.indexOf(flag);
|
||||
return index === -1 ? undefined : argv[index + 1];
|
||||
};
|
||||
const hasFlag = (flag: string) => argv.includes(flag);
|
||||
const BOOLEAN_FLAGS = new Set(["--dangerous", "--help", "-h", "--json"]);
|
||||
const VALUE_FLAGS = new Set(["--node", "--token", "--url", "--wait-seconds"]);
|
||||
|
||||
function failCli(message: string): never {
|
||||
writeStderrLine(message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function validateArgs(): void {
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const arg = argv[index] ?? "";
|
||||
if (BOOLEAN_FLAGS.has(arg)) {
|
||||
continue;
|
||||
}
|
||||
if (VALUE_FLAGS.has(arg)) {
|
||||
const value = argv[index + 1];
|
||||
if (!value || value.startsWith("--")) {
|
||||
failCli(`${arg} requires a value`);
|
||||
}
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
failCli(`Unknown argument: ${arg}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFlag("--help") || hasFlag("-h")) {
|
||||
writeStdoutLine(usage());
|
||||
process.exit(0);
|
||||
}
|
||||
validateArgs();
|
||||
|
||||
type NodeListPayload = {
|
||||
ts?: number;
|
||||
@@ -46,10 +91,7 @@ const dangerous = hasFlag("--dangerous") || process.env.OPENCLAW_RUN_DANGEROUS =
|
||||
const jsonOut = hasFlag("--json");
|
||||
|
||||
if (!urlRaw || !token) {
|
||||
writeStderrLine(
|
||||
"Usage: bun scripts/dev/ios-node-e2e.ts --url <wss://host[:port]> --token <gateway.auth.token> [--node <id|name-substring>] [--dangerous] [--json]\n" +
|
||||
"Or set env: OPENCLAW_GATEWAY_URL / OPENCLAW_GATEWAY_TOKEN",
|
||||
);
|
||||
writeStderrLine(usage());
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,17 @@ class UsageError extends Error {
|
||||
readonly exitCode = 1;
|
||||
}
|
||||
|
||||
class CliArgumentError extends UsageError {}
|
||||
|
||||
type DevicePairTelegramArgs = {
|
||||
accountId?: string;
|
||||
chatId?: string;
|
||||
help: boolean;
|
||||
};
|
||||
|
||||
const BOOLEAN_FLAGS = new Set(["--help", "-h"]);
|
||||
const VALUE_FLAGS = new Set(["--account", "-a", "--chat", "-c"]);
|
||||
|
||||
function writeStdoutLine(...parts: string[]): void {
|
||||
process.stdout.write(`${parts.join(" ")}\n`);
|
||||
}
|
||||
@@ -57,9 +68,41 @@ function readArg(args: string[], flag: string, short?: string): string | undefin
|
||||
function usage(): string {
|
||||
return [
|
||||
"Usage: bun scripts/dev/test-device-pair-telegram.ts --chat <telegram-chat-id> [--account <accountId>]",
|
||||
"",
|
||||
"Options:",
|
||||
" --chat, -c <id> Telegram chat id",
|
||||
" --account, -a <id> Telegram account id",
|
||||
" -h, --help Show this help",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function validateArgs(args: readonly string[]): void {
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index] ?? "";
|
||||
if (BOOLEAN_FLAGS.has(arg)) {
|
||||
continue;
|
||||
}
|
||||
if (VALUE_FLAGS.has(arg)) {
|
||||
const value = args[index + 1];
|
||||
if (!value || value.startsWith("--")) {
|
||||
throw new CliArgumentError(`${arg} requires a value`);
|
||||
}
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
throw new CliArgumentError(`Unknown argument: ${arg}`);
|
||||
}
|
||||
}
|
||||
|
||||
function parseDevicePairTelegramArgs(args: readonly string[]): DevicePairTelegramArgs {
|
||||
validateArgs(args);
|
||||
return {
|
||||
accountId: readArg([...args], "--account", "-a"),
|
||||
chatId: readArg([...args], "--chat", "-c"),
|
||||
help: args.includes("--help") || args.includes("-h"),
|
||||
};
|
||||
}
|
||||
|
||||
async function loadTelegramRuntimeSendMessage(): Promise<SendMessageTelegram> {
|
||||
const specifier = "../../extensions/telegram/runtime-api.js";
|
||||
const runtime = (await import(specifier)) as { sendMessageTelegram?: SendMessageTelegram };
|
||||
@@ -86,8 +129,10 @@ async function runDevicePairTelegram(
|
||||
args = process.argv.slice(2),
|
||||
deps: DevicePairTelegramDeps = createDefaultDeps(),
|
||||
): Promise<DevicePairTelegramResult> {
|
||||
const chatId = readArg(args, "--chat", "-c");
|
||||
const accountId = readArg(args, "--account", "-a");
|
||||
const { accountId, chatId, help } = parseDevicePairTelegramArgs(args);
|
||||
if (help) {
|
||||
throw new UsageError(usage());
|
||||
}
|
||||
if (!chatId) {
|
||||
throw new UsageError(usage());
|
||||
}
|
||||
@@ -133,7 +178,12 @@ async function runDevicePairTelegram(
|
||||
|
||||
async function main(): Promise<void> {
|
||||
try {
|
||||
const result = await runDevicePairTelegram();
|
||||
const args = process.argv.slice(2);
|
||||
if (args.includes("--help") || args.includes("-h")) {
|
||||
writeStdoutLine(usage());
|
||||
return;
|
||||
}
|
||||
const result = await runDevicePairTelegram(args);
|
||||
writeStdoutLine(
|
||||
"Sent split /pair messages to",
|
||||
result.chatId,
|
||||
@@ -150,4 +200,4 @@ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
||||
await main();
|
||||
}
|
||||
|
||||
export { runDevicePairTelegram };
|
||||
export { parseDevicePairTelegramArgs, runDevicePairTelegram };
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Gateway Smoke tests cover QA Lab gateway smoke evidence.
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { createServer, type Server } from "node:http";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { WebSocket, WebSocketServer } from "ws";
|
||||
@@ -163,6 +164,45 @@ describe("gateway-smoke", () => {
|
||||
};
|
||||
}
|
||||
|
||||
it("prints CLI help without connecting", () => {
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
["--import", "tsx", "scripts/dev/gateway-smoke.ts", "--help"],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
encoding: "utf8",
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout).toContain("Usage: bun scripts/dev/gateway-smoke.ts");
|
||||
expect(result.stderr).toBe("");
|
||||
});
|
||||
|
||||
it("rejects unknown CLI args before connecting", () => {
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[
|
||||
"--import",
|
||||
"tsx",
|
||||
"scripts/dev/gateway-smoke.ts",
|
||||
"--url",
|
||||
"ws://127.0.0.1:9",
|
||||
"--token",
|
||||
"token",
|
||||
"--wat",
|
||||
],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
encoding: "utf8",
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe(1);
|
||||
expect(result.stdout).toBe("");
|
||||
expect(result.stderr.trim()).toBe("Unknown argument: --wat");
|
||||
});
|
||||
|
||||
it("passes against a loopback gateway websocket using the real client", async () => {
|
||||
const stdout: string[] = [];
|
||||
const stderr: string[] = [];
|
||||
|
||||
@@ -216,7 +216,46 @@ function runScript(url: string, extraArgs: readonly string[] = []): Promise<Scri
|
||||
});
|
||||
}
|
||||
|
||||
function runScriptRaw(args: readonly string[]): Promise<ScriptResult> {
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn(
|
||||
process.execPath,
|
||||
["--import", "tsx", "scripts/dev/ios-node-e2e.ts", ...args],
|
||||
{
|
||||
stdio: "pipe",
|
||||
},
|
||||
);
|
||||
const stdout = createBoundedChildOutput();
|
||||
const stderr = createBoundedChildOutput();
|
||||
child.stdout.on("data", (chunk) => {
|
||||
stdout.append(chunk);
|
||||
});
|
||||
child.stderr.on("data", (chunk) => {
|
||||
stderr.append(chunk);
|
||||
});
|
||||
child.on("close", (status, signal) => {
|
||||
resolve({ status, signal, stdout: stdout.text(), stderr: stderr.text(), timedOut: false });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe("ios-node-e2e", () => {
|
||||
it("prints CLI help without connecting", async () => {
|
||||
const result = await runScriptRaw(["--help"]);
|
||||
|
||||
expect(result).toMatchObject({ signal: null, status: 0, timedOut: false });
|
||||
expect(result.stdout).toContain("Usage: bun scripts/dev/ios-node-e2e.ts");
|
||||
expect(result.stderr).toBe("");
|
||||
});
|
||||
|
||||
it("rejects unknown CLI args before connecting", async () => {
|
||||
const result = await runScript("ws://127.0.0.1:9", ["--wat"]);
|
||||
|
||||
expect(result).toMatchObject({ signal: null, status: 1, timedOut: false });
|
||||
expect(result.stderr.trim()).toBe("Unknown argument: --wat");
|
||||
expect(result.stdout).toBe("");
|
||||
});
|
||||
|
||||
it("rejects malformed wait seconds before connecting", async () => {
|
||||
const result = await runScript("ws://127.0.0.1:9", ["--wait-seconds", "1e3"]);
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Test Device Pair Telegram tests cover the dev Telegram pairing smoke helper.
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { runDevicePairTelegram } from "../../scripts/dev/test-device-pair-telegram.ts";
|
||||
import {
|
||||
parseDevicePairTelegramArgs,
|
||||
runDevicePairTelegram,
|
||||
} from "../../scripts/dev/test-device-pair-telegram.ts";
|
||||
|
||||
const scriptUrl = pathToFileURL("scripts/dev/test-device-pair-telegram.ts").href;
|
||||
|
||||
@@ -10,6 +13,34 @@ describe("scripts/dev/test-device-pair-telegram.ts", () => {
|
||||
await expect(import(`${scriptUrl}?case=load-${Date.now()}`)).resolves.toBeDefined();
|
||||
});
|
||||
|
||||
it("parses help without requiring Telegram config", () => {
|
||||
expect(parseDevicePairTelegramArgs(["--help"])).toEqual({
|
||||
accountId: undefined,
|
||||
chatId: undefined,
|
||||
help: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects unknown args before loading OpenClaw plugins", async () => {
|
||||
const cfg = { channels: { telegram: { enabled: true } } };
|
||||
const loadOpenClawPlugins = vi.fn();
|
||||
const executePluginCommand = vi.fn();
|
||||
const sendMessageTelegram = vi.fn();
|
||||
|
||||
await expect(
|
||||
runDevicePairTelegram(["--chat", "chat-123", "--wat"], {
|
||||
executePluginCommand,
|
||||
getRuntimeConfig: () => cfg,
|
||||
loadOpenClawPlugins,
|
||||
matchPluginCommand: () => ({ args: "from-match", command: { name: "pair" } as never }),
|
||||
sendMessageTelegram,
|
||||
}),
|
||||
).rejects.toThrow("Unknown argument: --wat");
|
||||
expect(loadOpenClawPlugins).not.toHaveBeenCalled();
|
||||
expect(executePluginCommand).not.toHaveBeenCalled();
|
||||
expect(sendMessageTelegram).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("sends the generated /pair reply through the injected Telegram runtime", async () => {
|
||||
const cfg = { channels: { telegram: { enabled: true } } };
|
||||
const loadOpenClawPlugins = vi.fn();
|
||||
|
||||
Reference in New Issue
Block a user