Files
claude-code/src/services/skillSearch/__tests__/prefetch.test.ts
2026-04-22 22:38:09 +08:00

102 lines
3.0 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
import {
existsSync,
mkdirSync,
mkdtempSync,
rmSync,
writeFileSync,
} from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { clearCommandsCache } from '../../../commands.js'
import { getTurnZeroSkillDiscovery } from '../prefetch.js'
import { clearSkillIndexCache } from '../localSearch.js'
let root: string
let previousCwd: string
const originalEnv = { ...process.env }
beforeEach(() => {
root = mkdtempSync(join(tmpdir(), 'skill-search-prefetch-'))
previousCwd = process.cwd()
process.chdir(root)
process.env = { ...originalEnv }
process.env.CLAUDE_CONFIG_DIR = join(root, 'config')
process.env.CLAUDE_SKILL_LEARNING_HOME = join(root, 'learning')
process.env.SKILL_SEARCH_ENABLED = '1'
process.env.SKILL_LEARNING_ENABLED = '1'
process.env.NODE_ENV = 'test'
process.env.ANTHROPIC_API_KEY = 'test-key'
clearCommandsCache()
clearSkillIndexCache()
})
afterEach(() => {
process.chdir(previousCwd)
process.env = { ...originalEnv }
clearCommandsCache()
clearSkillIndexCache()
try {
rmSync(root, {
recursive: true,
force: true,
maxRetries: 10,
retryDelay: 100,
})
} catch {
// Windows can keep transient handles after dynamic command loading.
}
})
describe('skill search prefetch', () => {
test('auto-loads high-confidence project skill content', async () => {
const skillDir = join(root, '.claude', 'skills', 'feature-audit')
mkdirSync(skillDir, { recursive: true })
writeFileSync(
join(skillDir, 'SKILL.md'),
[
'---',
'name: feature-audit',
'description: Audit feature flags and classify minimal implementations',
'---',
'',
'# Feature Audit',
'',
'Use the feature flag audit workflow and classify flags as stub, shell, MVP, or thin-toggle.',
].join('\n'),
)
const attachment = await getTurnZeroSkillDiscovery(
'audit feature flags for minimal implementation stubs',
[],
{ agentId: undefined } as any,
)
expect(attachment?.type).toBe('skill_discovery')
if (attachment?.type !== 'skill_discovery') {
throw new Error('expected skill_discovery attachment')
}
expect(attachment.skills[0]?.name).toBe('feature-audit')
expect(attachment.skills[0]?.autoLoaded).toBe(true)
expect(attachment.skills[0]?.content).toContain(
'feature flag audit workflow',
)
})
test('records a pending skill gap on the first unmatched prompt (no draft file yet)', async () => {
const attachment = await getTurnZeroSkillDiscovery(
'frobnicate zephyr ledger workflow',
[],
{ agentId: undefined } as any,
)
expect(attachment?.type).toBe('skill_discovery')
if (attachment?.type !== 'skill_discovery') {
throw new Error('expected skill_discovery attachment')
}
expect(attachment.skills).toEqual([])
expect(attachment.gap?.status).toBe('pending')
expect(attachment.gap?.draftPath).toBeUndefined()
})
})