Merge pull request #371 from kaitranntt/kai/fix/uninstall-hooks-cleanup

fix: uninstall hooks cleanup + test isolation
This commit is contained in:
Kai (Tam Nhu) Tran authored and GitHub committed 2026-01-25 21:53:00 -05:00
commit 2f66f74ff9
29 files changed
+677 -59

No files matched your search

+67
View File
@@ -118,6 +118,8 @@ test_case "Empty uninstall: Reports 0 items removed" "Zero removed count" bash -
# ============================================================================
# Install first so we can test uninstall
# NOTE: --install is currently a no-op (under development), but we call it
# to test the install/uninstall cycle works without errors
echo ""
echo -e "${YELLOW}Setting up for install/uninstall cycle test...${NC}"
bash -c "HOME='$TEST_HOME' '$CCS_PATH' --install > /dev/null 2>&1"
@@ -281,6 +283,71 @@ test_case "Edge case: Missing parent directories" "Handles missing .claude direc
[[ \$exit_code -eq 0 ]]
"
# ============================================================================
# TEST SECTION 7: PER-PROFILE HOOK BEHAVIOR (Global settings untouched)
# ============================================================================
echo ""
echo "========================================"
echo "SECTION 7: PER-PROFILE HOOK BEHAVIOR"
echo "========================================"
test_case "Per-profile: Uninstall does NOT touch global settings.json" "Global settings unchanged" bash -c "
# Setup: Create settings.json with user hook
mkdir -p '$TEST_CLAUDE_DIR'
cat > '$TEST_CLAUDE_DIR/settings.json' << 'EOFINNER'
{
\"hooks\": {
\"PreToolUse\": [
{ \"matcher\": \"WebSearch\", \"hooks\": [{ \"command\": \"my-custom-hook.sh\" }] }
]
}
}
EOFINNER
# Uninstall
HOME='$TEST_HOME' '$CCS_PATH' --uninstall > /dev/null 2>&1
# Verify global settings unchanged
grep -q 'my-custom-hook' '$TEST_CLAUDE_DIR/settings.json'
"
test_case "Per-profile: Uninstall preserves user hooks in global settings" "User hooks in global preserved" bash -c "
# Setup: Create settings.json with CCS hook (old format - shouldn't be touched)
mkdir -p '$TEST_CLAUDE_DIR'
cat > '$TEST_CLAUDE_DIR/settings.json' << 'EOFINNER'
{
\"hooks\": {
\"PreToolUse\": [
{ \"matcher\": \"WebSearch\", \"hooks\": [{ \"command\": \"node ~/.ccs/hooks/websearch-transformer.cjs\" }] },
{ \"matcher\": \"WebSearch\", \"hooks\": [{ \"command\": \"my-custom-hook.sh\" }] }
]
}
}
EOFINNER
# Uninstall - should NOT modify global settings
HOME='$TEST_HOME' '$CCS_PATH' --uninstall > /dev/null 2>&1
# Both hooks should still be in global settings (not touched)
grep -q 'websearch-transformer' '$TEST_CLAUDE_DIR/settings.json' &&
grep -q 'my-custom-hook' '$TEST_CLAUDE_DIR/settings.json'
"
test_case "Per-profile: Migration marker cleanup on uninstall" "Marker file removed" bash -c "
# Setup: Create ~/.ccs/.hook-migrated marker
mkdir -p '$TEST_HOME/.ccs'
echo '2026-01-25T00:00:00.000Z' > '$TEST_HOME/.ccs/.hook-migrated'
# Uninstall
HOME='$TEST_HOME' '$CCS_PATH' --uninstall > /dev/null 2>&1
# Verify marker removed
[[ ! -f '$TEST_HOME/.ccs/.hook-migrated' ]]
"
test_case "Per-profile: Handles missing settings.json" "No error when settings.json missing" bash -c "
# Ensure settings.json doesn't exist
rm -f '$TEST_CLAUDE_DIR/settings.json'
HOME='$TEST_HOME' '$CCS_PATH' --uninstall > /dev/null 2>&1
exit_code=\$?
[[ \$exit_code -eq 0 ]]
"
# ============================================================================
# SUMMARY
# ============================================================================
+110
View File
@@ -191,6 +191,8 @@ Test-Case "Empty uninstall: Reports 0 items removed" "Zero removed count" {
# ============================================================================
# Install first so we can test uninstall
# NOTE: --install is currently a no-op (under development), but we call it
# to test the install/uninstall cycle works without errors
Write-Host ""
Write-ColorOutput "Setting up for install/uninstall cycle test..." "Yellow"
$originalHome = $env:HOME
@@ -425,6 +427,114 @@ Test-Case "Edge case: Missing parent directories" "Handles missing .claude direc
}
}
# ============================================================================
# TEST SECTION 7: SETTINGS.JSON HOOK CLEANUP
# ============================================================================
Write-Host ""
Write-ColorOutput "========================================" "Yellow"
Write-ColorOutput "SECTION 7: SETTINGS.JSON HOOK CLEANUP" "Yellow"
Write-ColorOutput "========================================" "Yellow"
Test-Case "Hook cleanup: Removes CCS WebSearch hook from settings.json" "Hook removed" {
# Setup: Create settings.json with CCS hook
New-Item -ItemType Directory -Path $TestClaudeDir -Force | Out-Null
$settingsPath = Join-Path $TestClaudeDir "settings.json"
$settingsContent = @'
{
"hooks": {
"PreToolUse": [
{ "matcher": "WebSearch", "hooks": [{ "command": "node ~/.ccs/hooks/websearch-transformer.cjs" }] }
]
}
}
'@
Set-Content -Path $settingsPath -Value $settingsContent
# Install then uninstall
$env:HOME = $TestHome
try {
& $CcsPath --install | Out-Null
& $CcsPath --uninstall | Out-Null
} catch { }
# Check hook removed
$content = Get-Content -Path $settingsPath -Raw
-not ($content -match "websearch-transformer")
}
Test-Case "Hook cleanup: Preserves user-defined WebSearch hooks" "User hooks kept" {
# Setup: Create settings.json with user hook
New-Item -ItemType Directory -Path $TestClaudeDir -Force | Out-Null
$settingsPath = Join-Path $TestClaudeDir "settings.json"
$settingsContent = @'
{
"hooks": {
"PreToolUse": [
{ "matcher": "WebSearch", "hooks": [{ "command": "my-custom-hook.sh" }] }
]
}
}
'@
Set-Content -Path $settingsPath -Value $settingsContent
# Uninstall
$env:HOME = $TestHome
try {
& $CcsPath --uninstall | Out-Null
} catch { }
# Check user hook preserved
$content = Get-Content -Path $settingsPath -Raw
$content -match "my-custom-hook"
}
Test-Case "Hook cleanup: Handles mixed CCS and user hooks" "Only CCS hook removed" {
# Setup: Create settings.json with both CCS and user hooks
New-Item -ItemType Directory -Path $TestClaudeDir -Force | Out-Null
$settingsPath = Join-Path $TestClaudeDir "settings.json"
$settingsContent = @'
{
"hooks": {
"PreToolUse": [
{ "matcher": "WebSearch", "hooks": [{ "command": "node ~/.ccs/hooks/websearch-transformer.cjs" }] },
{ "matcher": "WebSearch", "hooks": [{ "command": "my-custom-hook.sh" }] },
{ "matcher": "Bash", "hooks": [{ "command": "bash-hook.sh" }] }
]
}
}
'@
Set-Content -Path $settingsPath -Value $settingsContent
# Uninstall
$env:HOME = $TestHome
try {
& $CcsPath --uninstall | Out-Null
} catch { }
# Check CCS hook removed, user hooks preserved
$content = Get-Content -Path $settingsPath -Raw
(-not ($content -match "websearch-transformer")) -and
($content -match "my-custom-hook") -and
($content -match "bash-hook")
}
Test-Case "Hook cleanup: Handles missing settings.json" "No error when settings.json missing" {
# Ensure settings.json doesn't exist
$settingsPath = Join-Path $TestClaudeDir "settings.json"
if (Test-Path $settingsPath) {
Remove-Item $settingsPath -Force
}
$env:HOME = $TestHome
try {
& $CcsPath --uninstall | Out-Null
return $true
} catch {
return $false
}
}
# ============================================================================
# SUMMARY
# ============================================================================
+2 -3
View File
@@ -39,9 +39,8 @@ describe('integration: special commands', () => {
it('handles --uninstall command', () => {
const output = execSync(`node ${ccsPath} --uninstall`, { encoding: 'utf8' });
assert(output.includes('Feature not available'));
assert(output.includes('under development'));
assert(output.includes('.claude/ integration testing'));
assert(output.includes('Uninstalling CCS'));
assert(output.includes('[OK] Uninstall complete!') || output.includes('Nothing to uninstall'));
});
describe('ccs update command flags', () => {