99 lines
3.4 KiB
JavaScript
99 lines
3.4 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { existsSync, readFileSync, readlinkSync, lstatSync } from 'node:fs'
|
|
import { join } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import test from 'node:test'
|
|
import { apply } from '../bundle/index.js'
|
|
|
|
const dshDir = fileURLToPath(new URL('..', import.meta.url))
|
|
const bundleDir = join(dshDir, 'bundle')
|
|
const pkg = JSON.parse(readFileSync(join(bundleDir, 'package.json'), 'utf8'))
|
|
|
|
test('package declares a DSH bundle patch and the runtime files', () => {
|
|
assert.equal(pkg.name, '@local/dsh-harness')
|
|
assert.equal(pkg.type, 'module')
|
|
assert.equal(pkg.dsh?.bundle?.patch, './cordis.patch.yml')
|
|
assert.equal(pkg.exports?.['.'], './index.js')
|
|
assert.ok(pkg.files.includes('index.js'))
|
|
assert.ok(pkg.files.includes('command-approve.js'))
|
|
assert.ok(pkg.files.includes('tool-approve.js'))
|
|
assert.ok(pkg.files.includes('lib'))
|
|
assert.ok(pkg.files.includes('hooks'))
|
|
assert.ok(existsSync(join(bundleDir, 'cordis.patch.yml')))
|
|
})
|
|
|
|
test('bundle patch inserts the harness row', () => {
|
|
const patch = readFileSync(join(bundleDir, 'cordis.patch.yml'), 'utf8')
|
|
assert.match(patch, /id: dsh-harness/)
|
|
assert.match(patch, /name: '@local\/dsh-harness'/)
|
|
})
|
|
|
|
test('display resources and hook bridge exist', () => {
|
|
for (const file of ['icon.svg', 'locale/en.json', 'locale/ru.json', 'dsh-tools.md']) {
|
|
assert.ok(existsSync(join(bundleDir, file)), `${file} exists`)
|
|
}
|
|
assert.ok(existsSync(join(bundleDir, 'hooks', 'guard-main.sh')))
|
|
assert.ok(existsSync(join(bundleDir, 'hooks', 'require-plan.sh')))
|
|
assert.match(readFileSync(join(bundleDir, 'dsh-tools.md'), 'utf8'), /subagent/)
|
|
})
|
|
|
|
test('hook directory is a link to the repository source of truth', () => {
|
|
const hooks = join(bundleDir, 'hooks')
|
|
const stat = lstatSync(hooks)
|
|
if (stat.isSymbolicLink()) {
|
|
assert.equal(readlinkSync(hooks), '../../hooks')
|
|
}
|
|
})
|
|
|
|
test('index.js registers all three integration points', () => {
|
|
const source = readFileSync(join(bundleDir, 'index.js'), 'utf8')
|
|
assert.match(source, /tools\.guard/)
|
|
assert.match(source, /agent\/pre-step/)
|
|
assert.match(source, /registerRoleCommand/)
|
|
assert.match(source, /createGuard/)
|
|
assert.match(source, /prepareBootstrap/)
|
|
})
|
|
|
|
test('runtime files have no external @deepseek-ai imports', () => {
|
|
for (const file of [
|
|
'index.js',
|
|
'command-role.js',
|
|
'command-check.js',
|
|
'command-approve.js',
|
|
'tool-approve.js',
|
|
'lib/message.js',
|
|
'lib/guards.js',
|
|
'lib/gates.js',
|
|
'lib/approval.js',
|
|
'lib/bootstrap.js',
|
|
'lib/prestep.js',
|
|
'lib/role.js',
|
|
'lib/legacy.js',
|
|
'lib/selfcheck.js',
|
|
]) {
|
|
const source = readFileSync(join(bundleDir, file), 'utf8')
|
|
assert.doesNotMatch(source, /from ['"]@deepseek-ai\//, `${file} must not import DSH internals`)
|
|
}
|
|
})
|
|
|
|
test('apply registers the request_approval tool when tools.register exists', () => {
|
|
const registered = []
|
|
const ctx = {
|
|
tools: {
|
|
guard() {},
|
|
register(definition) {
|
|
registered.push(definition)
|
|
},
|
|
},
|
|
workingDirectory: { get() { return process.cwd() } },
|
|
logger: { info() {}, warn() {}, error() {} },
|
|
get() { return { ask: async () => ({ answers: [] }) } },
|
|
}
|
|
|
|
apply(ctx)
|
|
|
|
assert.equal(registered.length, 1, 'one tool is registered')
|
|
assert.equal(registered[0].name, 'request_approval')
|
|
assert.deepEqual(registered[0].parameters.required, ['change_id', 'kind'])
|
|
})
|