mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 01:29:56 +00:00
test(runtime): keep Node-only child proofs on Node (#147329)
* test(runtime): keep Node-only child proofs on Node * test(runtime): link Bun exit code fix
This commit is contained in:
parent
88c2b10d6b
commit
bf1b8db1f5
9 changed files with 48 additions and 24 deletions
|
|
@ -5,11 +5,13 @@ import os from "node:os";
|
|||
import path from "node:path";
|
||||
import { createInterface } from "node:readline";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { resolveTestNodeExecPath } from "openclaw/plugin-sdk/test-fixtures";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createCodexNativeTestState } from "./native-app-server.test-support.js";
|
||||
|
||||
type ProcessTree = { parent: number; child: number; descendant: number };
|
||||
const fixture = fileURLToPath(new URL("./transport-orphan.test-helper.ts", import.meta.url));
|
||||
const nodeExecPath = resolveTestNodeExecPath();
|
||||
|
||||
function isAlive(pid: number) {
|
||||
try {
|
||||
|
|
@ -93,7 +95,7 @@ describe.skipIf(process.platform === "win32")("Codex stdio crash recovery", () =
|
|||
root,
|
||||
...(native ? [native.command, native.cwd] : []),
|
||||
];
|
||||
const parent = spawn(process.execPath, args, {
|
||||
const parent = spawn(nodeExecPath, args, {
|
||||
env: {
|
||||
HOME: root,
|
||||
...native?.env,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import { describe, expect, it } from "vitest";
|
|||
|
||||
describe("googlechat secret contract import boundary", () => {
|
||||
it("exposes service-account targets without loading secret setup or resolution", async () => {
|
||||
const stdout = await runDirectImportSmoke(`
|
||||
const stdout = await runDirectImportSmoke(
|
||||
`
|
||||
import { realpathSync } from "node:fs";
|
||||
import { registerHooks } from "node:module";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
|
@ -38,7 +39,9 @@ try {
|
|||
} finally {
|
||||
hooks.deregister();
|
||||
}
|
||||
`);
|
||||
`,
|
||||
{ runtime: "node" },
|
||||
);
|
||||
const result = JSON.parse(stdout);
|
||||
expect(result.observed.entry).toBe(true);
|
||||
expect(result.collectorCallable).toBe(true);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
|||
import type { ProviderAuthMethod } from "openclaw/plugin-sdk/core";
|
||||
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
||||
import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api";
|
||||
import { resolveTestNodeExecPath } from "openclaw/plugin-sdk/test-fixtures";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { azLoginDeviceCodeWithOptions, execAz, getAccessTokenResultAsync } from "./cli.js";
|
||||
import plugin from "./index.js";
|
||||
|
|
@ -41,7 +42,13 @@ vi.mock("node:child_process", async () => {
|
|||
const actual = await vi.importActual<typeof import("node:child_process")>("node:child_process");
|
||||
return {
|
||||
...actual,
|
||||
execFileSync: execFileSyncMock,
|
||||
execFileSync: ((file: string, ...args: unknown[]) =>
|
||||
file === "az"
|
||||
? execFileSyncMock(file, ...args)
|
||||
: Reflect.apply(actual.execFileSync, actual, [
|
||||
file,
|
||||
...args,
|
||||
])) as typeof actual.execFileSync,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -755,6 +762,7 @@ describe("microsoft-foundry plugin", () => {
|
|||
timeout: 180_000,
|
||||
},
|
||||
async ({ signal }) => {
|
||||
const nodeExecPath = resolveTestNodeExecPath();
|
||||
const { runExec } = await vi.importActual<
|
||||
typeof import("openclaw/plugin-sdk/process-runtime")
|
||||
>("openclaw/plugin-sdk/process-runtime");
|
||||
|
|
@ -788,7 +796,7 @@ process.stdout.write(JSON.stringify({
|
|||
if (process.platform === "win32") {
|
||||
await fs.writeFile(
|
||||
path.join(binDir, "az.cmd"),
|
||||
`@echo off\r\n"${process.execPath}" "${fakeAz}" %*\r\n`,
|
||||
`@echo off\r\n"${nodeExecPath}" "${fakeAz}" %*\r\n`,
|
||||
);
|
||||
} else {
|
||||
await fs.chmod(fakeAz, 0o700);
|
||||
|
|
@ -861,7 +869,7 @@ assert.equal(afterOldest, 130, "the evicted account must refresh through az");
|
|||
// The child has its own real profile store and token cache. Only az is synthetic;
|
||||
// no ambient credentials or the parent Vitest mocks enter its provider graph.
|
||||
const { stdout } = await runExec(
|
||||
process.execPath,
|
||||
nodeExecPath,
|
||||
["--import", "tsx/esm", script, proofDir, new URL("./index.ts", import.meta.url).href],
|
||||
{
|
||||
cwd: repoRoot,
|
||||
|
|
@ -871,7 +879,7 @@ assert.equal(afterOldest, 130, "the evicted account must refresh through az");
|
|||
baseEnv: {
|
||||
PATH: [
|
||||
binDir,
|
||||
path.dirname(process.execPath),
|
||||
path.dirname(nodeExecPath),
|
||||
...(process.platform === "win32" ? [] : ["/usr/bin", "/bin"]),
|
||||
].join(path.delimiter),
|
||||
HOME: homeDir,
|
||||
|
|
|
|||
|
|
@ -3,14 +3,16 @@ import { execFile } from "node:child_process";
|
|||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { resolveTestNodeExecPath } from "openclaw/plugin-sdk/test-fixtures";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
describe("sendMSTeamsActivityWithReference SDK import ordering", () => {
|
||||
it("keeps root exports intact when quoted behavior is the first SDK access", async () => {
|
||||
const nodeExecPath = resolveTestNodeExecPath();
|
||||
await execFileAsync(
|
||||
process.execPath,
|
||||
nodeExecPath,
|
||||
["scripts/lib/plugin-npm-runtime-build.mjs", "extensions/msteams"],
|
||||
{ cwd: process.cwd() },
|
||||
);
|
||||
|
|
@ -146,7 +148,7 @@ describe("sendMSTeamsActivityWithReference SDK import ordering", () => {
|
|||
`;
|
||||
|
||||
const { stdout, stderr } = await execFileAsync(
|
||||
process.execPath,
|
||||
nodeExecPath,
|
||||
["--import", "tsx", "--input-type=module", "--eval", fixture],
|
||||
{
|
||||
cwd: path.dirname(outDir),
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ function optionArgs(options: PathCommandOptions): string[] {
|
|||
|
||||
async function invokePathCli(args: string[], runtime: TestRuntime): Promise<void> {
|
||||
const previousExitCode = process.exitCode;
|
||||
process.exitCode = undefined;
|
||||
process.exitCode = 0;
|
||||
const stdoutWrite = vi.spyOn(process.stdout, "write").mockImplementation(((chunk: unknown) => {
|
||||
runtime.writeStdout(String(chunk));
|
||||
return true;
|
||||
|
|
@ -141,7 +141,9 @@ async function invokePathCli(args: string[], runtime: TestRuntime): Promise<void
|
|||
} finally {
|
||||
stdoutWrite.mockRestore();
|
||||
stderrWrite.mockRestore();
|
||||
process.exitCode = previousExitCode;
|
||||
// oxlint-disable-next-line no-warning-comments -- replace the pending link after Bun ships the fix.
|
||||
// TODO(bun#42607): Assign undefined once Bun clears a nonzero process.exitCode.
|
||||
process.exitCode = previousExitCode ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { execFile } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { promisify } from "node:util";
|
||||
import { resolveTestNodeExecPath } from "openclaw/plugin-sdk/test-fixtures";
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
|
@ -8,11 +9,12 @@ const repoRoot = fileURLToPath(new URL("../../../", import.meta.url));
|
|||
const fixturePath = fileURLToPath(
|
||||
new URL("./test-support/bus-server-shutdown.ts", import.meta.url),
|
||||
);
|
||||
const nodeExecPath = resolveTestNodeExecPath();
|
||||
|
||||
// Vitest's own handles would keep an unreferenced shutdown timer alive.
|
||||
it.each(["bus", "provider"])("finishes %s shutdown after rejecting an upload", async (kind) => {
|
||||
const { stdout, stderr } = await execFileAsync(
|
||||
process.execPath,
|
||||
nodeExecPath,
|
||||
["--import", "./scripts/tsx.mjs", fixturePath, kind],
|
||||
{ cwd: repoRoot, encoding: "utf8", timeout: 20_000 },
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import path from "node:path";
|
|||
import { setTimeout as sleep } from "node:timers/promises";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { useAutoCleanupTempDirTracker } from "openclaw/plugin-sdk/test-env";
|
||||
import { resolveTestNodeExecPath } from "openclaw/plugin-sdk/test-fixtures";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import type { QaSuiteSummaryJson } from "./suite-summary.js";
|
||||
import { runQaWindowsTaskkill } from "./windows-system-tools.js";
|
||||
|
|
@ -16,6 +17,7 @@ const fixturePath = fileURLToPath(
|
|||
const artifactsRoot = path.join(repoRoot, ".artifacts", "qa-e2e");
|
||||
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
|
||||
const activeChildren = new Set<ChildProcess>();
|
||||
const nodeExecPath = resolveTestNodeExecPath();
|
||||
|
||||
const PROCESS_LIFECYCLE_SCENARIO = "channel-chat-baseline";
|
||||
// Suite execution contends with the surrounding extension shard; only the bounded
|
||||
|
|
@ -109,15 +111,11 @@ afterEach(async () => {
|
|||
});
|
||||
|
||||
function startSuiteProcess(outputDir: string, scenarioIds: readonly string[]) {
|
||||
const child = spawn(
|
||||
process.execPath,
|
||||
["--import", "tsx", fixturePath, outputDir, ...scenarioIds],
|
||||
{
|
||||
cwd: repoRoot,
|
||||
env: buildSuiteProcessEnv(outputDir),
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
},
|
||||
);
|
||||
const child = spawn(nodeExecPath, ["--import", "tsx", fixturePath, outputDir, ...scenarioIds], {
|
||||
cwd: repoRoot,
|
||||
env: buildSuiteProcessEnv(outputDir),
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
activeChildren.add(child);
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export { sanitizeTerminalText } from "../../packages/terminal-core/src/safe-text
|
|||
export { countLines, hasBalancedFences } from "../test-utils/chunk-test-helpers.js";
|
||||
export { expectGeneratedTokenPersistedToGatewayAuth } from "../test-utils/auth-token-assertions.js";
|
||||
export { typedCases } from "../test-utils/typed-cases.js";
|
||||
export { resolveTestNodeExecPath } from "../test-utils/node-process.js";
|
||||
export { createRequireRecord } from "../../test/helpers/record.js";
|
||||
export type { RecordRequirementKind, RecordRequirementMessage } from "../../test/helpers/record.js";
|
||||
export {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { execFile } from "node:child_process";
|
|||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { promisify } from "node:util";
|
||||
import { resolveTestNodeExecPath } from "../../test-utils/node-process.js";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
||||
|
|
@ -17,9 +18,14 @@ const SHARED_IMPORT_ENV = {
|
|||
TERM: process.env.TERM,
|
||||
} satisfies NodeJS.ProcessEnv;
|
||||
|
||||
export async function runDirectImportSmoke(code: string): Promise<string> {
|
||||
const runtimeArgs = process.versions.bun ? [] : ["--import", "tsx"];
|
||||
const { stdout } = await execFileAsync(process.execPath, [...runtimeArgs, "-e", code], {
|
||||
export async function runDirectImportSmoke(
|
||||
code: string,
|
||||
options: { runtime?: "current" | "node" } = {},
|
||||
): Promise<string> {
|
||||
const useNode = options.runtime === "node" || !process.versions.bun;
|
||||
const runtimeArgs = useNode ? ["--import", "tsx"] : [];
|
||||
const execPath = options.runtime === "node" ? resolveTestNodeExecPath() : process.execPath;
|
||||
const { stdout } = await execFileAsync(execPath, [...runtimeArgs, "-e", code], {
|
||||
cwd: repoRoot,
|
||||
env: SHARED_IMPORT_ENV,
|
||||
timeout: 40_000,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue