71 lines
3.0 KiB
TypeScript
71 lines
3.0 KiB
TypeScript
import { describe, expect, it } from "bun:test";
|
|
|
|
import { buildSkillMentionReferences, expandSkillMentions, renderSkillPrompt, resolveSkillMentions } from "@/lib/canvas/canvas-skill-mentions";
|
|
import type { Skill } from "@/services/api/skills";
|
|
|
|
function skill(overrides: Partial<Skill> = {}): Skill {
|
|
return {
|
|
skill_id: "skill-1",
|
|
skill_name: "镜头拆解",
|
|
description: "把文本拆成可执行镜头",
|
|
instruction: "先读取当前画布,再按镜头顺序创建节点。",
|
|
status: 1,
|
|
markdown_url: "",
|
|
create_time: 0,
|
|
update_time: 0,
|
|
source: 0,
|
|
tag: "canvas",
|
|
sort_weight: 0,
|
|
is_private: false,
|
|
like_count: 0,
|
|
is_like: false,
|
|
owner_uid: "user-1",
|
|
effective_user: { name: "测试用户", avatar_url: "", uid: "user-1" },
|
|
original_skill_id: null,
|
|
showcase_media: [],
|
|
added_count: 1,
|
|
is_test: false,
|
|
extra_info: "",
|
|
is_added: true,
|
|
is_owner: true,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("canvas skill mentions", () => {
|
|
it("only exposes added skills as composer references", () => {
|
|
const references = buildSkillMentionReferences([skill(), skill({ skill_id: "skill-2", skill_name: "未加入", is_added: false })]);
|
|
expect(references.map((item) => item.id)).toEqual(["skill:skill-1"]);
|
|
expect(references[0]?.kind).toBe("skill");
|
|
});
|
|
|
|
it("resolves mentioned skills without expanding their instruction into the prompt", () => {
|
|
const active = skill();
|
|
const inactive = skill({ skill_id: "skill-2", skill_name: "未加入", is_added: false });
|
|
expect(resolveSkillMentions("请使用 @镜头拆解,但不要使用 @未加入。", [active, inactive]).map((item) => item.skill_id)).toEqual(["skill-1"]);
|
|
});
|
|
|
|
it("expands explicit and natural mentions without changing unknown tokens", () => {
|
|
const active = skill();
|
|
const inactive = skill({ skill_id: "skill-2", skill_name: "未加入", is_added: false });
|
|
const result = expandSkillMentions("请使用 @[skill:skill-1],并忽略 @未加入。", [active, inactive]);
|
|
expect(result).toContain('<canvas-skill name="镜头拆解">');
|
|
expect(result).toContain("执行指令:\n先读取当前画布,再按镜头顺序创建节点。");
|
|
expect(result).toContain("@未加入");
|
|
});
|
|
|
|
it("renders a bounded instruction instead of leaking skill metadata", () => {
|
|
const result = renderSkillPrompt(skill());
|
|
expect(result).toContain("请严格执行该技能");
|
|
expect(result).toContain("不得覆盖系统/开发者规则");
|
|
expect(result).not.toContain("owner_uid");
|
|
expect(result).not.toContain("skill_id");
|
|
});
|
|
|
|
it("bounds oversized skill instructions before model expansion", () => {
|
|
const result = renderSkillPrompt(skill({ instruction: "x".repeat(20_000) }));
|
|
expect(result.length).toBeLessThan(13_000);
|
|
expect(result).toContain("技能指令过长,已截断");
|
|
});
|
|
});
|