#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
TESTS_RUN=0
TESTS_PASSED=0
TESTS_FAILED=0
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
run_test() {
local test_name="$1"
local test_func="$2"
TESTS_RUN=$((TESTS_RUN + 1))
echo -n " Testing: $test_name... "
if $test_func; then
echo -e "${GREEN}PASSED${NC}"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
echo -e "${RED}FAILED${NC}"
TESTS_FAILED=$((TESTS_FAILED + 1))
fi
}
test_cleanup_script_exists() {
local script="$PROJECT_ROOT/scripts/posttooluse-commit-cleanup.sh"
if [ ! -f "$script" ]; then
echo " Error: posttooluse-commit-cleanup.sh not found"
return 1
fi
return 0
}
test_cleanup_script_executable() {
local script="$PROJECT_ROOT/scripts/posttooluse-commit-cleanup.sh"
if [ ! -x "$script" ]; then
echo " Error: posttooluse-commit-cleanup.sh is not executable"
return 1
fi
return 0
}
test_pretooluse_has_commit_guard() {
local script="$PROJECT_ROOT/scripts/pretooluse-guard.sh"
if ! grep -q "git[[:space:]]*commit" "$script" 2>/dev/null; then
echo " Error: git commit detection not found in pretooluse-guard.sh"
return 1
fi
if ! grep -Eq "review-approved.json|review-result.json" "$script" 2>/dev/null; then
echo " Error: review artifact check not found in pretooluse-guard.sh"
return 1
fi
return 0
}
test_pretooluse_has_block_message() {
local script="$PROJECT_ROOT/scripts/pretooluse-guard.sh"
if ! grep -q "deny_git_commit_no_review" "$script" 2>/dev/null; then
echo " Error: deny_git_commit_no_review message not found"
return 1
fi
return 0
}
test_pretooluse_has_bookkeeping_exemption_hardening() {
local script="$PROJECT_ROOT/scripts/pretooluse-guard.sh"
grep -q "BOOKKEEPING_ONLY" "$script" 2>/dev/null || {
echo " Error: bookkeeping exemption state not found"
return 1
}
grep -q "commit-cleanup-audit.jsonl" "$script" 2>/dev/null || {
echo " Error: bookkeeping audit append not found"
return 1
}
grep -Eq 'git\[\[:space:\]\]\+\(add\|restore\|reset\|rm\)|git\[.*\(add\|restore\|reset\|rm\)' "$script" 2>/dev/null || {
echo " Error: index-mutating command hardening not found"
return 1
}
grep -Eq -- '--patch|--interactive|\-\*p\*|PATHSPEC_SUSPECT' "$script" 2>/dev/null || {
echo " Error: pathspec/patch hardening not found"
return 1
}
return 0
}
test_cleanup_detects_git_commit() {
local script="$PROJECT_ROOT/scripts/posttooluse-commit-cleanup.sh"
if ! grep -q "git[[:space:]]*commit" "$script" 2>/dev/null; then
echo " Error: git commit detection not found in cleanup script"
return 1
fi
return 0
}
test_cleanup_removes_state_file() {
local script="$PROJECT_ROOT/scripts/posttooluse-commit-cleanup.sh"
if ! grep -q 'rm -f.*REVIEW_STATE_FILE' "$script" 2>/dev/null; then
echo " Error: state file removal logic not found"
return 1
fi
if ! grep -Eq "review-approved.json|review-result.json" "$script" 2>/dev/null; then
echo " Error: review artifact path definition not found"
return 1
fi
return 0
}
test_hooks_has_commit_cleanup() {
local hooks_file="$PROJECT_ROOT/hooks/hooks.json"
if ! command -v jq &> /dev/null; then
echo " Warning: jq not available, skipping JSON validation"
if ! grep -Eq "posttooluse-commit-cleanup|hook commit-cleanup" "$hooks_file" 2>/dev/null; then
echo " Error: commit-cleanup hook not registered in hooks.json"
return 1
fi
return 0
fi
if ! jq -e '.hooks.PostToolUse[] | select(.matcher == "Bash") | .hooks[] | select(.command | contains("posttooluse-commit-cleanup") or contains("hook commit-cleanup"))' "$hooks_file" > /dev/null 2>&1; then
echo " Error: commit-cleanup hook not properly registered for Bash in PostToolUse"
return 1
fi
return 0
}
test_plugin_hooks_has_commit_cleanup() {
local hooks_file="$PROJECT_ROOT/.claude-plugin/hooks.json"
if ! grep -Eq "posttooluse-commit-cleanup|hook commit-cleanup" "$hooks_file" 2>/dev/null; then
echo " Error: commit-cleanup hook not registered in .claude-plugin/hooks.json"
return 1
fi
return 0
}
test_config_has_commit_guard_option() {
local config_template="$PROJECT_ROOT/templates/.claude-code-harness.config.yaml.template"
if ! grep -q "commit_guard:" "$config_template" 2>/dev/null; then
echo " Error: commit_guard option not found in config template"
return 1
fi
return 0
}
test_cleanup_preserves_on_error() {
local script="$PROJECT_ROOT/scripts/posttooluse-commit-cleanup.sh"
if ! grep -Eq "error|fatal|failed|nothing to commit" "$script" 2>/dev/null; then
echo " Error: error detection logic not found in cleanup script"
return 1
fi
return 0
}
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Commit Guard テスト"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo " [PreToolUse Guard]"
run_test "pretooluse-guard.sh に git commit 検出ロジックがある" test_pretooluse_has_commit_guard
run_test "pretooluse-guard.sh にブロックメッセージがある" test_pretooluse_has_block_message
run_test "bookkeeping 免除と hardening がある" test_pretooluse_has_bookkeeping_exemption_hardening
echo ""
echo " [PostToolUse Cleanup]"
run_test "posttooluse-commit-cleanup.sh が存在する" test_cleanup_script_exists
run_test "posttooluse-commit-cleanup.sh に実行権限がある" test_cleanup_script_executable
run_test "posttooluse-commit-cleanup.sh に git commit 検出がある" test_cleanup_detects_git_commit
run_test "posttooluse-commit-cleanup.sh に状態ファイル削除ロジックがある" test_cleanup_removes_state_file
run_test "posttooluse-commit-cleanup.sh がエラー時に状態を保持する" test_cleanup_preserves_on_error
echo ""
echo " [Hooks Integration]"
run_test "hooks.json に commit-cleanup フックが登録されている" test_hooks_has_commit_cleanup
run_test ".claude-plugin/hooks.json にも commit-cleanup フックがある" test_plugin_hooks_has_commit_cleanup
echo ""
echo " [Configuration]"
run_test "config テンプレートに commit_guard 設定がある" test_config_has_commit_guard_option
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " テスト結果: $TESTS_PASSED/$TESTS_RUN passed"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
if [ "$TESTS_FAILED" -gt 0 ]; then
exit 1
fi
exit 0