Files
cobble/src/harness/tools/skills/open-bot-skills.test.ts
T
tbenandClaude Opus 5.5 2df1bf83f4 Show when each extension was last updated
MCP, skill and tool rows in Settings say "Updated 5m ago" beside how
they're reached and who gets them. updated_at now means a change to an
extension's settings (an edit, or any Access save): runs, connections and
MCP syncs keep their own last_checked_at / last_synced_at and no longer
touch it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 08:07:44 +01:00

89 lines
3.5 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import type { SkillRecord } from "@/src/types";
import openBotSkills from "./open-bot-skills";
const shown_chars = 30000;
function skill(overrides: Partial<SkillRecord> & { slug: string }): SkillRecord {
return {
id: 1,
name: overrides.slug,
description: `When ${overrides.slug} applies.`,
body: `${overrides.slug} body`,
all_bots: false,
enabled: true,
updated_at: null,
...overrides,
};
}
describe("openBotSkills", () => {
test("no skills means no tool and no note", () => {
expect(openBotSkills({ skills: [], shown_chars })).toEqual({ tools: [], note: "" });
});
test("a disabled skill is left out entirely", () => {
const opened = openBotSkills({ skills: [skill({ slug: "seo", enabled: false })], shown_chars });
expect(opened.tools).toHaveLength(0);
expect(opened.note).toBe("");
});
test("one tool, named load_skill, whose description lists the slugs", () => {
const opened = openBotSkills({ skills: [skill({ slug: "seo" }), skill({ slug: "inbox", id: 2 })], shown_chars });
expect(opened.tools).toHaveLength(1);
expect(opened.tools[0]!.name).toBe("load_skill");
expect(opened.tools[0]!.description).toContain("- seo:");
expect(opened.tools[0]!.description).toContain("- inbox:");
expect(opened.note).toContain("# Skills");
});
test("a built-in that took the name wins", () => {
expect(openBotSkills({ skills: [skill({ slug: "seo" })], taken: ["load_skill"], shown_chars })).toEqual({
tools: [],
note: "",
});
});
test("the tool returns the body of the slug it's given", async () => {
const opened = openBotSkills({ skills: [skill({ slug: "seo", body: "1. Pull rankings" })], shown_chars });
const output = await opened.tools[0]!.run!({ args: { slug: "seo" }, signal: AbortSignal.timeout(1000) });
expect(output).toBe("1. Pull rankings");
});
test("an unknown slug fails with the slugs it does have", async () => {
const opened = openBotSkills({ skills: [skill({ slug: "seo" })], shown_chars });
await expect(opened.tools[0]!.run!({ args: { slug: "nope" }, signal: AbortSignal.timeout(1000) })).rejects.toThrow(
/no skill called "nope".*seo/,
);
});
test("a long body is cut at the limit and says so", async () => {
const opened = openBotSkills({
skills: [skill({ slug: "seo", body: "x".repeat(shown_chars + 500) })],
shown_chars,
});
const output = await opened.tools[0]!.run!({ args: { slug: "seo" }, signal: AbortSignal.timeout(1000) });
expect(output).toContain("Cut at");
expect(output.length).toBeLessThan(shown_chars + 200);
});
test("the cut follows the install's limit", async () => {
const opened = openBotSkills({ skills: [skill({ slug: "seo", body: "x".repeat(1500) })], shown_chars: 1000 });
const output = await opened.tools[0]!.run!({ args: { slug: "seo" }, signal: AbortSignal.timeout(1000) });
expect(output).toContain("Cut at 1000 characters");
});
test("an empty body says it is empty rather than returning nothing", async () => {
const opened = openBotSkills({ skills: [skill({ slug: "seo", body: "" })], shown_chars });
const output = await opened.tools[0]!.run!({ args: { slug: "seo" }, signal: AbortSignal.timeout(1000) });
expect(output).toContain("is empty");
});
});