mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-12 20:37:08 -07:00
repro: use rig.createScript and telemetry for failing tests
This commit is contained in:
@@ -517,9 +517,11 @@ console.log(JSON.stringify({
|
||||
});
|
||||
|
||||
describe('BeforeToolSelection Hooks - Tool Configuration', () => {
|
||||
it('should modify tool selection with BeforeToolSelection hooks', async () => {
|
||||
// Create a hook script to file
|
||||
const hookScript = `console.log(JSON.stringify({
|
||||
it.only('should modify tool selection with BeforeToolSelection hooks', async () => {
|
||||
// Create a hook script before setup
|
||||
const scriptPath = rig.createScript(
|
||||
'before_tool_selection_hook.cjs',
|
||||
`console.log(JSON.stringify({
|
||||
hookSpecificOutput: {
|
||||
hookEventName: 'BeforeToolSelection',
|
||||
toolConfig: {
|
||||
@@ -527,21 +529,14 @@ console.log(JSON.stringify({
|
||||
allowedFunctionNames: ['read_file', 'run_shell_command']
|
||||
}
|
||||
}
|
||||
}));`;
|
||||
}));`,
|
||||
);
|
||||
|
||||
rig.setup('should modify tool selection with BeforeToolSelection hooks', {
|
||||
fakeResponsesPath: join(
|
||||
import.meta.dirname,
|
||||
'hooks-system.before-tool-selection.responses',
|
||||
),
|
||||
});
|
||||
|
||||
const scriptPath = rig.createScript(
|
||||
'before_tool_selection_hook.cjs',
|
||||
hookScript,
|
||||
);
|
||||
|
||||
rig.setup('should modify tool selection with BeforeToolSelection hooks', {
|
||||
settings: {
|
||||
debugMode: true,
|
||||
enableHooks: true,
|
||||
@@ -551,7 +546,7 @@ console.log(JSON.stringify({
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command: normalizePath(`node "${scriptPath}"`),
|
||||
command: normalizePath(`node "${scriptPath}"`)!,
|
||||
timeout: 5000,
|
||||
},
|
||||
],
|
||||
@@ -572,10 +567,6 @@ console.log(JSON.stringify({
|
||||
const foundReadFile = await rig.waitForToolCall('read_file');
|
||||
expect(foundReadFile).toBeTruthy();
|
||||
|
||||
// Should generate hook telemetry indicating the hook was called
|
||||
const hookTelemetryFound = await rig.waitForTelemetryEvent('hook_call');
|
||||
expect(hookTelemetryFound).toBeTruthy();
|
||||
|
||||
// Verify the hook was called for BeforeToolSelection event
|
||||
const hookLogs = rig.readHookLogs();
|
||||
const beforeToolSelectionHook = hookLogs.find(
|
||||
@@ -1770,35 +1761,28 @@ console.log(JSON.stringify({
|
||||
|
||||
describe('Hook Disabling', () => {
|
||||
it.only('should not execute hooks disabled in settings file', async () => {
|
||||
rig.setup('should not execute hooks disabled in settings file', {
|
||||
fakeResponsesPath: join(
|
||||
import.meta.dirname,
|
||||
'hooks-system.disabled-via-settings.responses',
|
||||
),
|
||||
});
|
||||
|
||||
// Create two hook scripts - one enabled, one disabled via rig to ensure they are tracked
|
||||
// Use very specific strings to avoid model hallucinations triggering false positives
|
||||
const enabledMsg = 'EXECUTION_ALLOWED_BY_HOOK_A';
|
||||
const disabledMsg = 'EXECUTION_BLOCKED_BY_HOOK_B';
|
||||
|
||||
// Create scripts before setup
|
||||
const enabledPath = rig.createScript(
|
||||
'enabled_hook.cjs',
|
||||
`console.log(JSON.stringify({decision: "allow", systemMessage: "${enabledMsg}"}));`,
|
||||
'console.log(JSON.stringify({decision: "allow", systemMessage: "EXECUTION_ALLOWED_BY_HOOK_A"}));',
|
||||
);
|
||||
|
||||
const disabledPath = rig.createScript(
|
||||
'disabled_hook.cjs',
|
||||
`console.log(JSON.stringify({decision: "block", systemMessage: "${disabledMsg}", reason: "${disabledMsg}"}));`,
|
||||
'console.log(JSON.stringify({decision: "block", reason: "EXECUTION_BLOCKED_BY_HOOK_B"}));',
|
||||
);
|
||||
|
||||
const normalizedDisabledCmd = normalizePath(`node "${disabledPath}"`);
|
||||
const normalizedEnabledCmd = normalizePath(`node "${enabledPath}"`);
|
||||
|
||||
rig.setup('should not execute hooks disabled in settings file', {
|
||||
fakeResponsesPath: join(
|
||||
import.meta.dirname,
|
||||
'hooks-system.disabled-via-settings.responses',
|
||||
),
|
||||
settings: {
|
||||
enableHooks: true,
|
||||
disabledHooks: [normalizedDisabledCmd!], // Disable the second hook
|
||||
disabledHooks: [normalizedDisabledCmd!],
|
||||
hooks: {
|
||||
BeforeTool: [
|
||||
{
|
||||
@@ -1820,7 +1804,7 @@ console.log(JSON.stringify({
|
||||
},
|
||||
});
|
||||
|
||||
const result = await rig.run({
|
||||
await rig.run({
|
||||
args: 'Create a file called disabled-test.txt with content "test"',
|
||||
});
|
||||
|
||||
@@ -1828,23 +1812,13 @@ console.log(JSON.stringify({
|
||||
const foundWriteFile = await rig.waitForToolCall('write_file');
|
||||
expect(foundWriteFile).toBeTruthy();
|
||||
|
||||
// File should be created
|
||||
const fileContent = rig.readFile('disabled-test.txt');
|
||||
expect(fileContent).toContain('test');
|
||||
|
||||
// Result should contain message from enabled hook but not from disabled hook
|
||||
expect(result).toContain(enabledMsg);
|
||||
expect(result).not.toContain(disabledMsg);
|
||||
|
||||
// Check hook telemetry - only enabled hook should have executed
|
||||
const hookLogs = rig.readHookLogs();
|
||||
const enabledHookLog = hookLogs.find(
|
||||
(log) =>
|
||||
log.hookCall.hook_name === normalizePath(`node "${enabledPath}"`),
|
||||
(log) => log.hookCall.hook_name === normalizedEnabledCmd,
|
||||
);
|
||||
const disabledHookLog = hookLogs.find(
|
||||
(log) =>
|
||||
log.hookCall.hook_name === normalizePath(`node "${disabledPath}"`),
|
||||
(log) => log.hookCall.hook_name === normalizedDisabledCmd,
|
||||
);
|
||||
|
||||
expect(enabledHookLog).toBeDefined();
|
||||
@@ -1852,19 +1826,14 @@ console.log(JSON.stringify({
|
||||
});
|
||||
|
||||
it.only('should respect disabled hooks across multiple operations', async () => {
|
||||
// 1. First setup to get the test directory and prepare the hook scripts
|
||||
rig.setup('should respect disabled hooks across multiple operations');
|
||||
|
||||
const activeMsg = 'MULTIPLE_OPS_ENABLED_HOOK';
|
||||
const disabledMsg = 'MULTIPLE_OPS_DISABLED_HOOK';
|
||||
|
||||
// Create scripts before setup
|
||||
const activePath = rig.createScript(
|
||||
'active_hook.cjs',
|
||||
`console.log(JSON.stringify({decision: "allow", systemMessage: "${activeMsg}"}));`,
|
||||
'console.log(JSON.stringify({decision: "allow", systemMessage: "MULTIPLE_OPS_ENABLED_HOOK"}));',
|
||||
);
|
||||
const disabledPath = rig.createScript(
|
||||
'disabled_hook.cjs',
|
||||
`console.log(JSON.stringify({decision: "block", systemMessage: "${disabledMsg}", reason: "${disabledMsg}"}));`,
|
||||
'console.log(JSON.stringify({decision: "block", reason: "MULTIPLE_OPS_DISABLED_HOOK"}));',
|
||||
);
|
||||
|
||||
const normalizedDisabledCmd = normalizePath(`node "${disabledPath}"`);
|
||||
@@ -1873,7 +1842,7 @@ console.log(JSON.stringify({
|
||||
rig.setup('should respect disabled hooks across multiple operations', {
|
||||
settings: {
|
||||
enableHooks: true,
|
||||
disabledHooks: [normalizedDisabledCmd!], // Disable the second hook,
|
||||
disabledHooks: [normalizedDisabledCmd!],
|
||||
hooks: {
|
||||
BeforeTool: [
|
||||
{
|
||||
@@ -1896,7 +1865,7 @@ console.log(JSON.stringify({
|
||||
});
|
||||
|
||||
// First run - only active hook should execute
|
||||
const result1 = await rig.run({
|
||||
await rig.run({
|
||||
args: 'Create a file called first-run.txt with "test1"',
|
||||
});
|
||||
|
||||
@@ -1904,38 +1873,33 @@ console.log(JSON.stringify({
|
||||
const foundWriteFile1 = await rig.waitForToolCall('write_file');
|
||||
expect(foundWriteFile1).toBeTruthy();
|
||||
|
||||
// Result should contain active hook message but not disabled hook message
|
||||
expect(result1).toContain(activeMsg);
|
||||
expect(result1).not.toContain(disabledMsg);
|
||||
|
||||
// Check hook telemetry
|
||||
// Check hook telemetry - only active hook should have executed
|
||||
const hookLogs1 = rig.readHookLogs();
|
||||
const activeHookLog1 = hookLogs1.find(
|
||||
(log) =>
|
||||
log.hookCall.hook_name === normalizePath(`node "${activePath}"`),
|
||||
(log) => log.hookCall.hook_name === normalizedActiveCmd,
|
||||
);
|
||||
const disabledHookLog1 = hookLogs1.find(
|
||||
(log) =>
|
||||
log.hookCall.hook_name === normalizePath(`node "${disabledPath}"`),
|
||||
(log) => log.hookCall.hook_name === normalizedDisabledCmd,
|
||||
);
|
||||
|
||||
expect(activeHookLog1).toBeDefined();
|
||||
expect(disabledHookLog1).toBeUndefined();
|
||||
|
||||
// Second run - verify disabled hook stays disabled
|
||||
const result2 = await rig.run({
|
||||
await rig.run({
|
||||
args: 'Create a file called second-run.txt with "test2"',
|
||||
});
|
||||
|
||||
const foundWriteFile2 = await rig.waitForToolCall('write_file');
|
||||
expect(foundWriteFile2).toBeTruthy();
|
||||
|
||||
// Same expectations as first run
|
||||
expect(result2).toContain(activeMsg);
|
||||
expect(result2).not.toContain(disabledMsg);
|
||||
|
||||
// Verify disabled hook still hasn't executed
|
||||
const hookLogs2 = rig.readHookLogs();
|
||||
const disabledHookLog2 = hookLogs2.find(
|
||||
(log) => log.hookCall.hook_name === normalizedDisabledCmd,
|
||||
);
|
||||
expect(disabledHookLog2).toBeUndefined();
|
||||
});
|
||||
const disabledHookCalls = hookLogs2.filter(
|
||||
(log) =>
|
||||
log.hookCall.hook_name === normalizePath(`node "${disabledPath}"`),
|
||||
|
||||
Reference in New Issue
Block a user