mirror of
https://github.com/Egonex-AI/Understand-Anything.git
synced 2026-10-06 14:33:10 +08:00
test(hooks): cover PostToolUse trigger guards
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
import { spawnSync } from 'node:child_process';
|
import { spawnSync } from 'node:child_process';
|
||||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
import {
|
||||||
|
mkdirSync,
|
||||||
|
mkdtempSync,
|
||||||
|
readFileSync,
|
||||||
|
rmSync,
|
||||||
|
writeFileSync,
|
||||||
|
} from 'node:fs';
|
||||||
import { tmpdir } from 'node:os';
|
import { tmpdir } from 'node:os';
|
||||||
import { dirname, join, resolve } from 'node:path';
|
import { dirname, join, resolve } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
@@ -13,34 +19,65 @@ const hookScript = join(
|
|||||||
'hooks',
|
'hooks',
|
||||||
'post-tool-use-auto-update.mjs',
|
'post-tool-use-auto-update.mjs',
|
||||||
);
|
);
|
||||||
|
const hooksConfig = JSON.parse(
|
||||||
|
readFileSync(
|
||||||
|
join(repoRoot, 'understand-anything-plugin', 'hooks', 'hooks.json'),
|
||||||
|
'utf8',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
function runHook({
|
function runHook({
|
||||||
command = 'git commit -m "test"',
|
command = 'git commit -m "test"',
|
||||||
autoUpdate = true,
|
autoUpdate = true,
|
||||||
|
configContents,
|
||||||
|
createConfig = true,
|
||||||
createGraph = true,
|
createGraph = true,
|
||||||
|
dataDirName = '.understand-anything',
|
||||||
|
input,
|
||||||
|
pluginRoot,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const projectRoot = mkdtempSync(join(tmpdir(), 'ua-post-tool-use-'));
|
const projectRoot = mkdtempSync(join(tmpdir(), 'ua-post-tool-use-'));
|
||||||
const dataDir = join(projectRoot, '.understand-anything');
|
const dataDir = join(projectRoot, dataDirName);
|
||||||
mkdirSync(dataDir);
|
mkdirSync(dataDir);
|
||||||
writeFileSync(join(dataDir, 'config.json'), JSON.stringify({ autoUpdate }));
|
if (createConfig) {
|
||||||
|
writeFileSync(
|
||||||
|
join(dataDir, 'config.json'),
|
||||||
|
configContents ?? JSON.stringify({ autoUpdate }),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (createGraph) writeFileSync(join(dataDir, 'knowledge-graph.json'), '{}');
|
if (createGraph) writeFileSync(join(dataDir, 'knowledge-graph.json'), '{}');
|
||||||
|
|
||||||
const pluginRoot = join(projectRoot, 'plugin root');
|
const resolvedPluginRoot = pluginRoot ?? join(projectRoot, 'plugin root');
|
||||||
const result = spawnSync(process.execPath, [hookScript], {
|
const result = spawnSync(process.execPath, [hookScript], {
|
||||||
cwd: projectRoot,
|
cwd: projectRoot,
|
||||||
input: JSON.stringify({
|
input:
|
||||||
tool_name: 'Bash',
|
input ??
|
||||||
tool_input: { command },
|
JSON.stringify({
|
||||||
}),
|
tool_name: 'Bash',
|
||||||
env: { ...process.env, CLAUDE_PLUGIN_ROOT: pluginRoot },
|
tool_input: { command },
|
||||||
|
}),
|
||||||
|
env: { ...process.env, CLAUDE_PLUGIN_ROOT: resolvedPluginRoot },
|
||||||
encoding: 'utf8',
|
encoding: 'utf8',
|
||||||
});
|
});
|
||||||
|
|
||||||
rmSync(projectRoot, { recursive: true, force: true });
|
rmSync(projectRoot, { recursive: true, force: true });
|
||||||
return { ...result, pluginRoot };
|
return { ...result, pluginRoot: resolvedPluginRoot };
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('PostToolUse auto-update hook', () => {
|
describe('PostToolUse auto-update hook', () => {
|
||||||
|
it('is registered as the Bash PostToolUse handler', () => {
|
||||||
|
const registration = hooksConfig.hooks.PostToolUse[0];
|
||||||
|
|
||||||
|
expect(registration.matcher).toBe('Bash');
|
||||||
|
expect(registration.hooks).toEqual([
|
||||||
|
{
|
||||||
|
type: 'command',
|
||||||
|
command:
|
||||||
|
'node "${CLAUDE_PLUGIN_ROOT}/hooks/post-tool-use-auto-update.mjs"',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('injects the auto-update instruction as PostToolUse additional context', () => {
|
it('injects the auto-update instruction as PostToolUse additional context', () => {
|
||||||
const result = runHook();
|
const result = runHook();
|
||||||
const output = JSON.parse(result.stdout);
|
const output = JSON.parse(result.stdout);
|
||||||
@@ -60,6 +97,37 @@ describe('PostToolUse auto-update hook', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each(['commit', 'merge', 'cherry-pick', 'rebase'])(
|
||||||
|
'recognizes git %s as a graph-changing operation',
|
||||||
|
(operation) => {
|
||||||
|
const result = runHook({ command: `git ${operation} example` });
|
||||||
|
|
||||||
|
expect(result.status).toBe(0);
|
||||||
|
expect(
|
||||||
|
JSON.parse(result.stdout).hookSpecificOutput.hookEventName,
|
||||||
|
).toBe('PostToolUse');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it('preserves Windows-style plugin paths as valid JSON', () => {
|
||||||
|
const pluginRoot = 'C:\\Users\\Example Person\\understand-anything';
|
||||||
|
const result = runHook({ pluginRoot });
|
||||||
|
const output = JSON.parse(result.stdout);
|
||||||
|
|
||||||
|
expect(output.hookSpecificOutput.additionalContext).toContain(
|
||||||
|
`${pluginRoot}/hooks/auto-update-prompt.md`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports the legacy .ua data directory', () => {
|
||||||
|
const result = runHook({ dataDirName: '.ua' });
|
||||||
|
|
||||||
|
expect(result.status).toBe(0);
|
||||||
|
expect(JSON.parse(result.stdout).hookSpecificOutput.hookEventName).toBe(
|
||||||
|
'PostToolUse',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('stays silent for unrelated Bash commands', () => {
|
it('stays silent for unrelated Bash commands', () => {
|
||||||
const result = runHook({ command: 'git status --short' });
|
const result = runHook({ command: 'git status --short' });
|
||||||
|
|
||||||
@@ -73,4 +141,17 @@ describe('PostToolUse auto-update hook', () => {
|
|||||||
expect(result.status).toBe(0);
|
expect(result.status).toBe(0);
|
||||||
expect(result.stdout).toBe('');
|
expect(result.stdout).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['malformed stdin', { input: '{not-json' }],
|
||||||
|
['a missing config', { createConfig: false }],
|
||||||
|
['a malformed config', { configContents: '{not-json' }],
|
||||||
|
['a missing graph', { createGraph: false }],
|
||||||
|
])('stays silent for %s', (_scenario, options) => {
|
||||||
|
const result = runHook(options);
|
||||||
|
|
||||||
|
expect(result.status).toBe(0);
|
||||||
|
expect(result.stderr).toBe('');
|
||||||
|
expect(result.stdout).toBe('');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user