package.test.mjs 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. import assert from "node:assert/strict";
  2. import { execFile } from "node:child_process";
  3. import { access, mkdtemp, readFile, rm } from "node:fs/promises";
  4. import { tmpdir } from "node:os";
  5. import { dirname, join, resolve } from "node:path";
  6. import test from "node:test";
  7. import { fileURLToPath } from "node:url";
  8. import { promisify } from "node:util";
  9. import { smokePackage } from "./smoke-package.mjs";
  10. import { smokeMcp } from "./smoke-mcp.mjs";
  11. const execFileAsync = promisify(execFile);
  12. const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
  13. test("package structure and plan validation smoke", async () => {
  14. const result = await smokePackage();
  15. assert.equal(result.status, "ok");
  16. });
  17. test("MCP exposes the image-set tools", async () => {
  18. const result = await smokeMcp();
  19. assert.equal(result.status, "ok");
  20. assert.deepEqual(result.tools.sort(), ["fmode_image_set_run", "fmode_image_set_validate"].sort());
  21. const validator = result.toolDefinitions.find((tool) => tool.name === "fmode_image_set_validate");
  22. assert.deepEqual(validator.annotations, {
  23. readOnlyHint: true,
  24. destructiveHint: false,
  25. idempotentHint: true,
  26. openWorldHint: false,
  27. });
  28. });
  29. test("skill can call its MCP tools and delegates unsupported images to the attachment bridge", async () => {
  30. const skill = await readFile(join(root, "skills", "fmode-image-set", "SKILL.md"), "utf8");
  31. assert.doesNotMatch(skill, /^allowed-tools:/m);
  32. assert.match(skill, /\[Unsupported Image\]/);
  33. assert.match(skill, /`fmode_image_set_run` 的 `action=start`/);
  34. assert.match(skill, /不得先用 Glob 搜图/);
  35. });
  36. test("workspace install places the complete package under the discoverable skill root", async () => {
  37. const workspace = await mkdtemp(join(tmpdir(), "fmode-image-set-workspace-"));
  38. try {
  39. await execFileAsync(
  40. process.execPath,
  41. [join(root, "bin", "fmode-image-set.js"), "workspace", "--skip-install"],
  42. { cwd: workspace },
  43. );
  44. const target = join(workspace, ".claude", "skills", "fmode-image-set");
  45. for (const relativePath of [
  46. ".claude-plugin/plugin.json",
  47. "bin/fmode-image-set.js",
  48. "docs/capability-map.md",
  49. "mcp/src/server.mjs",
  50. "memory-templates/image-set-profile.json",
  51. "scripts/smoke-package.mjs",
  52. "skills/fmode-image-set/SKILL.md",
  53. "skill-package-manifest.json",
  54. "SKILL.md",
  55. "references/image-set-planner.md",
  56. "agents/openai.yaml",
  57. ]) {
  58. await access(join(target, relativePath));
  59. }
  60. const config = JSON.parse(await readFile(join(workspace, ".mcp.json"), "utf8"));
  61. assert.equal(
  62. resolve(config.mcpServers["fmode-image-set"].cwd),
  63. resolve(target),
  64. );
  65. } finally {
  66. await rm(workspace, { recursive: true, force: true });
  67. }
  68. });