


























@@ -36,6 +36,41 @@ vi.mock("../infra/fs-safe.js", () => ({
3636const { installSkillFromClawHub, searchSkillsFromClawHub, updateSkillsFromClawHub } =
3737await import("./skills-clawhub.js");
383839+function expectInstallPackageSourceDir(sourceDir: string) {
40+const call = installPackageDirMock.mock.calls[0];
41+if (!call) {
42+throw new Error("expected installPackageDir call");
43+}
44+expect(call[0]?.sourceDir).toBe(sourceDir);
45+}
46+47+function expectInstalledSkill(
48+result: Awaited<ReturnType<typeof installSkillFromClawHub>>,
49+expected: { slug?: string; version?: string; targetDir?: string } = {},
50+) {
51+expect(result.ok).toBe(true);
52+if (!result.ok) {
53+throw new Error(`expected skill install success, got ${result.error}`);
54+}
55+if (expected.slug) {
56+expect(result.slug).toBe(expected.slug);
57+}
58+if (expected.version) {
59+expect(result.version).toBe(expected.version);
60+}
61+if (expected.targetDir) {
62+expect(result.targetDir).toBe(expected.targetDir);
63+}
64+}
65+66+function expectInvalidSlug(result: Awaited<ReturnType<typeof installSkillFromClawHub>>) {
67+expect(result.ok).toBe(false);
68+if (result.ok) {
69+throw new Error("expected invalid slug failure");
70+}
71+expect(result.error).toContain("Invalid skill slug");
72+}
73+3974describe("skills-clawhub", () => {
4075beforeEach(() => {
4176fetchClawHubSkillDetailMock.mockReset();
@@ -90,13 +125,8 @@ describe("skills-clawhub", () => {
90125version: "1.0.0",
91126baseUrl: undefined,
92127});
93-expect(installPackageDirMock).toHaveBeenCalledWith(
94-expect.objectContaining({
95-sourceDir: "/tmp/extracted-skill",
96-}),
97-);
98-expect(result).toMatchObject({
99-ok: true,
128+expectInstallPackageSourceDir("/tmp/extracted-skill");
129+expectInstalledSkill(result, {
100130slug: "agentreceipt",
101131version: "1.0.0",
102132targetDir: "/tmp/workspace/skills/agentreceipt",
@@ -114,12 +144,8 @@ describe("skills-clawhub", () => {
114144slug: "agentreceipt",
115145});
116146117-expect(result).toMatchObject({ ok: true });
118-expect(installPackageDirMock).toHaveBeenCalledWith(
119-expect.objectContaining({
120-sourceDir: "/tmp/extracted-skill",
121-}),
122-);
147+expectInstalledSkill(result);
148+expectInstallPackageSourceDir("/tmp/extracted-skill");
123149},
124150);
125151@@ -165,15 +191,13 @@ describe("skills-clawhub", () => {
165191}
166192167193function expectLegacyUpdateSuccess(results: unknown, workspaceDir: string, slug: string) {
168-expect(results).toMatchObject([
169-{
170-ok: true,
171- slug,
172-previousVersion: "0.9.0",
173-version: "1.0.0",
174-targetDir: path.join(workspaceDir, "skills", slug),
175-},
176-]);
194+expect(Array.isArray(results)).toBe(true);
195+const first = (results as Array<Record<string, unknown>>)[0];
196+expect(first?.ok).toBe(true);
197+expect(first?.slug).toBe(slug);
198+expect(first?.previousVersion).toBe("0.9.0");
199+expect(first?.version).toBe("1.0.0");
200+expect(first?.targetDir).toBe(path.join(workspaceDir, "skills", slug));
177201}
178202179203it("updates all tracked legacy Unicode slugs in place", async () => {
@@ -246,54 +270,39 @@ describe("skills-clawhub", () => {
246270workspaceDir: "/tmp/workspace",
247271slug: "re\u0430ct",
248272});
249-expect(result).toMatchObject({
250-ok: false,
251-error: expect.stringContaining("Invalid skill slug"),
252-});
273+expectInvalidSlug(result);
253274});
254275255276it("rejects Cyrillic homograph 'е' (U+0435) in slug", async () => {
256277const result = await installSkillFromClawHub({
257278workspaceDir: "/tmp/workspace",
258279slug: "r\u0435act",
259280});
260-expect(result).toMatchObject({
261-ok: false,
262-error: expect.stringContaining("Invalid skill slug"),
263-});
281+expectInvalidSlug(result);
264282});
265283266284it("rejects Cyrillic homograph 'о' (U+043E) in slug", async () => {
267285const result = await installSkillFromClawHub({
268286workspaceDir: "/tmp/workspace",
269287slug: "t\u043Edo",
270288});
271-expect(result).toMatchObject({
272-ok: false,
273-error: expect.stringContaining("Invalid skill slug"),
274-});
289+expectInvalidSlug(result);
275290});
276291277292it("rejects slug with mixed Unicode and ASCII", async () => {
278293const result = await installSkillFromClawHub({
279294workspaceDir: "/tmp/workspace",
280295slug: "cаlеndаr",
281296});
282-expect(result).toMatchObject({
283-ok: false,
284-error: expect.stringContaining("Invalid skill slug"),
285-});
297+expectInvalidSlug(result);
286298});
287299288300it("rejects slug with non-Latin scripts", async () => {
289301const result = await installSkillFromClawHub({
290302workspaceDir: "/tmp/workspace",
291303slug: "技能",
292304});
293-expect(result).toMatchObject({
294-ok: false,
295-error: expect.stringContaining("Invalid skill slug"),
296-});
305+expectInvalidSlug(result);
297306});
298307299308it("rejects Unicode that case-folds to ASCII (Kelvin sign U+212A)", async () => {
@@ -302,48 +311,39 @@ describe("skills-clawhub", () => {
302311workspaceDir: "/tmp/workspace",
303312slug: "\u212Aalendar",
304313});
305-expect(result).toMatchObject({
306-ok: false,
307-error: expect.stringContaining("Invalid skill slug"),
308-});
314+expectInvalidSlug(result);
309315});
310316311317it("rejects slug starting with a hyphen", async () => {
312318const result = await installSkillFromClawHub({
313319workspaceDir: "/tmp/workspace",
314320slug: "-calendar",
315321});
316-expect(result).toMatchObject({
317-ok: false,
318-error: expect.stringContaining("Invalid skill slug"),
319-});
322+expectInvalidSlug(result);
320323});
321324322325it("rejects slug ending with a hyphen", async () => {
323326const result = await installSkillFromClawHub({
324327workspaceDir: "/tmp/workspace",
325328slug: "calendar-",
326329});
327-expect(result).toMatchObject({
328-ok: false,
329-error: expect.stringContaining("Invalid skill slug"),
330-});
330+expectInvalidSlug(result);
331331});
332332333333it("accepts uppercase ASCII slugs (preserves original casing behavior)", async () => {
334334const result = await installSkillFromClawHub({
335335workspaceDir: "/tmp/workspace",
336336slug: "React",
337337});
338-expect(result).toMatchObject({ ok: true });
338+expectInstalledSkill(result);
339339});
340340341341it("accepts valid lowercase ASCII slugs", async () => {
342342const result = await installSkillFromClawHub({
343343workspaceDir: "/tmp/workspace",
344344slug: "calendar-2",
345345});
346-expect(result).toMatchObject({ ok: true });
346+expectInstalledSkill(result);
347347});
348348});
349349此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。