diff --git a/packages/opencode/test/cli/skill.test.ts b/packages/opencode/test/cli/skill.test.ts index 6a04bd364..f71e93390 100644 --- a/packages/opencode/test/cli/skill.test.ts +++ b/packages/opencode/test/cli/skill.test.ts @@ -3,7 +3,8 @@ import { describe, test, expect } from "bun:test" import fs from "fs/promises" import path from "path" import { tmpdir } from "../fixture/fixture" -import { detectToolReferences, SHELL_BUILTINS } from "../../src/cli/cmd/skill-helpers" +import { detectToolReferences, SHELL_BUILTINS, skillSource } from "../../src/cli/cmd/skill-helpers" +import os from "os" // --------------------------------------------------------------------------- // Unit tests — import production code directly (no duplication) @@ -456,6 +457,44 @@ describe("skill install — symlink safety", () => { }) }) +// --------------------------------------------------------------------------- +// skillSource trust classification — determines builtin / global / project label +// --------------------------------------------------------------------------- + +describe("skillSource", () => { + // Global.Path.home reads process.env.OPENCODE_TEST_HOME || os.homedir() + const home = process.env.OPENCODE_TEST_HOME || os.homedir() + + test("builtin: prefix → builtin", () => { + expect(skillSource("builtin:dbt-run")).toBe("builtin") + expect(skillSource("builtin:")).toBe("builtin") + }) + + test("~/.altimate/builtin/... → builtin", () => { + expect(skillSource(path.join(home, ".altimate", "builtin", "dbt-run", "SKILL.md"))).toBe("builtin") + }) + + test("~/.claude/skills/... → global", () => { + expect(skillSource(path.join(home, ".claude", "skills", "my-skill", "SKILL.md"))).toBe("global") + }) + + test("~/.agents/skills/... → global", () => { + expect(skillSource(path.join(home, ".agents", "skills", "my-skill", "SKILL.md"))).toBe("global") + }) + + test("~/.altimate-code/skills/... → global", () => { + expect(skillSource(path.join(home, ".altimate-code", "skills", "custom", "SKILL.md"))).toBe("global") + }) + + test("project path → project", () => { + expect(skillSource("/home/user/myproject/.opencode/skills/custom/SKILL.md")).toBe("project") + }) + + test("random path with no skill dir match → project", () => { + expect(skillSource("/tmp/something/SKILL.md")).toBe("project") + }) +}) + describe("GitHub URL normalization", () => { test("extracts owner/repo from web URLs", () => { const re = /^https?:\/\/github\.com\/([^/]+\/[^/]+?)(?:\/(?:tree|blob)\/.*)?$/ diff --git a/packages/opencode/test/util/fn.test.ts b/packages/opencode/test/util/fn.test.ts new file mode 100644 index 000000000..c24b71275 --- /dev/null +++ b/packages/opencode/test/util/fn.test.ts @@ -0,0 +1,32 @@ +import { describe, test, expect } from "bun:test" +import { z } from "zod" +import { fn } from "../../src/util/fn" + +describe("fn: zod-validated function wrapper", () => { + test("passes validated input to callback", () => { + const add = fn(z.object({ a: z.number(), b: z.number() }), ({ a, b }) => a + b) + expect(add({ a: 1, b: 2 })).toBe(3) + }) + + test("throws ZodError on invalid input", () => { + const greet = fn(z.object({ name: z.string() }), ({ name }) => `hi ${name}`) + expect(() => greet({ name: 42 } as any)).toThrow() + }) + + test(".force() bypasses validation", () => { + const double = fn(z.number().int(), (n) => n * 2) + // 1.5 is not an int, but .force skips validation + expect(double.force(1.5)).toBe(3) + }) + + test(".schema exposes the original zod schema", () => { + const schema = z.string().email() + const validate = fn(schema, (s) => s.toUpperCase()) + expect(validate.schema).toBe(schema) + }) + + test("rejects unknown keys with strict schema", () => { + const strict = fn(z.object({ id: z.number() }).strict(), ({ id }) => id) + expect(() => strict({ id: 1, extra: true } as any)).toThrow() + }) +})