Files
oh-my-opencode-free-fork/src/features/opencode-skill-loader/loader.test.ts
2026-01-01 23:02:43 +09:00

161 lines
4.3 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach } from "bun:test"
import { mkdirSync, writeFileSync, rmSync } from "fs"
import { join } from "path"
import { tmpdir } from "os"
const TEST_DIR = join(tmpdir(), "skill-loader-test-" + Date.now())
const SKILLS_DIR = join(TEST_DIR, ".opencode", "skill")
function createTestSkill(name: string, content: string): string {
const skillDir = join(SKILLS_DIR, name)
mkdirSync(skillDir, { recursive: true })
const skillPath = join(skillDir, "SKILL.md")
writeFileSync(skillPath, content)
return skillDir
}
describe("skill loader MCP parsing", () => {
beforeEach(() => {
mkdirSync(TEST_DIR, { recursive: true })
})
afterEach(() => {
rmSync(TEST_DIR, { recursive: true, force: true })
})
describe("parseSkillMcpConfig", () => {
it("parses skill with nested MCP config", async () => {
// #given
const skillContent = `---
name: test-skill
description: A test skill with MCP
mcp:
sqlite:
command: uvx
args:
- mcp-server-sqlite
- --db-path
- ./data.db
memory:
command: npx
args: [-y, "@anthropic-ai/mcp-server-memory"]
---
This is the skill body.
`
createTestSkill("test-mcp-skill", skillContent)
// #when
const { discoverSkills } = await import("./loader")
const originalCwd = process.cwd()
process.chdir(TEST_DIR)
try {
const skills = discoverSkills({ includeClaudeCodePaths: false })
const skill = skills.find(s => s.name === "test-skill")
// #then
expect(skill).toBeDefined()
expect(skill?.mcpConfig).toBeDefined()
expect(skill?.mcpConfig?.sqlite).toBeDefined()
expect(skill?.mcpConfig?.sqlite?.command).toBe("uvx")
expect(skill?.mcpConfig?.sqlite?.args).toEqual([
"mcp-server-sqlite",
"--db-path",
"./data.db"
])
expect(skill?.mcpConfig?.memory).toBeDefined()
expect(skill?.mcpConfig?.memory?.command).toBe("npx")
} finally {
process.chdir(originalCwd)
}
})
it("returns undefined mcpConfig for skill without MCP", async () => {
// #given
const skillContent = `---
name: simple-skill
description: A simple skill without MCP
---
This is a simple skill.
`
createTestSkill("simple-skill", skillContent)
// #when
const { discoverSkills } = await import("./loader")
const originalCwd = process.cwd()
process.chdir(TEST_DIR)
try {
const skills = discoverSkills({ includeClaudeCodePaths: false })
const skill = skills.find(s => s.name === "simple-skill")
// #then
expect(skill).toBeDefined()
expect(skill?.mcpConfig).toBeUndefined()
} finally {
process.chdir(originalCwd)
}
})
it("preserves env var placeholders without expansion", async () => {
// #given
const skillContent = `---
name: env-skill
mcp:
api-server:
command: node
args: [server.js]
env:
API_KEY: "\${API_KEY}"
DB_PATH: "\${HOME}/data.db"
---
Skill with env vars.
`
createTestSkill("env-skill", skillContent)
// #when
const { discoverSkills } = await import("./loader")
const originalCwd = process.cwd()
process.chdir(TEST_DIR)
try {
const skills = discoverSkills({ includeClaudeCodePaths: false })
const skill = skills.find(s => s.name === "env-skill")
// #then
expect(skill?.mcpConfig?.["api-server"]?.env?.API_KEY).toBe("${API_KEY}")
expect(skill?.mcpConfig?.["api-server"]?.env?.DB_PATH).toBe("${HOME}/data.db")
} finally {
process.chdir(originalCwd)
}
})
it("handles malformed YAML gracefully", async () => {
// #given
const skillContent = `---
name: bad-yaml
mcp: [this is not valid yaml for mcp
---
Skill body.
`
createTestSkill("bad-yaml-skill", skillContent)
// #when
const { discoverSkills } = await import("./loader")
const originalCwd = process.cwd()
process.chdir(TEST_DIR)
try {
const skills = discoverSkills({ includeClaudeCodePaths: false })
const skill = skills.find(s => s.name === "bad-yaml")
// #then - should still load skill but without MCP config
expect(skill).toBeDefined()
expect(skill?.mcpConfig).toBeUndefined()
} finally {
process.chdir(originalCwd)
}
})
})
})