mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-16 19:40:45 +00:00
test: tighten assertions and harness coverage
This commit is contained in:
@@ -236,7 +236,7 @@ describe("barnacle-auto-response", () => {
|
||||
expect(managedLabelSpecs[PROOF_SUFFICIENT_LABEL].color).toBe("0E8A16");
|
||||
|
||||
for (const label of Object.values(candidateLabels)) {
|
||||
expect(managedLabelSpecs[label]).toBeDefined();
|
||||
expect(managedLabelSpecs).toHaveProperty(label);
|
||||
expect(managedLabelSpecs[label].description).toMatch(/^Candidate:/);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -12,6 +12,14 @@ import {
|
||||
writeBuildAllStepCacheStamp,
|
||||
} from "../../scripts/build-all.mjs";
|
||||
|
||||
function getBuildAllStep(label: string) {
|
||||
const step = BUILD_ALL_STEPS.find((entry) => entry.label === label);
|
||||
if (!step) {
|
||||
throw new Error(`Missing build-all step ${label}`);
|
||||
}
|
||||
return step;
|
||||
}
|
||||
|
||||
function withBuildCacheFixture(
|
||||
run: (fixture: {
|
||||
rootDir: string;
|
||||
@@ -53,8 +61,7 @@ function withBuildCacheFixture(
|
||||
|
||||
describe("resolveBuildAllStep", () => {
|
||||
it("routes pnpm steps through the npm_execpath pnpm runner on Windows", () => {
|
||||
const step = BUILD_ALL_STEPS.find((entry) => entry.label === "plugins:assets:build");
|
||||
expect(step).toBeTruthy();
|
||||
const step = getBuildAllStep("plugins:assets:build");
|
||||
|
||||
const result = resolveBuildAllStep(step, {
|
||||
platform: "win32",
|
||||
@@ -76,8 +83,7 @@ describe("resolveBuildAllStep", () => {
|
||||
});
|
||||
|
||||
it("keeps node steps on the current node binary", () => {
|
||||
const step = BUILD_ALL_STEPS.find((entry) => entry.label === "runtime-postbuild");
|
||||
expect(step).toBeTruthy();
|
||||
const step = getBuildAllStep("runtime-postbuild");
|
||||
|
||||
const result = resolveBuildAllStep(step, {
|
||||
nodeExecPath: "/custom/node",
|
||||
@@ -95,8 +101,7 @@ describe("resolveBuildAllStep", () => {
|
||||
});
|
||||
|
||||
it("adds heap headroom for plugin-sdk dts on Windows", () => {
|
||||
const step = BUILD_ALL_STEPS.find((entry) => entry.label === "build:plugin-sdk:dts");
|
||||
expect(step).toBeTruthy();
|
||||
const step = getBuildAllStep("build:plugin-sdk:dts");
|
||||
|
||||
const result = resolveBuildAllStep(step, {
|
||||
platform: "win32",
|
||||
@@ -168,15 +173,13 @@ describe("resolveBuildAllSteps", () => {
|
||||
});
|
||||
|
||||
it("does not cache plugin-sdk entry shims over compiled JS", () => {
|
||||
const step = BUILD_ALL_STEPS.find((entry) => entry.label === "write-plugin-sdk-entry-dts");
|
||||
expect(step).toBeTruthy();
|
||||
expect(step?.cache).toBeUndefined();
|
||||
const step = getBuildAllStep("write-plugin-sdk-entry-dts");
|
||||
expect(step.cache).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not cache hook metadata over compiled hook handlers", () => {
|
||||
const step = BUILD_ALL_STEPS.find((entry) => entry.label === "copy-hook-metadata");
|
||||
expect(step).toBeTruthy();
|
||||
expect(step?.cache).toBeUndefined();
|
||||
const step = getBuildAllStep("copy-hook-metadata");
|
||||
expect(step.cache).toBeUndefined();
|
||||
});
|
||||
|
||||
it("rejects unknown build profiles", () => {
|
||||
|
||||
@@ -12,8 +12,10 @@ function extractFunctionBody(source: string, name: string): string {
|
||||
const match = source.match(
|
||||
new RegExp(`^function ${name} \\{\\r?\\n([\\s\\S]*?)^\\}\\r?\\n`, "m"),
|
||||
);
|
||||
expect(match?.[1]).toBeDefined();
|
||||
return match![1];
|
||||
if (match?.[1] === undefined) {
|
||||
throw new Error(`Missing PowerShell function body ${name}`);
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function findPowerShell(): string | undefined {
|
||||
|
||||
@@ -215,13 +215,13 @@ printf 'BBBBB22222\\t0\\tBeta Team\\r\\n'`,
|
||||
expect(fallback).toBe("BBBBB22222");
|
||||
});
|
||||
|
||||
it("resolves a fallback team ID from Xcode team listings (smoke)", async () => {
|
||||
it("resolves a fallback team ID from Xcode team listings (smoke)", () => {
|
||||
const fallbackResult = runScript(sharedHomeDir, { IOS_PYTHON_BIN: sharedFakePythonPath });
|
||||
expect(fallbackResult.ok).toBe(true);
|
||||
expect(fallbackResult.stdout).toBe("AAAAA11111");
|
||||
});
|
||||
|
||||
it("prints actionable guidance when Xcode account exists but no Team ID is resolvable", async () => {
|
||||
it("prints actionable guidance when Xcode account exists but no Team ID is resolvable", () => {
|
||||
const result = runScript(sharedHomeDir);
|
||||
expect(result.ok).toBe(false);
|
||||
expect(
|
||||
|
||||
@@ -39,18 +39,24 @@ function readWorkflow(path: string): Workflow {
|
||||
|
||||
function workflowJob(path: string, jobName: string): WorkflowJob {
|
||||
const job = readWorkflow(path).jobs?.[jobName];
|
||||
expect(job, `expected workflow job ${jobName}`).toBeDefined();
|
||||
return job!;
|
||||
if (!job) {
|
||||
throw new Error(`Expected workflow job ${jobName} in ${path}`);
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
function workflowStep(job: WorkflowJob, stepName: string): WorkflowStep {
|
||||
const step = job.steps?.find((candidate) => candidate.name === stepName);
|
||||
expect(step, `expected workflow step ${stepName}`).toBeDefined();
|
||||
return step!;
|
||||
if (!step) {
|
||||
throw new Error(`Expected workflow step ${stepName}`);
|
||||
}
|
||||
return step;
|
||||
}
|
||||
|
||||
function expectTextToIncludeAll(text: string | undefined, snippets: string[]): void {
|
||||
expect(text).toBeDefined();
|
||||
if (text === undefined) {
|
||||
throw new Error("Expected text to be defined before checking snippets");
|
||||
}
|
||||
for (const snippet of snippets) {
|
||||
expect(text).toContain(snippet);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,14 @@ function readPluginPrereleaseWorkflow() {
|
||||
return parse(readFileSync(".github/workflows/plugin-prerelease.yml", "utf8"));
|
||||
}
|
||||
|
||||
function getDockerLane(name: string) {
|
||||
const lane = findLaneByName(name);
|
||||
if (!lane) {
|
||||
throw new Error(`Missing Docker E2E lane ${name}`);
|
||||
}
|
||||
return lane;
|
||||
}
|
||||
|
||||
describe("scripts/lib/plugin-prerelease-test-plan.mjs", () => {
|
||||
it("covers every pre-release plugin skill surface in the plugin prerelease plan", () => {
|
||||
const plan = assertPluginPrereleaseTestPlanComplete();
|
||||
@@ -55,7 +63,7 @@ describe("scripts/lib/plugin-prerelease-test-plan.mjs", () => {
|
||||
]);
|
||||
|
||||
for (const lane of plan.dockerLanes) {
|
||||
expect(findLaneByName(lane), lane).toBeTruthy();
|
||||
expect(getDockerLane(lane).name).toBe(lane);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -83,7 +91,7 @@ describe("scripts/lib/plugin-prerelease-test-plan.mjs", () => {
|
||||
});
|
||||
|
||||
it("uses kitchen-sink npm and ClawHub scenarios as the registry install canary", () => {
|
||||
const lane = findLaneByName("kitchen-sink-plugin");
|
||||
const lane = getDockerLane("kitchen-sink-plugin");
|
||||
const script = readFileSync("scripts/e2e/kitchen-sink-plugin-docker.sh", "utf8");
|
||||
const sweepScript = readFileSync("scripts/e2e/lib/kitchen-sink-plugin/sweep.sh", "utf8");
|
||||
const assertionsScript = readFileSync(
|
||||
@@ -153,7 +161,7 @@ describe("scripts/lib/plugin-prerelease-test-plan.mjs", () => {
|
||||
});
|
||||
|
||||
it("keeps the generic plugin Docker lane as an external install contract canary", () => {
|
||||
const lane = findLaneByName("plugins");
|
||||
const lane = getDockerLane("plugins");
|
||||
const sweepScript = readFileSync("scripts/e2e/lib/plugins/sweep.sh", "utf8");
|
||||
const clawhubScript = readFileSync("scripts/e2e/lib/plugins/clawhub.sh", "utf8");
|
||||
const assertionsScript = readFileSync("scripts/e2e/lib/plugins/assertions.mjs", "utf8");
|
||||
|
||||
@@ -26,6 +26,10 @@ async function createExtensionsDir() {
|
||||
return extensionsDir;
|
||||
}
|
||||
|
||||
async function expectPathExists(filePath: string) {
|
||||
await expect(fs.access(filePath)).resolves.toBeUndefined();
|
||||
}
|
||||
|
||||
async function writePluginPackage(
|
||||
extensionsDir: string,
|
||||
pluginId: string,
|
||||
@@ -284,7 +288,7 @@ describe("bundled plugin postinstall", () => {
|
||||
log: { log: vi.fn(), warn: vi.fn() },
|
||||
});
|
||||
|
||||
await expect(fs.stat(legacyRuntimeRoot)).resolves.toBeTruthy();
|
||||
await expectPathExists(legacyRuntimeRoot);
|
||||
});
|
||||
|
||||
it("honors disable env before source-checkout pruning", async () => {
|
||||
@@ -301,7 +305,7 @@ describe("bundled plugin postinstall", () => {
|
||||
log: { log: vi.fn(), warn: vi.fn() },
|
||||
});
|
||||
|
||||
await expect(fs.stat(path.join(extensionsDir, "acpx", "node_modules"))).resolves.toBeTruthy();
|
||||
await expectPathExists(path.join(extensionsDir, "acpx", "node_modules"));
|
||||
});
|
||||
|
||||
it("migrates the plugin registry during postinstall from built dist contracts", async () => {
|
||||
@@ -448,7 +452,7 @@ describe("bundled plugin postinstall", () => {
|
||||
}),
|
||||
).toEqual(["dist/channel-CJUAgRQR.js"]);
|
||||
|
||||
await expect(fs.stat(currentFile)).resolves.toBeTruthy();
|
||||
await expectPathExists(currentFile);
|
||||
await expect(fs.stat(staleFile)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
});
|
||||
|
||||
@@ -515,7 +519,7 @@ describe("bundled plugin postinstall", () => {
|
||||
await expect(fs.stat(overrideLegacyRoot)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expect(fs.stat(systemLegacyRoot)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expect(fs.lstat(legacySymlink)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expect(fs.stat(thirdPartyNodeModules)).resolves.toBeTruthy();
|
||||
await expectPathExists(thirdPartyNodeModules);
|
||||
expect(log.warn).not.toHaveBeenCalled();
|
||||
expect(log.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("[postinstall] pruned legacy plugin runtime deps:"),
|
||||
@@ -620,7 +624,7 @@ describe("bundled plugin postinstall", () => {
|
||||
}),
|
||||
).toEqual(["dist/memory-state-old.js"]);
|
||||
|
||||
await expect(fs.stat(importedChunk)).resolves.toBeTruthy();
|
||||
await expectPathExists(importedChunk);
|
||||
await expect(fs.stat(staleFile)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
});
|
||||
|
||||
@@ -704,7 +708,7 @@ describe("bundled plugin postinstall", () => {
|
||||
}),
|
||||
).not.toThrow();
|
||||
|
||||
await expect(fs.stat(staleFile)).resolves.toBeTruthy();
|
||||
await expectPathExists(staleFile);
|
||||
expect(warn).toHaveBeenCalledWith(
|
||||
"[postinstall] skipping dist prune: missing dist inventory: dist/postinstall-inventory.json",
|
||||
);
|
||||
@@ -726,7 +730,7 @@ describe("bundled plugin postinstall", () => {
|
||||
}),
|
||||
).not.toThrow();
|
||||
|
||||
await expect(fs.stat(currentFile)).resolves.toBeTruthy();
|
||||
await expectPathExists(currentFile);
|
||||
expect(warn).toHaveBeenCalledWith(
|
||||
"[postinstall] skipping dist prune: invalid dist inventory: dist/postinstall-inventory.json",
|
||||
);
|
||||
@@ -898,9 +902,7 @@ describe("bundled plugin postinstall", () => {
|
||||
await expect(fs.stat(path.join(extensionsDir, "acpx", "node_modules"))).rejects.toMatchObject({
|
||||
code: "ENOENT",
|
||||
});
|
||||
await expect(
|
||||
fs.stat(path.join(extensionsDir, "fixtures", "node_modules")),
|
||||
).resolves.toBeTruthy();
|
||||
await expectPathExists(path.join(extensionsDir, "fixtures", "node_modules"));
|
||||
});
|
||||
|
||||
it("skips symlink entries when pruning source-checkout bundled plugin node_modules", () => {
|
||||
|
||||
@@ -23,7 +23,7 @@ describe("root package override guardrails", () => {
|
||||
const pnpmOverride = manifest.pnpm?.overrides?.["@aws-sdk/client-bedrock-runtime"];
|
||||
|
||||
expect(pnpmOverride).toBe("3.1024.0");
|
||||
expect(manifest.dependencies?.[packageName]).toBeDefined();
|
||||
expect(manifest.dependencies).toHaveProperty(packageName);
|
||||
expect(npmOverride).toBe(`$${packageName}`);
|
||||
});
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ describe("runtime postbuild static assets", () => {
|
||||
expect(await fs.readFile(destPath, "utf8")).toBe("proxy-data\n");
|
||||
});
|
||||
|
||||
it("warns when a declared static asset is missing", async () => {
|
||||
it("warns when a declared static asset is missing", () => {
|
||||
const rootDir = createTempDir("openclaw-runtime-postbuild-");
|
||||
const warn = vi.fn();
|
||||
|
||||
|
||||
@@ -32,8 +32,10 @@ function findExtensionWithoutTests() {
|
||||
(candidate) => !resolveExtensionTestPlan({ targetArg: candidate, cwd: process.cwd() }).hasTests,
|
||||
);
|
||||
|
||||
expect(extensionId).toBeDefined();
|
||||
return extensionId ?? "missing-no-test-extension";
|
||||
if (!extensionId) {
|
||||
throw new Error("Expected at least one extension without tests");
|
||||
}
|
||||
return extensionId;
|
||||
}
|
||||
|
||||
describe("scripts/test-extension.mjs", () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
collectVitestFileDurations,
|
||||
normalizeTrackedRepoPath,
|
||||
runVitestJsonReport,
|
||||
tryReadJsonFile,
|
||||
} from "../../scripts/test-report-utils.mjs";
|
||||
|
||||
@@ -84,14 +85,12 @@ describe("scripts/test-report-utils tryReadJsonFile", () => {
|
||||
|
||||
describe("scripts/test-report-utils runVitestJsonReport", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
spawnSyncMock.mockReset();
|
||||
});
|
||||
|
||||
it("launches Vitest through pnpm exec", async () => {
|
||||
spawnSyncMock.mockReturnValue({ status: 0 });
|
||||
const reportPath = path.join(os.tmpdir(), `openclaw-vitest-json-${Date.now()}.json`);
|
||||
const { runVitestJsonReport } = await import("../../scripts/test-report-utils.mjs");
|
||||
|
||||
expect(
|
||||
runVitestJsonReport({
|
||||
|
||||
@@ -29,9 +29,9 @@ describe("scripts/ui windows spawn behavior", () => {
|
||||
});
|
||||
|
||||
it("allows safe forwarded args when shell mode is required on Windows", () => {
|
||||
expect(() =>
|
||||
expect(
|
||||
assertSafeWindowsShellArgs(["run", "build", "--filter", "@openclaw/ui"], "win32"),
|
||||
).not.toThrow();
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("rejects dangerous forwarded args when shell mode is required on Windows", () => {
|
||||
@@ -44,6 +44,6 @@ describe("scripts/ui windows spawn behavior", () => {
|
||||
});
|
||||
|
||||
it("does not reject args on non-windows platforms", () => {
|
||||
expect(() => assertSafeWindowsShellArgs(["contains&metacharacters"], "linux")).not.toThrow();
|
||||
expect(assertSafeWindowsShellArgs(["contains&metacharacters"], "linux")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user