mirror of
https://github.com/nexu-io/open-design.git
synced 2026-09-20 06:15:06 +08:00
4b48f94b7c
* refactor(ci): align tools-pack sources with scope boundaries * refactor(tools-pack): unify test source imports * fix(tools-pack): resolve workspace root from package Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * test(tools-pack): exercise bundled CLI directly --------- Co-authored-by: Looper <looper@noreply.github.com>
724 lines
25 KiB
TypeScript
724 lines
25 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
access,
|
|
chmod,
|
|
constants,
|
|
mkdir,
|
|
mkdtemp,
|
|
readFile,
|
|
rm,
|
|
stat,
|
|
writeFile,
|
|
} from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join } from "node:path";
|
|
import process from "node:process";
|
|
|
|
import { domToPptxBundleResource } from "@/dom-to-pptx-resource.js";
|
|
import { copyBundledResourceTrees } from "@/resources/index.js";
|
|
import { copyOptionalVelaCliBinary, resolveOptionalVelaCliBinary } from "@/vela-cli.js";
|
|
|
|
async function writeFakeOpenCodeCompanion(
|
|
source: string,
|
|
content = "#!/bin/sh\nexit 0\n",
|
|
): Promise<string> {
|
|
const companion = join(dirname(source), "libexec", "opencode", "opencode");
|
|
await mkdir(dirname(companion), { recursive: true });
|
|
await writeFile(companion, content, "utf8");
|
|
await chmod(companion, 0o755);
|
|
return companion;
|
|
}
|
|
|
|
async function pinnedVelaCliVersion(): Promise<string> {
|
|
const manifest = JSON.parse(
|
|
await readFile(new URL("../../package.json", import.meta.url), "utf8"),
|
|
) as {
|
|
optionalDependencies?: Record<string, string>;
|
|
};
|
|
const version = manifest.optionalDependencies?.["@powerformer/vela-cli"];
|
|
if (!version) throw new Error("missing @powerformer/vela-cli pin");
|
|
return version;
|
|
}
|
|
|
|
/**
|
|
* Help output as current Vela prints it: `--authorize-only` made `stageDir`
|
|
* optional, so the usage line reads `[stageDir]`. Every flag the daemon drives
|
|
* is still there — the capability is unchanged, only the usage shape moved.
|
|
*/
|
|
async function velaCliCommandWithOptionalStageDir(
|
|
_source: string,
|
|
args: readonly string[],
|
|
): Promise<{ stderr: string; stdout: string }> {
|
|
if (args[0] === "--version") {
|
|
return { stderr: "", stdout: `${await pinnedVelaCliVersion()}\n` };
|
|
}
|
|
if (args[0] === "billing") {
|
|
return {
|
|
stderr: "",
|
|
stdout: [
|
|
"Usage:",
|
|
" vela billing workspace-snapshot [flags]",
|
|
" --workspace-id string",
|
|
" --format string",
|
|
].join("\n"),
|
|
};
|
|
}
|
|
return {
|
|
stderr: "",
|
|
stdout: [
|
|
"Usage:",
|
|
" vela team-projects pull <projectId> [stageDir] [flags]",
|
|
" --authorize-only",
|
|
" --expected-version int",
|
|
" --live-dir string",
|
|
" --ref string",
|
|
" --json",
|
|
].join("\n"),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* A CLI whose pull usage dropped the staging-directory positional entirely.
|
|
* The daemon always passes it, so packaging must refuse this binary rather
|
|
* than ship one that rejects the invocation at runtime.
|
|
*/
|
|
async function velaCliCommandWithoutStageDir(
|
|
_source: string,
|
|
args: readonly string[],
|
|
): Promise<{ stderr: string; stdout: string }> {
|
|
if (args[0] === "--version") {
|
|
return { stderr: "", stdout: `${await pinnedVelaCliVersion()}\n` };
|
|
}
|
|
if (args[0] === "billing") {
|
|
return {
|
|
stderr: "",
|
|
stdout: [
|
|
"Usage:",
|
|
" vela billing workspace-snapshot [flags]",
|
|
" --workspace-id string",
|
|
" --format string",
|
|
].join("\n"),
|
|
};
|
|
}
|
|
return {
|
|
stderr: "",
|
|
stdout: [
|
|
"Usage:",
|
|
" vela team-projects pull <projectId> [flags]",
|
|
" --expected-version int",
|
|
" --live-dir string",
|
|
" --ref string",
|
|
" --json",
|
|
].join("\n"),
|
|
};
|
|
}
|
|
|
|
async function matchingVelaCliCommand(
|
|
_binary: string,
|
|
args: readonly string[],
|
|
): Promise<{ stderr: string; stdout: string }> {
|
|
if (args[0] === "--version") {
|
|
return { stderr: "", stdout: `${await pinnedVelaCliVersion()}\n` };
|
|
}
|
|
if (args[0] === "billing") {
|
|
return {
|
|
stderr: "",
|
|
stdout: [
|
|
"Usage:",
|
|
" vela billing workspace-snapshot [flags]",
|
|
" --workspace-id string",
|
|
" --format string",
|
|
].join("\n"),
|
|
};
|
|
}
|
|
return {
|
|
stderr: "",
|
|
stdout: [
|
|
"Usage:",
|
|
" vela team-projects pull <projectId> <stageDir> [flags]",
|
|
" --expected-version int",
|
|
" --live-dir string",
|
|
" --ref string",
|
|
" --json",
|
|
].join("\n"),
|
|
};
|
|
}
|
|
|
|
describe("domToPptxBundleResource", () => {
|
|
it("derives the vendored bundle path from the workspace root, not the caller cwd", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-resource-"));
|
|
const workspaceRoot = join(root, "workspace");
|
|
const callerCwd = join(root, "caller");
|
|
const previousCwd = process.cwd();
|
|
|
|
try {
|
|
await mkdir(workspaceRoot, { recursive: true });
|
|
await mkdir(callerCwd, { recursive: true });
|
|
process.chdir(callerCwd);
|
|
|
|
expect(domToPptxBundleResource({ workspaceRoot })).toEqual({
|
|
from: join(workspaceRoot, "apps", "desktop", "vendor", "dom-to-pptx", "dom-to-pptx.bundle.js.gz"),
|
|
to: "dom-to-pptx.bundle.js.gz",
|
|
});
|
|
} finally {
|
|
process.chdir(previousCwd);
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("copyBundledResourceTrees", () => {
|
|
it("includes daemon resources and the packaged Website Clone main path", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-"));
|
|
const workspaceRoot = join(root, "workspace");
|
|
const resourceRoot = join(root, "resources");
|
|
|
|
try {
|
|
const promptTemplatePath = join(
|
|
workspaceRoot,
|
|
"prompt-templates",
|
|
"image",
|
|
"sample.json",
|
|
);
|
|
const designTemplatePath = join(
|
|
workspaceRoot,
|
|
"design-templates",
|
|
"orbit-general",
|
|
"SKILL.md",
|
|
);
|
|
const communityPetPath = join(
|
|
workspaceRoot,
|
|
"assets",
|
|
"community-pets",
|
|
"sample",
|
|
"pet.json",
|
|
);
|
|
const communityRegistryPath = join(
|
|
workspaceRoot,
|
|
"plugins",
|
|
"registry",
|
|
"community",
|
|
"open-design-marketplace.json",
|
|
);
|
|
const webCloneSkillPath = join(
|
|
workspaceRoot,
|
|
"skills",
|
|
"web-clone",
|
|
"SKILL.md",
|
|
);
|
|
const webCloneBrowserRuntimePath = join(
|
|
workspaceRoot,
|
|
"skills",
|
|
"web-clone",
|
|
"scripts",
|
|
"lib",
|
|
"system-browser.mjs",
|
|
);
|
|
await mkdir(join(workspaceRoot, "skills", "sample"), { recursive: true });
|
|
// Packaged Website Clone runs this staged skill from both the UI and the
|
|
// bundled `od` CLI. Keep the skill and its zero-dependency CDP runtime in
|
|
// the fixture: losing either file would break a primary product path and
|
|
// tempt the agent to install Playwright into the user's project again.
|
|
await mkdir(dirname(webCloneBrowserRuntimePath), { recursive: true });
|
|
// The skills/design-templates split (see specs/current/
|
|
// skills-and-design-templates.md) added a separate top-level
|
|
// `design-templates/` tree that copyBundledResourceTrees now also
|
|
// bundles. Create it in the fixture so the recursive copy does not
|
|
// fail with ENOENT before reaching the prompt-templates assertion.
|
|
await mkdir(join(workspaceRoot, "design-templates", "orbit-general"), {
|
|
recursive: true,
|
|
});
|
|
await mkdir(join(workspaceRoot, "design-systems", "sample"), {
|
|
recursive: true,
|
|
});
|
|
await mkdir(join(workspaceRoot, "craft", "sample"), { recursive: true });
|
|
await mkdir(join(workspaceRoot, "plugins", "_official", "sample"), {
|
|
recursive: true,
|
|
});
|
|
await mkdir(join(workspaceRoot, "plugins", "registry", "community"), {
|
|
recursive: true,
|
|
});
|
|
await mkdir(join(workspaceRoot, "assets", "frames"), { recursive: true });
|
|
await mkdir(join(workspaceRoot, "assets", "community-pets", "sample"), {
|
|
recursive: true,
|
|
});
|
|
await mkdir(join(workspaceRoot, "prompt-templates", "image"), {
|
|
recursive: true,
|
|
});
|
|
await mkdir(join(workspaceRoot, "data", "plugin-previews"), {
|
|
recursive: true,
|
|
});
|
|
await writeFile(promptTemplatePath, "{\"id\":\"sample\"}\n", "utf8");
|
|
await writeFile(webCloneSkillPath, "# Website Clone\n", "utf8");
|
|
await writeFile(
|
|
webCloneBrowserRuntimePath,
|
|
"export const systemChromium = {};\n",
|
|
"utf8",
|
|
);
|
|
await writeFile(
|
|
join(workspaceRoot, "data", "plugin-previews", "manifest.json"),
|
|
"{\"previews\":{}}\n",
|
|
"utf8",
|
|
);
|
|
await writeFile(designTemplatePath, "# Orbit General\n", "utf8");
|
|
await writeFile(communityPetPath, "{\"name\":\"sample\"}\n", "utf8");
|
|
await writeFile(
|
|
join(workspaceRoot, "plugins", "_official", "sample", "open-design.json"),
|
|
"{\"id\":\"sample\"}\n",
|
|
"utf8",
|
|
);
|
|
await writeFile(communityRegistryPath, "{\"plugins\":[]}\n", "utf8");
|
|
|
|
await copyBundledResourceTrees({ workspaceRoot, resourceRoot });
|
|
|
|
await expect(
|
|
readFile(join(resourceRoot, "skills", "web-clone", "SKILL.md"), "utf8"),
|
|
).resolves.toBe("# Website Clone\n");
|
|
await expect(
|
|
readFile(
|
|
join(
|
|
resourceRoot,
|
|
"skills",
|
|
"web-clone",
|
|
"scripts",
|
|
"lib",
|
|
"system-browser.mjs",
|
|
),
|
|
"utf8",
|
|
),
|
|
).resolves.toBe("export const systemChromium = {};\n");
|
|
|
|
await expect(
|
|
readFile(
|
|
join(resourceRoot, "prompt-templates", "image", "sample.json"),
|
|
"utf8",
|
|
),
|
|
).resolves.toBe("{\"id\":\"sample\"}\n");
|
|
// The baked plugin-preview manifest must land under data/plugin-previews so
|
|
// the packaged daemon can map plugins to their R2 clips; without it the
|
|
// gallery silently falls back to live iframes.
|
|
await expect(
|
|
readFile(
|
|
join(resourceRoot, "data", "plugin-previews", "manifest.json"),
|
|
"utf8",
|
|
),
|
|
).resolves.toBe("{\"previews\":{}}\n");
|
|
await expect(
|
|
readFile(
|
|
join(resourceRoot, "design-templates", "orbit-general", "SKILL.md"),
|
|
"utf8",
|
|
),
|
|
).resolves.toBe("# Orbit General\n");
|
|
await expect(
|
|
readFile(
|
|
join(resourceRoot, "community-pets", "sample", "pet.json"),
|
|
"utf8",
|
|
),
|
|
).resolves.toBe("{\"name\":\"sample\"}\n");
|
|
await expect(
|
|
readFile(
|
|
join(resourceRoot, "plugins", "_official", "sample", "open-design.json"),
|
|
"utf8",
|
|
),
|
|
).resolves.toBe("{\"id\":\"sample\"}\n");
|
|
await expect(
|
|
readFile(
|
|
join(
|
|
resourceRoot,
|
|
"plugins",
|
|
"registry",
|
|
"community",
|
|
"open-design-marketplace.json",
|
|
),
|
|
"utf8",
|
|
),
|
|
).resolves.toBe("{\"plugins\":[]}\n");
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("copyOptionalVelaCliBinary", () => {
|
|
it("rejects a strict build when the Vela CLI version does not match the package pin", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-vela-version-"));
|
|
const source = join(root, "source", "vela");
|
|
const resourceRoot = join(root, "resources", "open-design");
|
|
|
|
try {
|
|
await mkdir(join(root, "source"), { recursive: true });
|
|
await writeFile(source, "#!/bin/sh\nexit 0\n", "utf8");
|
|
await writeFakeOpenCodeCompanion(source);
|
|
|
|
await expect(
|
|
copyOptionalVelaCliBinary({
|
|
env: { OPEN_DESIGN_VELA_CLI_BIN: source },
|
|
platform: "mac",
|
|
requireBundled: true,
|
|
resourceRoot,
|
|
runCommand: async () => ({
|
|
stderr: "",
|
|
stdout: "0.0.0-test.0\n",
|
|
}),
|
|
}),
|
|
).rejects.toThrow(/Vela CLI version.*package pin/i);
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("rejects a strict build when Vela lacks authorized staged pulls", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-vela-capability-"));
|
|
const source = join(root, "source", "vela");
|
|
const resourceRoot = join(root, "resources", "open-design");
|
|
const expectedVersion = await pinnedVelaCliVersion();
|
|
|
|
try {
|
|
await mkdir(join(root, "source"), { recursive: true });
|
|
await writeFile(source, "#!/bin/sh\nexit 0\n", "utf8");
|
|
await writeFakeOpenCodeCompanion(source);
|
|
|
|
await expect(
|
|
copyOptionalVelaCliBinary({
|
|
env: { OPEN_DESIGN_VELA_CLI_BIN: source },
|
|
platform: "mac",
|
|
requireBundled: true,
|
|
resourceRoot,
|
|
runCommand: async (_binary, args) => {
|
|
if (args[0] === "--version") {
|
|
return { stderr: "", stdout: `${expectedVersion}\n` };
|
|
}
|
|
throw new Error('unknown command "pull" for "vela team-projects"');
|
|
},
|
|
}),
|
|
).rejects.toThrow(/authorized staged pull/i);
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("rejects a strict build when Vela lacks workspace billing snapshots", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-vela-billing-"));
|
|
const source = join(root, "source", "vela");
|
|
const resourceRoot = join(root, "resources", "open-design");
|
|
const expectedVersion = await pinnedVelaCliVersion();
|
|
|
|
try {
|
|
await mkdir(join(root, "source"), { recursive: true });
|
|
await writeFile(source, "#!/bin/sh\nexit 0\n", "utf8");
|
|
await writeFakeOpenCodeCompanion(source);
|
|
|
|
await expect(
|
|
copyOptionalVelaCliBinary({
|
|
env: { OPEN_DESIGN_VELA_CLI_BIN: source },
|
|
platform: "mac",
|
|
requireBundled: true,
|
|
resourceRoot,
|
|
runCommand: async (_binary, args) => {
|
|
if (args[0] === "--version") {
|
|
return { stderr: "", stdout: `${expectedVersion}\n` };
|
|
}
|
|
if (args[0] === "team-projects") {
|
|
return matchingVelaCliCommand(source, args);
|
|
}
|
|
throw new Error('unknown command "workspace-snapshot" for "vela billing"');
|
|
},
|
|
}),
|
|
).rejects.toThrow(/workspace billing snapshot/i);
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("copies the installed Vela CLI through the default npm resolver", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-vela-installed-"));
|
|
const resourceRoot = join(root, "resources", "open-design");
|
|
const platform = process.platform === "win32" ? "win" : process.platform === "darwin" ? "mac" : "linux";
|
|
|
|
try {
|
|
const copied = await copyOptionalVelaCliBinary({
|
|
env: {},
|
|
platform,
|
|
requireBundled: true,
|
|
resourceRoot,
|
|
});
|
|
|
|
const target = join(resourceRoot, "bin", process.platform === "win32" ? "vela.exe" : "vela");
|
|
expect(copied?.target).toBe(target);
|
|
await expect(access(target)).resolves.toBeUndefined();
|
|
const openCodeName = process.platform === "win32" ? "opencode.exe" : "opencode";
|
|
await expect(access(join(resourceRoot, "bin", "libexec", "opencode", openCodeName))).resolves.toBeUndefined();
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("accepts a Vela CLI whose pull usage line marks stageDir optional", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-vela-optional-stage-"));
|
|
const source = join(root, "source", "vela");
|
|
const resourceRoot = join(root, "resources", "open-design");
|
|
|
|
try {
|
|
await mkdir(join(root, "source"), { recursive: true });
|
|
await writeFile(source, "#!/bin/sh\nexit 0\n", "utf8");
|
|
await writeFakeOpenCodeCompanion(source, "#!/bin/sh\necho opencode\n");
|
|
|
|
// Validation must gate on the capability, not on whether an argument is
|
|
// spelled <required> or [optional]; otherwise a purely cosmetic usage
|
|
// change in Vela blocks packaging for no reason.
|
|
const copied = await copyOptionalVelaCliBinary({
|
|
env: { OPEN_DESIGN_VELA_CLI_BIN: source },
|
|
platform: "mac",
|
|
requireBundled: true,
|
|
resourceRoot,
|
|
runCommand: velaCliCommandWithOptionalStageDir,
|
|
});
|
|
|
|
expect(copied?.target).toBe(join(resourceRoot, "bin", "vela"));
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("refuses a Vela CLI whose pull usage dropped the staging directory", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-vela-no-stage-"));
|
|
const source = join(root, "source", "vela");
|
|
const resourceRoot = join(root, "resources", "open-design");
|
|
|
|
try {
|
|
await mkdir(join(root, "source"), { recursive: true });
|
|
await writeFile(source, "#!/bin/sh\nexit 0\n", "utf8");
|
|
await writeFakeOpenCodeCompanion(source, "#!/bin/sh\necho opencode\n");
|
|
|
|
// The daemon invokes `pull <projectId> <stageDir> --live-dir …`, so a CLI
|
|
// that no longer takes the positional must fail packaging rather than
|
|
// ship and reject that invocation at runtime.
|
|
await expect(
|
|
copyOptionalVelaCliBinary({
|
|
env: { OPEN_DESIGN_VELA_CLI_BIN: source },
|
|
platform: "mac",
|
|
requireBundled: true,
|
|
resourceRoot,
|
|
runCommand: velaCliCommandWithoutStageDir,
|
|
}),
|
|
).rejects.toThrow(/staged pull capability markers/u);
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("copies a configured Vela CLI binary into the POSIX resource bin", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-vela-"));
|
|
const source = join(root, "source", "vela");
|
|
const resourceRoot = join(root, "resources", "open-design");
|
|
|
|
try {
|
|
await mkdir(join(root, "source"), { recursive: true });
|
|
await writeFile(source, "#!/bin/sh\nexit 0\n", "utf8");
|
|
await writeFakeOpenCodeCompanion(source, "#!/bin/sh\necho opencode\n");
|
|
|
|
const copied = await copyOptionalVelaCliBinary({
|
|
env: { OPEN_DESIGN_VELA_CLI_BIN: source },
|
|
platform: "mac",
|
|
requireBundled: true,
|
|
resourceRoot,
|
|
runCommand: matchingVelaCliCommand,
|
|
});
|
|
|
|
const target = join(resourceRoot, "bin", "vela");
|
|
const companionTarget = join(resourceRoot, "bin", "libexec", "opencode", "opencode");
|
|
await expect(readFile(target, "utf8")).resolves.toBe("#!/bin/sh\nexit 0\n");
|
|
await expect(readFile(companionTarget, "utf8")).resolves.toBe(
|
|
"#!/bin/sh\necho opencode\n",
|
|
);
|
|
await expect(access(target, constants.X_OK)).resolves.toBeUndefined();
|
|
await expect(access(companionTarget, constants.X_OK)).resolves.toBeUndefined();
|
|
expect(copied).toEqual({ source, target });
|
|
if (process.platform !== "win32") {
|
|
expect((await stat(target)).mode & 0o111).not.toBe(0);
|
|
expect((await stat(companionTarget)).mode & 0o111).not.toBe(0);
|
|
}
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("fails strict mode when the OpenCode companion tree is missing", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-vela-strict-"));
|
|
const source = join(root, "source", "vela");
|
|
const resourceRoot = join(root, "resources", "open-design");
|
|
|
|
try {
|
|
await mkdir(join(root, "source"), { recursive: true });
|
|
await writeFile(source, "#!/bin/sh\nexit 0\n", "utf8");
|
|
|
|
await expect(
|
|
copyOptionalVelaCliBinary({
|
|
env: { OPEN_DESIGN_VELA_CLI_BIN: source },
|
|
platform: "mac",
|
|
requireBundled: true,
|
|
resourceRoot,
|
|
runCommand: matchingVelaCliCommand,
|
|
}),
|
|
).rejects.toThrow(/OpenCode companion directory is missing.*OPEN_DESIGN_VELA_CLI_BIN/);
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("copies the Vela CLI binary without a companion tree in non-strict mode", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-vela-nonstrict-"));
|
|
const source = join(root, "source", "vela");
|
|
const resourceRoot = join(root, "resources", "open-design");
|
|
|
|
try {
|
|
await mkdir(join(root, "source"), { recursive: true });
|
|
await writeFile(source, "#!/bin/sh\nexit 0\n", "utf8");
|
|
|
|
const copied = await copyOptionalVelaCliBinary({
|
|
env: { OPEN_DESIGN_VELA_CLI_BIN: source },
|
|
platform: "mac",
|
|
requireBundled: false,
|
|
resourceRoot,
|
|
});
|
|
|
|
const target = join(resourceRoot, "bin", "vela");
|
|
const companionTarget = join(resourceRoot, "bin", "libexec", "opencode", "opencode");
|
|
await expect(readFile(target, "utf8")).resolves.toBe("#!/bin/sh\nexit 0\n");
|
|
await expect(access(companionTarget)).rejects.toThrow();
|
|
expect(copied).toEqual({ source, target });
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("copies a configured Vela CLI binary into the Windows resource bin", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-vela-win-"));
|
|
const source = join(root, "source", "vela.exe");
|
|
const resourceRoot = join(root, "resources", "open-design");
|
|
|
|
try {
|
|
await mkdir(join(root, "source"), { recursive: true });
|
|
await writeFile(source, "fake exe\n", "utf8");
|
|
await writeFakeOpenCodeCompanion(source, "fake opencode\n");
|
|
|
|
const copied = await copyOptionalVelaCliBinary({
|
|
env: { OPEN_DESIGN_VELA_CLI_BIN: source },
|
|
platform: "win",
|
|
resourceRoot,
|
|
});
|
|
|
|
const target = join(resourceRoot, "bin", "vela.exe");
|
|
const companionTarget = join(resourceRoot, "bin", "libexec", "opencode", "opencode");
|
|
await expect(readFile(target, "utf8")).resolves.toBe("fake exe\n");
|
|
await expect(readFile(companionTarget, "utf8")).resolves.toBe("fake opencode\n");
|
|
expect(copied).toEqual({ source, target });
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("copies a Vela CLI binary resolved from the npm package", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-vela-npm-"));
|
|
const source = join(root, "source", "vela");
|
|
const resourceRoot = join(root, "resources", "open-design");
|
|
|
|
try {
|
|
await mkdir(join(root, "source"), { recursive: true });
|
|
await writeFile(source, "#!/bin/sh\nexit 0\n", "utf8");
|
|
await writeFakeOpenCodeCompanion(source, "#!/bin/sh\necho opencode\n");
|
|
|
|
const copied = await copyOptionalVelaCliBinary({
|
|
env: {},
|
|
importPackage: async () => ({
|
|
resolveVelaCliBin: () => source,
|
|
}),
|
|
platform: "mac",
|
|
requireBundled: true,
|
|
resourceRoot,
|
|
runCommand: matchingVelaCliCommand,
|
|
});
|
|
|
|
const target = join(resourceRoot, "bin", "vela");
|
|
const companionTarget = join(resourceRoot, "bin", "libexec", "opencode", "opencode");
|
|
await expect(readFile(target, "utf8")).resolves.toBe("#!/bin/sh\nexit 0\n");
|
|
await expect(readFile(companionTarget, "utf8")).resolves.toBe(
|
|
"#!/bin/sh\necho opencode\n",
|
|
);
|
|
await expect(access(target, constants.X_OK)).resolves.toBeUndefined();
|
|
expect(copied).toEqual({ source, target });
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("skips copying when the npm resolver reports an unsupported non-strict platform", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-vela-skip-"));
|
|
const resourceRoot = join(root, "resources", "open-design");
|
|
|
|
try {
|
|
const copied = await copyOptionalVelaCliBinary({
|
|
env: {},
|
|
importPackage: async () => ({
|
|
resolveVelaCliBin: () => null,
|
|
}),
|
|
platform: "linux",
|
|
resourceRoot,
|
|
});
|
|
|
|
expect(copied).toBeNull();
|
|
await expect(access(join(resourceRoot, "bin", "vela"))).rejects.toThrow();
|
|
} finally {
|
|
await rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("resolveOptionalVelaCliBinary", () => {
|
|
it("prefers OPEN_DESIGN_VELA_CLI_BIN over the npm resolver", async () => {
|
|
await expect(
|
|
resolveOptionalVelaCliBinary({
|
|
env: { OPEN_DESIGN_VELA_CLI_BIN: "/tmp/local-vela" },
|
|
importPackage: async () => ({
|
|
resolveVelaCliBin: () => "/tmp/npm-vela",
|
|
}),
|
|
}),
|
|
).resolves.toBe("/tmp/local-vela");
|
|
});
|
|
|
|
it("fails strict mode when the resolver package is missing", async () => {
|
|
await expect(
|
|
resolveOptionalVelaCliBinary({
|
|
env: {},
|
|
importPackage: async () => {
|
|
throw new Error("not installed");
|
|
},
|
|
requireBundled: true,
|
|
}),
|
|
).rejects.toThrow(/@powerformer\/vela-cli.*OPEN_DESIGN_VELA_CLI_BIN/);
|
|
});
|
|
|
|
it("fails strict mode when the resolver returns no binary", async () => {
|
|
await expect(
|
|
resolveOptionalVelaCliBinary({
|
|
env: {},
|
|
importPackage: async () => ({
|
|
resolveVelaCliBin: () => ({ supported: false }),
|
|
}),
|
|
requireBundled: true,
|
|
}),
|
|
).rejects.toThrow(/@powerformer\/vela-cli.*OPEN_DESIGN_VELA_CLI_BIN/);
|
|
});
|
|
|
|
it("returns null in non-strict mode when the resolver package is missing", async () => {
|
|
await expect(
|
|
resolveOptionalVelaCliBinary({
|
|
env: {},
|
|
importPackage: async () => {
|
|
throw new Error("not installed");
|
|
},
|
|
}),
|
|
).resolves.toBeNull();
|
|
});
|
|
});
|