Files
modelstudioai__cli/packages/bailian-kb-dsh/tests/skill.test.ts
T
zeyu.fz d24104f7dc feat(kb-dsh): 接入仓库工程约定并改名为公开包 bailian-kb-dsh
包名 @ali/bailian-kb-dsh → bailian-kb-dsh(公开 npm):package.json name +
cordis.patch.yml insert.name + tsdown PLUGIN_ID 三处同步(漏一处即 dsh 运行时崩)。

产物 lib/ → dist/(本仓 .gitignore 忽略 dist 不忽略 lib),连带 main/types/
exports/files/tsdown outDir 同步;.gitignore 补 *.tsbuildinfo。

依赖接 catalog(yaml/typescript/@types/node/vite-plus);测试导入 vitest →
vite-plus/test(全仓统一约定,消掉唯一的 vitest 依赖漂移)。

tsconfig 拆三件套:tsconfig.json 纯类型检查覆盖 src+tests(供 oxlint 自动发现,
含 jsx/DOM),tsconfig.build.json 产出 node 半,tsconfig.web.json 隔离检查 web 半。
补齐 tests 从未被类型检查暴露的一处 partial 输入类型错误。

根 vite.config.ts 新增两条 override:web 半 no-restricted-imports 把 tsdown 构建期
的 bundle purity gate 提前到 lint 期;全包放开 _ 前缀的 no-unused-vars。

文档:新增 docs/agents/dsh-plugin.md,AGENTS.md 项目地图/版本锁步例外/分层边界/
场景索引同步,packages.mjs 注释说明故意不进发布白名单。

格式化(单引号无分号 → 双引号加分号)由 pre-commit 的 vp check --fix 自动完成,
无法单独成 commit,一并纳入。

全仓 vp check 0 error;插件 13 文件 85 测试全绿;build + typecheck 通过。
2026-08-24 11:02:44 +08:00

100 lines
4.2 KiB
TypeScript

import { describe, expect, it } from "vite-plus/test";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { parseSkillFile } from "../src/skill.js";
const SKILL_PATH = fileURLToPath(new URL("../skills/bailian-kb/SKILL.md", import.meta.url));
describe("parseSkillFile", () => {
it("takes name and description from frontmatter and strips the block from the body", () => {
const parsed = parseSkillFile(
[
"---",
"name: demo-skill",
"description: >-",
" First line of the folded description,",
" continued on the next source line.",
"---",
"",
"# Heading",
"",
"Body text.",
"",
].join("\n"),
);
expect(parsed?.name).toBe("demo-skill");
// A folded scalar joins its lines with spaces; the value must arrive unfolded.
expect(parsed?.description).toBe(
"First line of the folded description, continued on the next source line.",
);
// The registry performs no parsing of its own, so the YAML must already be gone.
expect(parsed?.content).toBe("\n# Heading\n\nBody text.\n");
expect(parsed?.content).not.toContain("---");
expect(parsed?.content).not.toContain("description:");
});
it("carries optional whenToUse and metadata, omitting them when absent or blank", () => {
const withExtras = parseSkillFile(
[
"---",
"name: demo-skill",
"description: Routing text.",
"whenToUse: Extra guidance.",
"metadata:",
" owner: platform",
"---",
"Body.",
].join("\n"),
);
expect(withExtras?.whenToUse).toBe("Extra guidance.");
expect(withExtras?.metadata).toEqual({ owner: "platform" });
const bare = parseSkillFile("---\nname: demo-skill\ndescription: Routing text.\n---\nBody.");
expect(bare).not.toHaveProperty("whenToUse");
expect(bare).not.toHaveProperty("metadata");
});
it("rejects a file without usable frontmatter instead of throwing", () => {
// No fence at all: a plain markdown file.
expect(parseSkillFile("# Just markdown\n")).toBeUndefined();
// Opening fence never closes.
expect(parseSkillFile("---\nname: demo-skill\n")).toBeUndefined();
// Unparsable YAML inside the fence.
expect(parseSkillFile("---\nname: [unclosed\n---\nBody.")).toBeUndefined();
// Scalar and sequence roots are not frontmatter records.
expect(parseSkillFile("---\njust a string\n---\nBody.")).toBeUndefined();
expect(parseSkillFile("---\n- one\n- two\n---\nBody.")).toBeUndefined();
// name or description missing, blank, or the wrong type.
expect(parseSkillFile("---\ndescription: Routing text.\n---\nBody.")).toBeUndefined();
expect(parseSkillFile("---\nname: demo-skill\n---\nBody.")).toBeUndefined();
expect(parseSkillFile('---\nname: demo-skill\ndescription: " "\n---\nBody.')).toBeUndefined();
expect(parseSkillFile("---\nname: 42\ndescription: Routing text.\n---\nBody.")).toBeUndefined();
});
});
describe("the packaged bailian-kb SKILL.md", () => {
const parsed = parseSkillFile(readFileSync(SKILL_PATH, "utf8"));
it("parses, and is the single source of the registered name and description", () => {
expect(parsed?.name).toBe("bailian-kb");
expect(parsed?.description).toContain("bl");
// The frontmatter must state where credentials come from: that sentence used
// to live in skill.ts and would otherwise be lost with the duplicate.
expect(parsed?.description).toContain("DASHSCOPE_API_KEY");
});
it("keeps its description within the catalog truncation budget", () => {
// tool-skill publishes catalog entries through catalogDescription(), which
// whitespace-normalizes and hard-truncates at 500 characters.
const normalized = (parsed?.description ?? "").replaceAll(/\s+/g, " ").trim();
expect(normalized.length).toBeLessThanOrEqual(500);
});
it("registers a body with no frontmatter residue", () => {
expect(parsed?.content.startsWith("---")).toBe(false);
expect(parsed?.content).not.toContain("description: >-");
// The real body still begins with the document heading.
expect(parsed?.content.trimStart().startsWith("#")).toBe(true);
});
});