Files
clawpanel/tests/hermes-hooks-config.test.js
2026-05-26 04:04:22 +08:00

130 lines
3.9 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import {
buildHermesHooksConfigValues,
mergeHermesHooksConfig,
} from '../scripts/dev-api.js'
test('Hermes hooks 配置读取会提供安全默认值', () => {
const values = buildHermesHooksConfigValues({})
assert.deepEqual(values, {
hooksAutoAccept: false,
hooksJson: '{}',
})
})
test('Hermes hooks 配置读取会格式化事件映射', () => {
const values = buildHermesHooksConfigValues({
hooks_auto_accept: true,
hooks: {
pre_tool_call: [
{
matcher: 'terminal',
command: '~/.hermes/agent-hooks/block-rm-rf.sh',
timeout: 10,
},
],
pre_llm_call: [
{
command: '~/.hermes/agent-hooks/inject-cwd-context.sh',
},
],
},
})
const hooks = JSON.parse(values.hooksJson)
assert.equal(values.hooksAutoAccept, true)
assert.equal(hooks.pre_tool_call[0].matcher, 'terminal')
assert.equal(hooks.pre_tool_call[0].command, '~/.hermes/agent-hooks/block-rm-rf.sh')
assert.equal(hooks.pre_tool_call[0].timeout, 10)
assert.equal(hooks.pre_llm_call[0].command, '~/.hermes/agent-hooks/inject-cwd-context.sh')
})
test('Hermes hooks 配置保存会保留未知字段并写入 hooks', () => {
const next = mergeHermesHooksConfig({
model: { provider: 'openrouter' },
hooks: {
pre_tool_call: [
{
matcher: 'terminal',
command: 'old-hook.sh',
extra_flag: 'keep-old',
},
],
},
memory: { memory_enabled: true },
}, {
hooksAutoAccept: 'true',
hooksJson: JSON.stringify({
pre_tool_call: [
{
matcher: 'terminal',
command: '~/.hermes/agent-hooks/block-rm-rf.sh',
timeout: 10,
extra_flag: 'keep-hook',
},
],
post_tool_call: [
{
matcher: 'write_file|patch',
command: '~/.hermes/agent-hooks/auto-format.sh',
},
],
}),
})
assert.deepEqual(next.model, { provider: 'openrouter' })
assert.deepEqual(next.memory, { memory_enabled: true })
assert.equal(next.hooks_auto_accept, true)
assert.equal(next.hooks.pre_tool_call[0].command, '~/.hermes/agent-hooks/block-rm-rf.sh')
assert.equal(next.hooks.pre_tool_call[0].timeout, 10)
assert.equal(next.hooks.pre_tool_call[0].extra_flag, 'keep-hook')
assert.equal(next.hooks.post_tool_call[0].matcher, 'write_file|patch')
})
test('Hermes hooks 配置保存空对象会移除 hooks 但保留自动接受开关', () => {
const next = mergeHermesHooksConfig({
hooks_auto_accept: true,
hooks: {
pre_tool_call: [{ command: 'old-hook.sh' }],
},
streaming: { enabled: true },
}, {
hooksAutoAccept: false,
hooksJson: '{}',
})
assert.equal(next.hooks, undefined)
assert.equal(next.hooks_auto_accept, false)
assert.deepEqual(next.streaming, { enabled: true })
})
test('Hermes hooks 配置保存会拒绝非法 JSON、事件、结构、命令和超时', () => {
assert.throws(
() => mergeHermesHooksConfig({}, { hooksJson: '[' }),
/hooks JSON/,
)
assert.throws(
() => mergeHermesHooksConfig({}, { hooksJson: JSON.stringify({ bad_event: [{ command: 'hook.sh' }] }) }),
/hooks\.bad_event/,
)
assert.throws(
() => mergeHermesHooksConfig({}, { hooksJson: JSON.stringify({ pre_tool_call: { command: 'hook.sh' } }) }),
/hooks\.pre_tool_call/,
)
assert.throws(
() => mergeHermesHooksConfig({}, { hooksJson: JSON.stringify({ pre_tool_call: ['hook.sh'] }) }),
/hooks\.pre_tool_call\.0/,
)
assert.throws(
() => mergeHermesHooksConfig({}, { hooksJson: JSON.stringify({ pre_tool_call: [{ command: '' }] }) }),
/hooks\.pre_tool_call\.0\.command/,
)
assert.throws(
() => mergeHermesHooksConfig({}, { hooksJson: JSON.stringify({ pre_tool_call: [{ command: 'hook.sh', timeout: 0 }] }) }),
/hooks\.pre_tool_call\.0\.timeout/,
)
})