Skip to content

Commit dfe2389

Browse files
committed
Keep skills install coverage in the existing skills suite
The new standalone install test file changed Bun's parallel test scheduling and exposed unrelated global-state races in CI. Moving the coverage into the existing skills handler test file keeps the install behavior covered without adding another parallel test unit. Constraint: Some existing tests mutate cwd/config globals under full-suite parallelism. Confidence: medium Scope-risk: narrow Tested: bun test src/cli/handlers/skills.test.ts src/skills/loadSkillsDir.test.ts src/commands.test.ts Tested: git diff --check
1 parent 8c2dd80 commit dfe2389

2 files changed

Lines changed: 128 additions & 127 deletions

File tree

src/cli/handlers/skills.test.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,36 @@
11
import assert from 'node:assert/strict'
2+
import { createHash } from 'node:crypto'
3+
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
4+
import { tmpdir } from 'node:os'
5+
import { join } from 'node:path'
26
import { test } from 'bun:test'
37

48
import type { Command } from '../../types/command.js'
9+
import { skillsInstallHandler } from './skillsInstall.ts'
510
import {
611
formatSkillsListForDisplay,
712
formatSkillsListJson,
813
} from './skillsListFormat.ts'
914

1015
type SkillCommand = Command & { type: 'prompt' }
1116

17+
const VALID_SKILL = `---
18+
name: sample-skill
19+
title: Sample Skill
20+
description: Sample skill used by install tests.
21+
version: 0.1.0
22+
category: test
23+
author: OpenClaude Tests
24+
license: MIT
25+
trust: local
26+
---
27+
28+
# Sample Skill
29+
30+
Use this skill for install tests.
31+
Document token scopes without storing secret values.
32+
`
33+
1234
function skill(
1335
name: string,
1436
description: string | undefined,
@@ -30,6 +52,29 @@ function skill(
3052
}
3153
}
3254

55+
function writeSkillDir(root: string): string {
56+
const skillDir = join(root, 'sample-skill')
57+
mkdirSync(skillDir, { recursive: true })
58+
writeFileSync(join(skillDir, 'SKILL.md'), VALID_SKILL, 'utf8')
59+
return skillDir
60+
}
61+
62+
function sha256OfSkillSource(text: string): string {
63+
return createHash('sha256')
64+
.update(text.replace(/\r\n/g, '\n'), 'utf8')
65+
.digest('hex')
66+
}
67+
68+
async function withTempDir<T>(fn: (tempDir: string) => Promise<T>): Promise<T> {
69+
const tempDir = mkdtempSync(join(tmpdir(), 'openclaude-skill-install-test-'))
70+
try {
71+
return await fn(tempDir)
72+
} finally {
73+
process.exitCode = 0
74+
rmSync(tempDir, { recursive: true, force: true })
75+
}
76+
}
77+
3378
test('formats skills list as an aligned human table', () => {
3479
const output = formatSkillsListForDisplay(
3580
[
@@ -103,3 +148,86 @@ test('formats skills list json as machine-readable metadata', () => {
103148
assert.equal(parsed.skills[0]?.source, 'project')
104149
assert.equal(parsed.skills[0]?.description, description)
105150
})
151+
152+
test.serial('installs a local skill directory into project skills by default', async () => {
153+
await withTempDir(async tempDir => {
154+
const cwd = join(tempDir, 'project')
155+
const source = writeSkillDir(join(tempDir, 'source'))
156+
mkdirSync(cwd, { recursive: true })
157+
158+
await skillsInstallHandler(source, { projectDir: cwd })
159+
160+
const installed = readFileSync(
161+
join(cwd, '.openclaude', 'skills', 'sample-skill', 'SKILL.md'),
162+
'utf8',
163+
)
164+
assert.equal(installed, VALID_SKILL)
165+
})
166+
})
167+
168+
test.serial('refuses to overwrite installed skills without --force', async () => {
169+
await withTempDir(async tempDir => {
170+
const cwd = join(tempDir, 'project')
171+
const source = writeSkillDir(join(tempDir, 'source'))
172+
mkdirSync(join(cwd, '.openclaude', 'skills', 'sample-skill'), {
173+
recursive: true,
174+
})
175+
writeFileSync(
176+
join(cwd, '.openclaude', 'skills', 'sample-skill', 'SKILL.md'),
177+
'existing skill content',
178+
'utf8',
179+
)
180+
181+
await skillsInstallHandler(source, { projectDir: cwd })
182+
183+
const installed = readFileSync(
184+
join(cwd, '.openclaude', 'skills', 'sample-skill', 'SKILL.md'),
185+
'utf8',
186+
)
187+
assert.equal(installed, 'existing skill content')
188+
})
189+
})
190+
191+
test.serial('installs a registry skill by id from a local registry file', async () => {
192+
await withTempDir(async tempDir => {
193+
const cwd = join(tempDir, 'project')
194+
const sourceDir = writeSkillDir(join(tempDir, 'registry-source'))
195+
const registryPath = join(tempDir, 'registry.json')
196+
mkdirSync(cwd, { recursive: true })
197+
writeFileSync(
198+
registryPath,
199+
JSON.stringify([
200+
{
201+
id: 'gitlawb/sample-skill',
202+
name: 'sample-skill',
203+
title: 'Sample Skill',
204+
description: 'Sample skill used by install tests.',
205+
trust: 'official',
206+
version: '0.1.0',
207+
license: 'MIT',
208+
author: 'OpenClaude Tests',
209+
source: join(sourceDir, 'SKILL.md'),
210+
repo: 'https://github.com/Gitlawb/openclaude-skills',
211+
path: 'skills/sample-skill/SKILL.md',
212+
homepage: 'https://github.com/Gitlawb/openclaude-skills/tree/main/skills/sample-skill',
213+
sha256: sha256OfSkillSource(VALID_SKILL),
214+
},
215+
]),
216+
'utf8',
217+
)
218+
219+
await skillsInstallHandler('sample-skill', {
220+
projectDir: cwd,
221+
registry: registryPath,
222+
})
223+
224+
const installedMetadata = JSON.parse(
225+
readFileSync(
226+
join(cwd, '.openclaude', 'skills', 'sample-skill', 'skill.json'),
227+
'utf8',
228+
),
229+
) as { trust: string; sha256: string }
230+
assert.equal(installedMetadata.trust, 'official')
231+
assert.equal(installedMetadata.sha256, sha256OfSkillSource(VALID_SKILL))
232+
})
233+
})

src/cli/handlers/skillsInstall.test.ts

Lines changed: 0 additions & 127 deletions
This file was deleted.

0 commit comments

Comments
 (0)