mirror of
https://github.com/iflytek/skillhub.git
synced 2026-09-16 23:41:19 +00:00
- Interactive agent selection for install/uninstall/list - Namespace search selection for unspecified namespace - --skill-version option to skip interactive selection - --from option for local/github source installation - Remove deprecated add command (use install --from instead) - Path-based grouping for uninstall results
84 lines
2.9 KiB
TypeScript
84 lines
2.9 KiB
TypeScript
import { describe, it, expect, vi } from "vitest";
|
|
|
|
// local fs mock
|
|
vi.mock("node:fs", () => ({
|
|
existsSync: vi.fn(),
|
|
}));
|
|
|
|
it("parses local path", async () => {
|
|
vi.resetModules();
|
|
vi.doMock("node:fs", () => ({ existsSync: vi.fn().mockReturnValue(true) }));
|
|
const mod = await import("../src/core/source-parser");
|
|
const res = mod.parseSource("/abs/path");
|
|
expect(res.type).toBe("local");
|
|
expect(res.localPath).toBe("/abs/path");
|
|
});
|
|
|
|
it("parses github url from github.com", async () => {
|
|
vi.resetModules();
|
|
const mod = await import("../src/core/source-parser");
|
|
const res = mod.parseSource("https://github.com/owner/repo.git");
|
|
expect(res.type).toBe("github");
|
|
expect(res.owner).toBe("owner");
|
|
expect(res.repo).toBe("repo");
|
|
expect(res.cloneUrl).toBe("https://github.com/owner/repo.git");
|
|
});
|
|
|
|
it("parses shorthand owner/repo", async () => {
|
|
vi.resetModules();
|
|
const mod = await import("../src/core/source-parser");
|
|
const res = mod.parseSource("alice/awesome");
|
|
expect(res.type).toBe("github");
|
|
expect(res.owner).toBe("alice");
|
|
expect(res.repo).toBe("awesome");
|
|
});
|
|
|
|
it("throws on invalid source format", async () => {
|
|
vi.resetModules();
|
|
const mod = await import("../src/core/source-parser");
|
|
expect(() => mod.parseSource("invalid")).toThrow();
|
|
});
|
|
|
|
it("getCloneUrl uses cloneUrl when provided", async () => {
|
|
vi.resetModules();
|
|
const mod = await import("../src/core/source-parser");
|
|
const url = mod.getCloneUrl({ type: "github", owner: "a", repo: "b", cloneUrl: "https://example.com/a/b.git" } as any);
|
|
expect(url).toBe("https://example.com/a/b.git");
|
|
});
|
|
|
|
it("getCloneUrl builds default for github without cloneUrl", async () => {
|
|
vi.resetModules();
|
|
const mod = await import("../src/core/source-parser");
|
|
const url = mod.getCloneUrl({ type: "github", owner: "x", repo: "y" } as any);
|
|
expect(url).toBe("https://github.com/x/y.git");
|
|
});
|
|
|
|
it("parses @skill syntax: owner/repo@skillname", async () => {
|
|
vi.resetModules();
|
|
const mod = await import("../src/core/source-parser");
|
|
const res = mod.parseSource("vercel-labs/skills@openspec");
|
|
expect(res.type).toBe("github");
|
|
expect(res.owner).toBe("vercel-labs");
|
|
expect(res.repo).toBe("skills");
|
|
expect(res.skillFilter).toBe("openspec");
|
|
});
|
|
|
|
it("parses @skill syntax with branch: owner/repo@skillname", async () => {
|
|
vi.resetModules();
|
|
const mod = await import("../src/core/source-parser");
|
|
const res = mod.parseSource("owner/repo@something");
|
|
expect(res.type).toBe("github");
|
|
expect(res.owner).toBe("owner");
|
|
expect(res.repo).toBe("repo");
|
|
expect(res.skillFilter).toBe("something");
|
|
});
|
|
|
|
it("does not confuse @ in path with @skill syntax", async () => {
|
|
vi.resetModules();
|
|
const mod = await import("../src/core/source-parser");
|
|
const res = mod.parseSource("https://github.com/owner/repo");
|
|
expect(res.type).toBe("github");
|
|
expect(res.owner).toBe("owner");
|
|
expect(res.repo).toBe("repo");
|
|
expect(res.skillFilter).toBeUndefined();
|
|
});
|