From 00ab894febfcb9f0897eeb257a1f4217bcf9e9cc Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 21 Feb 2026 19:54:01 +0000 Subject: [PATCH] test(cli): dedupe acp program setup and cover token-file errors --- src/cli/acp-cli.option-collisions.test.ts | 92 +++++++++++------------ 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/src/cli/acp-cli.option-collisions.test.ts b/src/cli/acp-cli.option-collisions.test.ts index 3a48e7ab8..18ba92617 100644 --- a/src/cli/acp-cli.option-collisions.test.ts +++ b/src/cli/acp-cli.option-collisions.test.ts @@ -49,6 +49,23 @@ describe("acp cli option collisions", () => { } } + function createAcpProgram() { + const program = new Command(); + registerAcpCli(program); + return program; + } + + async function parseAcp(args: string[]) { + const program = createAcpProgram(); + await program.parseAsync(["acp", ...args], { from: "user" }); + } + + function expectCliError(pattern: RegExp) { + expect(serveAcpGateway).not.toHaveBeenCalled(); + expect(defaultRuntime.error).toHaveBeenCalledWith(expect.stringMatching(pattern)); + expect(defaultRuntime.exit).toHaveBeenCalledWith(1); + } + beforeAll(async () => { ({ registerAcpCli } = await import("./acp-cli.js")); }); @@ -74,17 +91,13 @@ describe("acp cli option collisions", () => { }); it("loads gateway token/password from files", async () => { - const { registerAcpCli } = await import("./acp-cli.js"); - const program = new Command(); - registerAcpCli(program); - await withSecretFiles({ token: "tok_file\n", password: "pw_file\n" }, async (files) => { - await program.parseAsync( - ["acp", "--token-file", files.tokenFile ?? "", "--password-file", files.passwordFile ?? ""], - { - from: "user", - }, - ); + await parseAcp([ + "--token-file", + files.tokenFile ?? "", + "--password-file", + files.passwordFile ?? "", + ]); }); expect(serveAcpGateway).toHaveBeenCalledWith( @@ -96,55 +109,23 @@ describe("acp cli option collisions", () => { }); it("rejects mixed secret flags and file flags", async () => { - const { registerAcpCli } = await import("./acp-cli.js"); - const program = new Command(); - registerAcpCli(program); - await withSecretFiles({ token: "tok_file\n" }, async (files) => { - await program.parseAsync( - ["acp", "--token", "tok_inline", "--token-file", files.tokenFile ?? ""], - { - from: "user", - }, - ); + await parseAcp(["--token", "tok_inline", "--token-file", files.tokenFile ?? ""]); }); - expect(serveAcpGateway).not.toHaveBeenCalled(); - expect(defaultRuntime.error).toHaveBeenCalledWith( - expect.stringMatching(/Use either --token or --token-file/), - ); - expect(defaultRuntime.exit).toHaveBeenCalledWith(1); + expectCliError(/Use either --token or --token-file/); }); it("rejects mixed password flags and file flags", async () => { - const { registerAcpCli } = await import("./acp-cli.js"); - const program = new Command(); - registerAcpCli(program); - await withSecretFiles({ password: "pw_file\n" }, async (files) => { - await program.parseAsync( - ["acp", "--password", "pw_inline", "--password-file", files.passwordFile ?? ""], - { - from: "user", - }, - ); + await parseAcp(["--password", "pw_inline", "--password-file", files.passwordFile ?? ""]); }); - expect(serveAcpGateway).not.toHaveBeenCalled(); - expect(defaultRuntime.error).toHaveBeenCalledWith( - expect.stringMatching(/Use either --password or --password-file/), - ); - expect(defaultRuntime.exit).toHaveBeenCalledWith(1); + expectCliError(/Use either --password or --password-file/); }); it("warns when inline secret flags are used", async () => { - const { registerAcpCli } = await import("./acp-cli.js"); - const program = new Command(); - registerAcpCli(program); - - await program.parseAsync(["acp", "--token", "tok_inline", "--password", "pw_inline"], { - from: "user", - }); + await parseAcp(["--token", "tok_inline", "--password", "pw_inline"]); expect(defaultRuntime.error).toHaveBeenCalledWith( expect.stringMatching(/--token can be exposed via process listings/), @@ -153,4 +134,21 @@ describe("acp cli option collisions", () => { expect.stringMatching(/--password can be exposed via process listings/), ); }); + + it("trims token file path before reading", async () => { + await withSecretFiles({ token: "tok_file\n" }, async (files) => { + await parseAcp(["--token-file", ` ${files.tokenFile ?? ""} `]); + }); + + expect(serveAcpGateway).toHaveBeenCalledWith( + expect.objectContaining({ + gatewayToken: "tok_file", + }), + ); + }); + + it("reports missing token-file read errors", async () => { + await parseAcp(["--token-file", "/tmp/openclaw-acp-missing-token.txt"]); + expectCliError(/Failed to read Gateway token file/); + }); });