File size: 4,141 Bytes
e249c6d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { spawn } from "node:child_process";
import { once } from "node:events";
import fs from "node:fs/promises";
import path from "node:path";
import { text } from "node:stream/consumers";
import { afterEach, expect, it, vi } from "vitest";
import { useAutoCleanupTempDirTracker } from "../../../test/helpers/temp-dir.js";
import type { CliBackendExecute } from "../../plugins/cli-backend.types.js";
import { buildPreparedCliRunContext } from "../cli-runner.test-helpers.js";
import { executePreparedCliRun as executePreparedCliRunImpl } from "./execute.js";
import { wrapPreparedCliRunWithTestAdmission } from "./execute.test-support.js";

const executePreparedCliRun = wrapPreparedCliRunWithTestAdmission(executePreparedCliRunImpl);
const tempDirs = useAutoCleanupTempDirTracker(afterEach);

async function runVerifiedFixture(command: string, args: string[], abortSignal: AbortSignal) {
  const execute: CliBackendExecute = async function* (context) {
    const child = spawn(context.command, context.args, {
      argv0: context.argv0,
      cwd: context.cwd,
      env: context.env,
      signal: context.abortSignal,
      killSignal: "SIGKILL",
      stdio: ["ignore", "pipe", "pipe"],
    });
    const closed = new Promise<void>((resolve) => {
      child.once("close", () => resolve());
    });
    try {
      const [[code], stdout, stderr] = await Promise.all([
        once(child, "close"),
        text(child.stdout),
        text(child.stderr),
      ]);
      if (code !== 0) {
        throw new Error(stderr || `Fixture CLI exited with code ${code}`);
      }
      yield { type: "result", subtype: "success", result: stdout };
    } finally {
      if (child.exitCode === null && child.signalCode === null) {
        child.kill("SIGKILL");
      }
      await closed;
    }
  };
  const context = buildPreparedCliRunContext({
    backend: {
      command,
      args,
      modelArg: undefined,
      sessionArgs: undefined,
      systemPromptFileArg: undefined,
      input: "stdin",
      output: "jsonl",
      jsonlDialect: "claude-stream-json",
    },
    onSuccessfulAuthBinding: () => {},
    runtimeArtifact: {
      kind: "bundled-package-tree",
      packageName: "@fixture/cli-invocation",
      entrypoint: "command",
      nativeExecutableNames: ["cli-fixture"],
    },
  });
  context.params.abortSignal = abortSignal;
  context.authBindingFingerprint = "fixture-owner";
  context.executionTarget = { kind: "plugin", execute };
  // Invocation correctness must not race the helper's one-second watchdog.
  // Vitest's real deadline still aborts the run and reaps the actual child.
  vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
  try {
    return await executePreparedCliRun(context);
  } finally {
    vi.useRealTimers();
  }
}

it("runs a verified plugin CLI script through its resolved interpreter", async ({ signal }) => {
  const root = tempDirs.make("openclaw-plugin-cli-invocation-");
  const entrypoint = path.join(root, "cli.js");
  await fs.writeFile(
    path.join(root, "package.json"),
    JSON.stringify({ name: "@fixture/cli-invocation", version: "1.0.0" }),
  );
  await fs.writeFile(
    entrypoint,
    `#!${process.execPath}\nprocess.stdout.write("fixture-script:" + process.argv.slice(2).join("|"));\n`,
    { mode: 0o755 },
  );
  let command = entrypoint;
  if (process.platform === "win32") {
    command = path.join(root, "cli.cmd");
    await fs.writeFile(command, '@echo off\r\n"%~dp0\\cli.js" %*\r\n');
  }

  await expect(
    runVerifiedFixture(command, ["--fixture-option", "kept"], signal),
  ).resolves.toMatchObject({
    text: "fixture-script:--fixture-option|kept",
  });
});

it.skipIf(process.platform === "win32")(
  "preserves a verified native CLI's symlink invocation name in a plugin process",
  async ({ signal }) => {
    const root = tempDirs.make("openclaw-plugin-cli-alias-");
    const command = path.join(root, "cli-fixture");
    await fs.symlink(process.execPath, command);

    await expect(
      runVerifiedFixture(command, ["-e", "process.stdout.write(process.argv0)"], signal),
    ).resolves.toMatchObject({ text: command });
  },
);