From 2fe6c336d71cd36e7983603491601307a5f674e7 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Wed, 4 Feb 2026 11:44:33 -0500 Subject: [PATCH] test: add stress test and PostToolUse preservation tests - Add stress test for 15 duplicate hooks (verifies O(n) scaling) - Add test verifying PostToolUse/PreToolCall remain untouched - Add optional debug logging when duplicates removed (CCS_DEBUG) Addresses review feedback from PR #452 --- src/utils/websearch/hook-utils.ts | 8 +- tests/unit/utils/websearch/hook-utils.test.ts | 79 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/utils/websearch/hook-utils.ts b/src/utils/websearch/hook-utils.ts index 7a551380..65607b34 100644 --- a/src/utils/websearch/hook-utils.ts +++ b/src/utils/websearch/hook-utils.ts @@ -48,5 +48,11 @@ export function deduplicateCcsHooks(settings: Record): boolean return false; // Remove subsequent duplicates }); - return hooks.PreToolUse.length < originalLength; + const newLength = hooks.PreToolUse.length; + if (process.env.CCS_DEBUG && newLength < originalLength) { + const removedCount = originalLength - newLength; + console.error(`Removed ${removedCount} duplicate CCS WebSearch hook(s)`); + } + + return newLength < originalLength; } diff --git a/tests/unit/utils/websearch/hook-utils.test.ts b/tests/unit/utils/websearch/hook-utils.test.ts index a3f7bd3e..64373365 100644 --- a/tests/unit/utils/websearch/hook-utils.test.ts +++ b/tests/unit/utils/websearch/hook-utils.test.ts @@ -289,4 +289,83 @@ describe('deduplicateCcsHooks', () => { expect(result).toBe(false); expect(settings.hooks.PreToolUse).toHaveLength(0); }); + + test('Stress test: 15 duplicate CCS WebSearch hooks', () => { + const ccsHook = { + matcher: 'WebSearch', + hooks: [ + { + command: 'node /home/user/.ccs/hooks/websearch-transformer/index.js', + }, + ], + }; + const settings = { + hooks: { + PreToolUse: Array(15).fill(ccsHook), + }, + }; + const result = deduplicateCcsHooks(settings); + expect(result).toBe(true); + expect(settings.hooks.PreToolUse).toHaveLength(1); + expect(settings.hooks.PreToolUse[0]).toEqual(ccsHook); + }); + + test('Leaves PostToolUse and PreToolCall untouched', () => { + const postToolUseHook1 = { + matcher: 'CustomMatcher1', + hooks: [{ command: 'custom-command-1' }], + }; + const postToolUseHook2 = { + matcher: 'CustomMatcher2', + hooks: [{ command: 'custom-command-2' }], + }; + const preToolCallHook = { + matcher: 'CustomMatcher3', + hooks: [{ command: 'custom-command-3' }], + }; + const settings = { + hooks: { + PreToolUse: [ + { + matcher: 'WebSearch', + hooks: [ + { + command: 'node /path1/.ccs/hooks/websearch-transformer/index.js', + }, + ], + }, + { + matcher: 'WebSearch', + hooks: [ + { + command: 'node /path2/.ccs/hooks/websearch-transformer/index.js', + }, + ], + }, + { + matcher: 'WebSearch', + hooks: [ + { + command: 'node /path3/.ccs/hooks/websearch-transformer/index.js', + }, + ], + }, + ], + PostToolUse: [postToolUseHook1, postToolUseHook2], + PreToolCall: [preToolCallHook], + }, + }; + const result = deduplicateCcsHooks(settings); + expect(result).toBe(true); + // PreToolUse should be deduplicated to 1 hook + expect(settings.hooks.PreToolUse).toHaveLength(1); + expect(settings.hooks.PreToolUse[0].matcher).toBe('WebSearch'); + // PostToolUse should remain unchanged with 2 hooks + expect(settings.hooks.PostToolUse).toHaveLength(2); + expect(settings.hooks.PostToolUse[0]).toEqual(postToolUseHook1); + expect(settings.hooks.PostToolUse[1]).toEqual(postToolUseHook2); + // PreToolCall should remain unchanged with 1 hook + expect(settings.hooks.PreToolCall).toHaveLength(1); + expect(settings.hooks.PreToolCall[0]).toEqual(preToolCallHook); + }); });