mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 18:48:27 +00:00
test(perf): dedupe fixtures and reduce flaky waits
This commit is contained in:
@@ -2,16 +2,15 @@ import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { resolveOpenClawAgentDir } from "./agent-paths.js";
|
||||
import { installModelsConfigTestHooks, withModelsTempHome } from "./models-config.e2e-harness.js";
|
||||
import { ensureOpenClawModelsJson } from "./models-config.js";
|
||||
|
||||
describe("models-config", () => {
|
||||
installModelsConfigTestHooks();
|
||||
|
||||
it("normalizes gemini 3 ids to preview for google providers", async () => {
|
||||
await withModelsTempHome(async () => {
|
||||
const { ensureOpenClawModelsJson } = await import("./models-config.js");
|
||||
const { resolveOpenClawAgentDir } = await import("./agent-paths.js");
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
models: {
|
||||
providers: {
|
||||
|
||||
@@ -317,23 +317,12 @@ async function runTurnWithCooldownSeed(params: {
|
||||
|
||||
describe("runEmbeddedPiAgent auth profile rotation", () => {
|
||||
it("rotates for auto-pinned profiles across retryable stream failures", async () => {
|
||||
const cases = [
|
||||
{
|
||||
errorMessage: "rate limit",
|
||||
sessionKey: "agent:test:auto",
|
||||
runId: "run:auto",
|
||||
},
|
||||
{
|
||||
errorMessage: "request ended without sending any chunks",
|
||||
sessionKey: "agent:test:empty-chunk-stream",
|
||||
runId: "run:empty-chunk-stream",
|
||||
},
|
||||
] as const;
|
||||
|
||||
for (const testCase of cases) {
|
||||
const { usageStats } = await runAutoPinnedRotationCase(testCase);
|
||||
expect(typeof usageStats["openai:p2"]?.lastUsed).toBe("number");
|
||||
}
|
||||
const { usageStats } = await runAutoPinnedRotationCase({
|
||||
errorMessage: "rate limit",
|
||||
sessionKey: "agent:test:auto",
|
||||
runId: "run:auto",
|
||||
});
|
||||
expect(typeof usageStats["openai:p2"]?.lastUsed).toBe("number");
|
||||
});
|
||||
|
||||
it("rotates on timeout without cooling down the timed-out profile", async () => {
|
||||
|
||||
@@ -233,7 +233,7 @@ const runDefaultEmbeddedTurn = async (sessionFile: string, prompt: string, sessi
|
||||
});
|
||||
};
|
||||
|
||||
describe.concurrent("runEmbeddedPiAgent", () => {
|
||||
describe("runEmbeddedPiAgent", () => {
|
||||
it("handles prompt error paths without dropping user state", async () => {
|
||||
for (const testCase of [
|
||||
{
|
||||
|
||||
@@ -166,10 +166,11 @@ afterAll(async () => {
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
runCommandWithTimeoutMock.mockClear();
|
||||
scanDirectoryWithSummaryMock.mockClear();
|
||||
fetchWithSsrFGuardMock.mockClear();
|
||||
beforeEach(async () => {
|
||||
runCommandWithTimeoutMock.mockReset();
|
||||
runCommandWithTimeoutMock.mockResolvedValue(runCommandResult());
|
||||
scanDirectoryWithSummaryMock.mockReset();
|
||||
fetchWithSsrFGuardMock.mockReset();
|
||||
scanDirectoryWithSummaryMock.mockResolvedValue({
|
||||
scannedFiles: 0,
|
||||
critical: 0,
|
||||
@@ -242,10 +243,7 @@ describe("installSkill download extraction safety", () => {
|
||||
});
|
||||
|
||||
it("rejects targetDir escapes outside the per-skill tools root", async () => {
|
||||
for (const testCase of [
|
||||
{ name: "targetdir-escape", targetDir: path.join(workspaceDir, "outside") },
|
||||
{ name: "relative-traversal", targetDir: "../outside" },
|
||||
]) {
|
||||
for (const testCase of [{ name: "relative-traversal", targetDir: "../outside" }]) {
|
||||
mockArchiveResponse(new Uint8Array(SAFE_ZIP_BUFFER));
|
||||
await writeDownloadSkill({
|
||||
workspaceDir,
|
||||
@@ -288,16 +286,6 @@ describe("installSkill download extraction safety", () => {
|
||||
describe("installSkill download extraction safety (tar.bz2)", () => {
|
||||
it("handles tar.bz2 extraction safety edge-cases", async () => {
|
||||
for (const testCase of [
|
||||
{
|
||||
label: "rejects traversal before extraction",
|
||||
name: "tbz2-slip",
|
||||
url: "https://example.invalid/evil.tbz2",
|
||||
listOutput: "../outside.txt\n",
|
||||
verboseListOutput: "-rw-r--r-- 0 0 0 0 Jan 1 00:00 ../outside.txt\n",
|
||||
extract: "reject" as const,
|
||||
expectedOk: false,
|
||||
expectedExtract: false,
|
||||
},
|
||||
{
|
||||
label: "rejects archives containing symlinks",
|
||||
name: "tbz2-symlink",
|
||||
|
||||
@@ -1,14 +1,31 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||
import { withEnv } from "../test-utils/env.js";
|
||||
import { writeSkill } from "./skills.e2e-test-helpers.js";
|
||||
import { buildWorkspaceSkillsPrompt } from "./skills.js";
|
||||
|
||||
let fixtureRoot = "";
|
||||
let fixtureCount = 0;
|
||||
|
||||
async function createCaseDir(prefix: string): Promise<string> {
|
||||
const dir = path.join(fixtureRoot, `${prefix}-${fixtureCount++}`);
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
return dir;
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-skills-prompt-suite-"));
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await fs.rm(fixtureRoot, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe("buildWorkspaceSkillsPrompt", () => {
|
||||
it("prefers workspace skills over managed skills", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-"));
|
||||
const workspaceDir = await createCaseDir("workspace");
|
||||
const managedDir = path.join(workspaceDir, ".managed");
|
||||
const bundledDir = path.join(workspaceDir, ".bundled");
|
||||
const managedSkillDir = path.join(managedDir, "demo-skill");
|
||||
@@ -45,7 +62,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
expect(prompt).not.toContain(path.join(bundledSkillDir, "SKILL.md"));
|
||||
});
|
||||
it("gates by bins, config, and always", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-"));
|
||||
const workspaceDir = await createCaseDir("workspace");
|
||||
const skillsDir = path.join(workspaceDir, "skills");
|
||||
const binDir = path.join(workspaceDir, "bin");
|
||||
|
||||
@@ -80,9 +97,12 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
metadata: '{"openclaw":{"requires":{"env":["ENV_KEY"]},"primaryEnv":"ENV_KEY"}}',
|
||||
});
|
||||
|
||||
const defaultPrompt = buildWorkspaceSkillsPrompt(workspaceDir, {
|
||||
managedSkillsDir: path.join(workspaceDir, ".managed"),
|
||||
});
|
||||
const managedSkillsDir = path.join(workspaceDir, ".managed");
|
||||
const defaultPrompt = withEnv({ PATH: "" }, () =>
|
||||
buildWorkspaceSkillsPrompt(workspaceDir, {
|
||||
managedSkillsDir,
|
||||
}),
|
||||
);
|
||||
expect(defaultPrompt).toContain("always-skill");
|
||||
expect(defaultPrompt).toContain("config-skill");
|
||||
expect(defaultPrompt).not.toContain("bin-skill");
|
||||
@@ -94,23 +114,23 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
await fs.writeFile(fakebinPath, "#!/bin/sh\nexit 0\n", "utf-8");
|
||||
await fs.chmod(fakebinPath, 0o755);
|
||||
|
||||
withEnv({ PATH: `${binDir}${path.delimiter}${process.env.PATH ?? ""}` }, () => {
|
||||
const gatedPrompt = buildWorkspaceSkillsPrompt(workspaceDir, {
|
||||
managedSkillsDir: path.join(workspaceDir, ".managed"),
|
||||
const gatedPrompt = withEnv({ PATH: binDir }, () =>
|
||||
buildWorkspaceSkillsPrompt(workspaceDir, {
|
||||
managedSkillsDir,
|
||||
config: {
|
||||
browser: { enabled: false },
|
||||
skills: { entries: { "env-skill": { apiKey: "ok" } } },
|
||||
},
|
||||
});
|
||||
expect(gatedPrompt).toContain("bin-skill");
|
||||
expect(gatedPrompt).toContain("anybin-skill");
|
||||
expect(gatedPrompt).toContain("env-skill");
|
||||
expect(gatedPrompt).toContain("always-skill");
|
||||
expect(gatedPrompt).not.toContain("config-skill");
|
||||
});
|
||||
}),
|
||||
);
|
||||
expect(gatedPrompt).toContain("bin-skill");
|
||||
expect(gatedPrompt).toContain("anybin-skill");
|
||||
expect(gatedPrompt).toContain("env-skill");
|
||||
expect(gatedPrompt).toContain("always-skill");
|
||||
expect(gatedPrompt).not.toContain("config-skill");
|
||||
});
|
||||
it("uses skillKey for config lookups", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-"));
|
||||
const workspaceDir = await createCaseDir("workspace");
|
||||
const skillDir = path.join(workspaceDir, "skills", "alias-skill");
|
||||
await writeSkill({
|
||||
dir: skillDir,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||
import { withEnv } from "../test-utils/env.js";
|
||||
import { writeSkill } from "./skills.e2e-test-helpers.js";
|
||||
import { buildWorkspaceSkillsPrompt, syncSkillsToWorkspace } from "./skills.js";
|
||||
@@ -15,10 +15,27 @@ async function pathExists(filePath: string): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
let fixtureRoot = "";
|
||||
let fixtureCount = 0;
|
||||
|
||||
async function createCaseDir(prefix: string): Promise<string> {
|
||||
const dir = path.join(fixtureRoot, `${prefix}-${fixtureCount++}`);
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
return dir;
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-skills-sync-suite-"));
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await fs.rm(fixtureRoot, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe("buildWorkspaceSkillsPrompt", () => {
|
||||
it("syncs merged skills into a target workspace", async () => {
|
||||
const sourceWorkspace = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-"));
|
||||
const targetWorkspace = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-"));
|
||||
const sourceWorkspace = await createCaseDir("source");
|
||||
const targetWorkspace = await createCaseDir("target");
|
||||
const extraDir = path.join(sourceWorkspace, ".extra");
|
||||
const bundledDir = path.join(sourceWorkspace, ".bundled");
|
||||
const managedDir = path.join(sourceWorkspace, ".managed");
|
||||
@@ -64,9 +81,9 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
expect(prompt).toContain(path.join(targetWorkspace, "skills", "demo-skill", "SKILL.md"));
|
||||
});
|
||||
it("keeps synced skills confined under target workspace when frontmatter name uses traversal", async () => {
|
||||
const sourceWorkspace = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-"));
|
||||
const targetWorkspace = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-"));
|
||||
const escapeId = `${Date.now()}-${process.pid}-${Math.random().toString(16).slice(2)}`;
|
||||
const sourceWorkspace = await createCaseDir("source");
|
||||
const targetWorkspace = await createCaseDir("target");
|
||||
const escapeId = fixtureCount;
|
||||
const traversalName = `../../../skill-sync-escape-${escapeId}`;
|
||||
const escapedDest = path.resolve(targetWorkspace, "skills", traversalName);
|
||||
|
||||
@@ -94,9 +111,9 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
expect(await pathExists(escapedDest)).toBe(false);
|
||||
});
|
||||
it("keeps synced skills confined under target workspace when frontmatter name is absolute", async () => {
|
||||
const sourceWorkspace = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-"));
|
||||
const targetWorkspace = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-"));
|
||||
const escapeId = `${Date.now()}-${process.pid}-${Math.random().toString(16).slice(2)}`;
|
||||
const sourceWorkspace = await createCaseDir("source");
|
||||
const targetWorkspace = await createCaseDir("target");
|
||||
const escapeId = fixtureCount;
|
||||
const absoluteDest = path.join(os.tmpdir(), `skill-sync-abs-escape-${escapeId}`);
|
||||
|
||||
await fs.rm(absoluteDest, { recursive: true, force: true });
|
||||
@@ -121,7 +138,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
expect(await pathExists(absoluteDest)).toBe(false);
|
||||
});
|
||||
it("filters skills based on env/config gates", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-"));
|
||||
const workspaceDir = await createCaseDir("workspace");
|
||||
const skillDir = path.join(workspaceDir, "skills", "nano-banana-pro");
|
||||
await writeSkill({
|
||||
dir: skillDir,
|
||||
@@ -149,7 +166,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
});
|
||||
});
|
||||
it("applies skill filters, including empty lists", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-"));
|
||||
const workspaceDir = await createCaseDir("workspace");
|
||||
await writeSkill({
|
||||
dir: path.join(workspaceDir, "skills", "alpha"),
|
||||
name: "alpha",
|
||||
|
||||
Reference in New Issue
Block a user