Tests: add Claude bundle plugin inspect integration test
This commit is contained in:
parent
a5fa75cdb3
commit
98fbbebf6a
180
src/plugins/bundle-claude-inspect.test.ts
Normal file
180
src/plugins/bundle-claude-inspect.test.ts
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||||
|
import { loadBundleManifest } from "./bundle-manifest.js";
|
||||||
|
import { inspectBundleMcpRuntimeSupport } from "./bundle-mcp.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration test: builds a Claude Code bundle plugin fixture on disk
|
||||||
|
* and verifies manifest parsing, capability detection, hook resolution,
|
||||||
|
* MCP server discovery, and settings detection all work end-to-end.
|
||||||
|
*/
|
||||||
|
describe("Claude bundle plugin inspect integration", () => {
|
||||||
|
let rootDir: string;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-claude-bundle-"));
|
||||||
|
|
||||||
|
// .claude-plugin/plugin.json
|
||||||
|
const manifestDir = path.join(rootDir, ".claude-plugin");
|
||||||
|
fs.mkdirSync(manifestDir, { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(manifestDir, "plugin.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
name: "Test Claude Plugin",
|
||||||
|
description: "Integration test fixture for Claude bundle inspection",
|
||||||
|
version: "1.0.0",
|
||||||
|
skills: ["skill-packs"],
|
||||||
|
commands: "extra-commands",
|
||||||
|
agents: "agents",
|
||||||
|
hooks: "custom-hooks",
|
||||||
|
mcpServers: ".mcp.json",
|
||||||
|
lspServers: ".lsp.json",
|
||||||
|
outputStyles: "output-styles",
|
||||||
|
}),
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
|
||||||
|
// skills/demo/SKILL.md
|
||||||
|
const skillDir = path.join(rootDir, "skill-packs", "demo");
|
||||||
|
fs.mkdirSync(skillDir, { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(skillDir, "SKILL.md"),
|
||||||
|
"---\nname: demo\ndescription: A demo skill\n---\nDo something useful.",
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
|
||||||
|
// commands/cmd/SKILL.md
|
||||||
|
const cmdDir = path.join(rootDir, "extra-commands", "cmd");
|
||||||
|
fs.mkdirSync(cmdDir, { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(cmdDir, "SKILL.md"),
|
||||||
|
"---\nname: cmd\ndescription: A command skill\n---\nRun a command.",
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
|
||||||
|
// hooks/hooks.json (default hook path)
|
||||||
|
const hooksDir = path.join(rootDir, "hooks");
|
||||||
|
fs.mkdirSync(hooksDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(hooksDir, "hooks.json"), '{"hooks":[]}', "utf-8");
|
||||||
|
|
||||||
|
// custom-hooks/ (manifest-declared hook path)
|
||||||
|
fs.mkdirSync(path.join(rootDir, "custom-hooks"), { recursive: true });
|
||||||
|
|
||||||
|
// .mcp.json with a stdio MCP server
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(rootDir, ".mcp.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
mcpServers: {
|
||||||
|
"test-stdio-server": {
|
||||||
|
command: "echo",
|
||||||
|
args: ["hello"],
|
||||||
|
},
|
||||||
|
"test-sse-server": {
|
||||||
|
url: "http://localhost:3000/sse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
|
||||||
|
// settings.json
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(rootDir, "settings.json"),
|
||||||
|
JSON.stringify({ thinkingLevel: "high" }),
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
|
||||||
|
// agents/ directory
|
||||||
|
fs.mkdirSync(path.join(rootDir, "agents"), { recursive: true });
|
||||||
|
|
||||||
|
// .lsp.json
|
||||||
|
fs.writeFileSync(path.join(rootDir, ".lsp.json"), '{"lspServers":{}}', "utf-8");
|
||||||
|
|
||||||
|
// output-styles/ directory
|
||||||
|
fs.mkdirSync(path.join(rootDir, "output-styles"), { recursive: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
fs.rmSync(rootDir, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("loads the full Claude bundle manifest with all capabilities", () => {
|
||||||
|
const result = loadBundleManifest({ rootDir, bundleFormat: "claude" });
|
||||||
|
expect(result.ok).toBe(true);
|
||||||
|
if (!result.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const m = result.manifest;
|
||||||
|
expect(m.name).toBe("Test Claude Plugin");
|
||||||
|
expect(m.description).toBe("Integration test fixture for Claude bundle inspection");
|
||||||
|
expect(m.version).toBe("1.0.0");
|
||||||
|
expect(m.bundleFormat).toBe("claude");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves skills from both skills and commands paths", () => {
|
||||||
|
const result = loadBundleManifest({ rootDir, bundleFormat: "claude" });
|
||||||
|
expect(result.ok).toBe(true);
|
||||||
|
if (!result.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(result.manifest.skills).toContain("skill-packs");
|
||||||
|
expect(result.manifest.skills).toContain("extra-commands");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves hooks from default and declared paths", () => {
|
||||||
|
const result = loadBundleManifest({ rootDir, bundleFormat: "claude" });
|
||||||
|
expect(result.ok).toBe(true);
|
||||||
|
if (!result.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default hooks/hooks.json path + declared custom-hooks
|
||||||
|
expect(result.manifest.hooks).toContain("hooks/hooks.json");
|
||||||
|
expect(result.manifest.hooks).toContain("custom-hooks");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("detects settings files", () => {
|
||||||
|
const result = loadBundleManifest({ rootDir, bundleFormat: "claude" });
|
||||||
|
expect(result.ok).toBe(true);
|
||||||
|
if (!result.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(result.manifest.settingsFiles).toEqual(["settings.json"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("detects all bundle capabilities", () => {
|
||||||
|
const result = loadBundleManifest({ rootDir, bundleFormat: "claude" });
|
||||||
|
expect(result.ok).toBe(true);
|
||||||
|
if (!result.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const caps = result.manifest.capabilities;
|
||||||
|
expect(caps).toContain("skills");
|
||||||
|
expect(caps).toContain("commands");
|
||||||
|
expect(caps).toContain("agents");
|
||||||
|
expect(caps).toContain("hooks");
|
||||||
|
expect(caps).toContain("mcpServers");
|
||||||
|
expect(caps).toContain("lspServers");
|
||||||
|
expect(caps).toContain("outputStyles");
|
||||||
|
expect(caps).toContain("settings");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("inspects MCP runtime support with supported and unsupported servers", () => {
|
||||||
|
const mcp = inspectBundleMcpRuntimeSupport({
|
||||||
|
pluginId: "test-claude-plugin",
|
||||||
|
rootDir,
|
||||||
|
bundleFormat: "claude",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mcp.hasSupportedStdioServer).toBe(true);
|
||||||
|
expect(mcp.supportedServerNames).toContain("test-stdio-server");
|
||||||
|
expect(mcp.unsupportedServerNames).toContain("test-sse-server");
|
||||||
|
expect(mcp.diagnostics).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user