mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 19:00:45 +00:00
fix: keep private qa source only
This commit is contained in:
@@ -1,13 +1,75 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { loadPrivateQaCliModule } from "./private-qa-cli.js";
|
||||
|
||||
describe("private-qa-cli", () => {
|
||||
it("loads the private QA CLI facade through the local plugin-sdk entrypoint", async () => {
|
||||
const module = await loadPrivateQaCliModule();
|
||||
const tempDirs: string[] = [];
|
||||
const originalPrivateQaCli = process.env.OPENCLAW_ENABLE_PRIVATE_QA_CLI;
|
||||
|
||||
afterEach(() => {
|
||||
for (const dir of tempDirs.splice(0)) {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
if (originalPrivateQaCli === undefined) {
|
||||
delete process.env.OPENCLAW_ENABLE_PRIVATE_QA_CLI;
|
||||
} else {
|
||||
process.env.OPENCLAW_ENABLE_PRIVATE_QA_CLI = originalPrivateQaCli;
|
||||
}
|
||||
});
|
||||
|
||||
it("loads the private QA CLI from a source checkout path", async () => {
|
||||
process.env.OPENCLAW_ENABLE_PRIVATE_QA_CLI = "1";
|
||||
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-private-qa-source-"));
|
||||
tempDirs.push(repoRoot);
|
||||
const importModule = vi.fn(async () => ({
|
||||
isQaLabCliAvailable: expect.any(Function),
|
||||
registerQaLabCli: expect.any(Function),
|
||||
}));
|
||||
|
||||
const module = await loadPrivateQaCliModule({
|
||||
importModule,
|
||||
resolvePackageRootSync: () => repoRoot,
|
||||
existsSync: (filePath) =>
|
||||
[
|
||||
path.join(repoRoot, ".git"),
|
||||
path.join(repoRoot, "src"),
|
||||
path.join(repoRoot, "dist", "plugin-sdk", "qa-lab.js"),
|
||||
].includes(filePath),
|
||||
});
|
||||
|
||||
expect(importModule).toHaveBeenCalledTimes(1);
|
||||
expect(importModule.mock.calls[0]?.[0]).toContain("/dist/plugin-sdk/qa-lab.js");
|
||||
expect(module).toMatchObject({
|
||||
isQaLabCliAvailable: expect.any(Function),
|
||||
registerQaLabCli: expect.any(Function),
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects non-source package roots even when private QA is enabled", async () => {
|
||||
process.env.OPENCLAW_ENABLE_PRIVATE_QA_CLI = "1";
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-private-qa-"));
|
||||
tempDirs.push(root);
|
||||
fs.writeFileSync(path.join(root, "package.json"), JSON.stringify({ name: "openclaw" }), "utf8");
|
||||
const importModule = vi.fn(async () => ({}));
|
||||
|
||||
expect(() =>
|
||||
loadPrivateQaCliModule({
|
||||
resolvePackageRootSync: () => root,
|
||||
importModule,
|
||||
}),
|
||||
).toThrow("Private QA CLI is only available from an OpenClaw source checkout.");
|
||||
expect(importModule).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rejects when the private QA env flag is disabled", async () => {
|
||||
delete process.env.OPENCLAW_ENABLE_PRIVATE_QA_CLI;
|
||||
const importModule = vi.fn(async () => ({}));
|
||||
|
||||
expect(() => loadPrivateQaCliModule({ importModule })).toThrow(
|
||||
"Private QA CLI is only available from an OpenClaw source checkout.",
|
||||
);
|
||||
expect(importModule).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,65 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { resolveOpenClawPackageRootSync } from "../../infra/openclaw-root.js";
|
||||
|
||||
const PRIVATE_QA_DIST_RELATIVE_PATH = path.join("dist", "plugin-sdk", "qa-lab.js");
|
||||
|
||||
export function isPrivateQaCliEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
|
||||
return env.OPENCLAW_ENABLE_PRIVATE_QA_CLI === "1";
|
||||
}
|
||||
|
||||
export function loadPrivateQaCliModule(): Promise<Record<string, unknown>> {
|
||||
const specifier = "../../plugin-sdk/qa-lab.js";
|
||||
return import(specifier) as Promise<Record<string, unknown>>;
|
||||
function resolvePrivateQaSourceModuleSpecifier(params?: {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
cwd?: string;
|
||||
argv1?: string;
|
||||
moduleUrl?: string;
|
||||
resolvePackageRootSync?: typeof resolveOpenClawPackageRootSync;
|
||||
existsSync?: typeof fs.existsSync;
|
||||
}): string | null {
|
||||
const env = params?.env ?? process.env;
|
||||
if (!isPrivateQaCliEnabled(env)) {
|
||||
return null;
|
||||
}
|
||||
const resolvePackageRootSync = params?.resolvePackageRootSync ?? resolveOpenClawPackageRootSync;
|
||||
const packageRoot = resolvePackageRootSync({
|
||||
argv1: params?.argv1 ?? process.argv[1],
|
||||
cwd: params?.cwd ?? process.cwd(),
|
||||
moduleUrl: params?.moduleUrl ?? import.meta.url,
|
||||
});
|
||||
if (!packageRoot) {
|
||||
return null;
|
||||
}
|
||||
const existsSync = params?.existsSync ?? fs.existsSync;
|
||||
const sourceModulePath = path.join(packageRoot, PRIVATE_QA_DIST_RELATIVE_PATH);
|
||||
if (
|
||||
!existsSync(path.join(packageRoot, ".git")) ||
|
||||
!existsSync(path.join(packageRoot, "src")) ||
|
||||
!existsSync(sourceModulePath)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return pathToFileURL(sourceModulePath).href;
|
||||
}
|
||||
|
||||
async function dynamicImportPrivateQaCliModule(
|
||||
specifier: string,
|
||||
): Promise<Record<string, unknown>> {
|
||||
return (await import(specifier)) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
export function loadPrivateQaCliModule(params?: {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
cwd?: string;
|
||||
argv1?: string;
|
||||
moduleUrl?: string;
|
||||
resolvePackageRootSync?: typeof resolveOpenClawPackageRootSync;
|
||||
existsSync?: typeof fs.existsSync;
|
||||
importModule?: (specifier: string) => Promise<Record<string, unknown>>;
|
||||
}): Promise<Record<string, unknown>> {
|
||||
const specifier = resolvePrivateQaSourceModuleSpecifier(params);
|
||||
if (!specifier) {
|
||||
throw new Error("Private QA CLI is only available from an OpenClaw source checkout.");
|
||||
}
|
||||
return (params?.importModule ?? dynamicImportPrivateQaCliModule)(specifier);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ const { registerQaLabCli } = vi.hoisted(() => ({
|
||||
qa.command("run").action(() => undefined);
|
||||
}),
|
||||
}));
|
||||
const { loadPrivateQaCliModule } = vi.hoisted(() => ({
|
||||
loadPrivateQaCliModule: vi.fn(async () => ({ registerQaLabCli })),
|
||||
}));
|
||||
|
||||
const { inferAction, registerCapabilityCli } = vi.hoisted(() => {
|
||||
const action = vi.fn();
|
||||
@@ -37,7 +40,13 @@ const { inferAction, registerCapabilityCli } = vi.hoisted(() => {
|
||||
vi.mock("../acp-cli.js", () => ({ registerAcpCli }));
|
||||
vi.mock("../nodes-cli.js", () => ({ registerNodesCli }));
|
||||
vi.mock("../capability-cli.js", () => ({ registerCapabilityCli }));
|
||||
vi.mock("../../plugin-sdk/qa-lab.js", () => ({ registerQaLabCli }));
|
||||
vi.mock("./private-qa-cli.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("./private-qa-cli.js")>("./private-qa-cli.js");
|
||||
return {
|
||||
...actual,
|
||||
loadPrivateQaCliModule,
|
||||
};
|
||||
});
|
||||
|
||||
describe("registerSubCliCommands", () => {
|
||||
const originalArgv = process.argv;
|
||||
@@ -66,6 +75,7 @@ describe("registerSubCliCommands", () => {
|
||||
registerNodesCli.mockClear();
|
||||
nodesAction.mockClear();
|
||||
registerQaLabCli.mockClear();
|
||||
loadPrivateQaCliModule.mockClear();
|
||||
registerCapabilityCli.mockClear();
|
||||
inferAction.mockClear();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user