Files
orca/src/cli/skills-reference-selector.test.ts
T

174 lines
5.9 KiB
TypeScript

import { describe, expect, it, beforeEach, vi } from 'vitest'
vi.mock('./bundled-skill-guides.js', () => ({
BUNDLED_SKILL_GUIDES: [
{
name: 'alpha',
description: 'Use when alpha work is needed.',
markdown: '# Alpha\n\nShort.\n',
aliases: ['legacy-alpha'],
references: [
{ name: 'first-gate', markdown: '# First gate\n\nDo the first thing.\n' },
{ name: 'second-gate', markdown: '# Second gate\n\nDo the second thing.\n' }
]
},
{
name: 'zeta',
description: 'Use when zeta work is needed.',
markdown: '# Zeta\n',
aliases: [],
references: []
}
]
}))
vi.mock('./runtime-client', async () => {
const { RuntimeClientError, RuntimeRpcFailureError } = await import('./runtime/types.js')
class RuntimeClient {
constructor() {
throw new Error('skills get constructed a RuntimeClient')
}
}
return {
RuntimeClient,
RuntimeClientError,
RuntimeRpcFailureError,
serveOrcaApp: vi.fn(),
getDefaultUserDataPath: vi.fn(() => '/tmp/orca-user-data')
}
})
import { main } from './index'
function stdoutText(spy: ReturnType<typeof vi.spyOn>): string {
return spy.mock.calls.map((call) => String(call[0])).join('')
}
describe('orca skills get --reference', () => {
beforeEach(() => {
vi.restoreAllMocks()
process.exitCode = undefined
})
it('prints only the named reference, with no kernel and no header', async () => {
const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true)
await main(['skills', 'get', 'alpha', '--reference', 'second-gate'], '/tmp/repo')
expect(stdoutText(stdoutSpy)).toBe('# Second gate\n\nDo the second thing.\n')
})
it('accepts the references/<file>.md spelling the gate table prints', async () => {
const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true)
await main(['skills', 'get', 'alpha', '--reference', 'references/first-gate.md'], '/tmp/repo')
expect(stdoutText(stdoutSpy)).toBe('# First gate\n\nDo the first thing.\n')
})
it('resolves a reference through a topic alias', async () => {
const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true)
await main(['skills', 'get', 'legacy-alpha', '--reference', 'first-gate.md'], '/tmp/repo')
expect(stdoutText(stdoutSpy)).toBe('# First gate\n\nDo the first thing.\n')
})
it('gives --reference --json the canonical topic, reference name, and Markdown', async () => {
const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true)
await main(
['skills', 'get', 'legacy-alpha', '--reference', 'references/first-gate.md', '--json'],
'/tmp/repo'
)
expect(stdoutText(stdoutSpy)).toBe(
`${JSON.stringify(
{
name: 'alpha',
reference: 'first-gate',
markdown: '# First gate\n\nDo the first thing.\n'
},
null,
2
)}\n`
)
})
it('lists reference names for --references', async () => {
const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true)
await main(['skills', 'get', 'alpha', '--references'], '/tmp/repo')
expect(stdoutText(stdoutSpy)).toBe('first-gate\nsecond-gate\n')
})
it('gives --references --json a stable schema', async () => {
const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true)
await main(['skills', 'get', 'alpha', '--references', '--json'], '/tmp/repo')
expect(stdoutText(stdoutSpy)).toBe(
`${JSON.stringify({ name: 'alpha', references: ['first-gate', 'second-gate'] }, null, 2)}\n`
)
})
it('reports a topic that ships no references', async () => {
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
await main(['skills', 'get', 'zeta', '--references'], '/tmp/repo')
expect(process.exitCode).toBe(1)
expect(errorSpy).toHaveBeenCalledWith('Guide "zeta" has no bundled references.')
})
it('names the available references for an unknown one', async () => {
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
await main(['skills', 'get', 'alpha', '--reference', 'nope'], '/tmp/repo')
expect(process.exitCode).toBe(1)
expect(errorSpy).toHaveBeenCalledWith(
'Unknown reference "nope" for alpha. Available: first-gate, second-gate'
)
})
it('rejects --reference on a topic with no references', async () => {
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
await main(['skills', 'get', 'zeta', '--reference', 'first-gate'], '/tmp/repo')
expect(process.exitCode).toBe(1)
expect(errorSpy).toHaveBeenCalledWith('Guide "zeta" has no bundled references.')
})
it('rejects --reference without a value', async () => {
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
await main(['skills', 'get', 'alpha', '--reference'], '/tmp/repo')
expect(process.exitCode).toBe(1)
expect(errorSpy).toHaveBeenCalledWith('Missing required --reference')
})
it('rejects combining --full with --reference', async () => {
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
await main(['skills', 'get', 'alpha', '--full', '--reference', 'first-gate'], '/tmp/repo')
expect(process.exitCode).toBe(1)
expect(errorSpy).toHaveBeenCalledWith('Use either --full or --reference, not both.')
})
it('rejects combining --references with --full or --reference', async () => {
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
await main(['skills', 'get', 'alpha', '--references', '--full'], '/tmp/repo')
await main(['skills', 'get', 'alpha', '--references', '--reference', 'first-gate'], '/tmp/repo')
expect(process.exitCode).toBe(1)
expect(errorSpy).toHaveBeenNthCalledWith(1, 'Use either --references or --full, not both.')
expect(errorSpy).toHaveBeenNthCalledWith(2, 'Use either --references or --reference, not both.')
})
})