|
| 1 | +#!/bin/bash |
| 2 | +# CodeSensei — Profile Tools Regression Tests |
| 3 | +# Covers doctor/import scripts and secret redaction in command logging. |
| 4 | + |
| 5 | +set -euo pipefail |
| 6 | + |
| 7 | +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" |
| 8 | +TEST_HOME=$(mktemp -d) |
| 9 | +export HOME="$TEST_HOME" |
| 10 | + |
| 11 | +PASS=0 |
| 12 | +FAIL=0 |
| 13 | + |
| 14 | +GREEN='\033[0;32m' |
| 15 | +RED='\033[0;31m' |
| 16 | +NC='\033[0m' |
| 17 | + |
| 18 | +pass() { PASS=$((PASS + 1)); echo -e " ${GREEN}✓${NC} $1"; } |
| 19 | +fail() { FAIL=$((FAIL + 1)); echo -e " ${RED}✗${NC} $1: $2"; } |
| 20 | + |
| 21 | +cleanup() { |
| 22 | + rm -rf "$TEST_HOME" |
| 23 | +} |
| 24 | +trap cleanup EXIT |
| 25 | + |
| 26 | +setup_profile() { |
| 27 | + rm -rf "$TEST_HOME/.code-sensei" |
| 28 | + mkdir -p "$TEST_HOME/.code-sensei" |
| 29 | + cat > "$TEST_HOME/.code-sensei/profile.json" <<'PROFILE' |
| 30 | +{ |
| 31 | + "version": "1.0.0", |
| 32 | + "belt": "yellow", |
| 33 | + "xp": 125, |
| 34 | + "session_concepts": [], |
| 35 | + "concepts_seen": ["html"], |
| 36 | + "concepts_mastered": ["html"], |
| 37 | + "streak": {"current": 3, "longest": 3, "last_session_date": "2026-03-18"}, |
| 38 | + "quizzes": {"total": 4, "correct": 3, "current_streak": 2, "longest_streak": 2}, |
| 39 | + "quiz_history": [] |
| 40 | +} |
| 41 | +PROFILE |
| 42 | +} |
| 43 | + |
| 44 | +create_wrapped_export() { |
| 45 | + local export_file="$1" |
| 46 | + cat > "$export_file" <<'EXPORT' |
| 47 | +{ |
| 48 | + "schema_version": "1.0", |
| 49 | + "exported_at": "2026-03-19T12:00:00Z", |
| 50 | + "plugin_version": "1.1.0", |
| 51 | + "profile": { |
| 52 | + "version": "1.0.0", |
| 53 | + "belt": "green", |
| 54 | + "xp": 3800, |
| 55 | + "session_concepts": [], |
| 56 | + "concepts_seen": ["html", "css", "js-basics"], |
| 57 | + "concepts_mastered": ["html", "css"], |
| 58 | + "streak": {"current": 9, "longest": 9, "last_session_date": "2026-03-19"}, |
| 59 | + "quizzes": {"total": 18, "correct": 14, "current_streak": 5, "longest_streak": 6}, |
| 60 | + "quiz_history": [] |
| 61 | + } |
| 62 | +} |
| 63 | +EXPORT |
| 64 | +} |
| 65 | + |
| 66 | +echo "" |
| 67 | +echo "━━━ CodeSensei Profile Tools Tests ━━━" |
| 68 | +echo "" |
| 69 | + |
| 70 | +# ============================================================ |
| 71 | +# TEST GROUP 1: doctor.sh |
| 72 | +# ============================================================ |
| 73 | +echo "▸ doctor.sh" |
| 74 | + |
| 75 | +rm -rf "$TEST_HOME/.code-sensei" |
| 76 | +OUTPUT=$(bash "$SCRIPT_DIR/scripts/doctor.sh") |
| 77 | + |
| 78 | +if echo "$OUTPUT" | jq . >/dev/null 2>&1; then |
| 79 | + pass "doctor output is valid JSON" |
| 80 | +else |
| 81 | + fail "doctor output is valid JSON" "got: $OUTPUT" |
| 82 | +fi |
| 83 | + |
| 84 | +STATUS=$(echo "$OUTPUT" | jq -r '.status') |
| 85 | +PROFILE_EXISTS=$(echo "$OUTPUT" | jq -r '.profile.exists') |
| 86 | +COMMANDS_DETECTED=$(echo "$OUTPUT" | jq -r '.checks.commands_detected') |
| 87 | +if [ "$STATUS" = "warn" ] && [ "$PROFILE_EXISTS" = "false" ] && [ "$COMMANDS_DETECTED" -ge 10 ]; then |
| 88 | + pass "doctor reports missing profile but valid plugin setup" |
| 89 | +else |
| 90 | + fail "doctor reports missing profile but valid plugin setup" "status=$STATUS profile_exists=$PROFILE_EXISTS commands=$COMMANDS_DETECTED" |
| 91 | +fi |
| 92 | + |
| 93 | +echo "" |
| 94 | + |
| 95 | +# ============================================================ |
| 96 | +# TEST GROUP 2: import-profile.sh |
| 97 | +# ============================================================ |
| 98 | +echo "▸ import-profile.sh" |
| 99 | + |
| 100 | +setup_profile |
| 101 | +EXPORT_FILE="$TEST_HOME/code-sensei-export.json" |
| 102 | +create_wrapped_export "$EXPORT_FILE" |
| 103 | + |
| 104 | +PREVIEW=$(bash "$SCRIPT_DIR/scripts/import-profile.sh" "$EXPORT_FILE") |
| 105 | +PREVIEW_STATUS=$(echo "$PREVIEW" | jq -r '.status') |
| 106 | +TARGET_BELT=$(echo "$PREVIEW" | jq -r '.target_summary.belt') |
| 107 | +CURRENT_BELT=$(echo "$PREVIEW" | jq -r '.current_summary.belt') |
| 108 | +if [ "$PREVIEW_STATUS" = "preview" ] && [ "$TARGET_BELT" = "green" ] && [ "$CURRENT_BELT" = "yellow" ]; then |
| 109 | + pass "import preview shows target and current profile summaries" |
| 110 | +else |
| 111 | + fail "import preview shows target and current profile summaries" "status=$PREVIEW_STATUS target=$TARGET_BELT current=$CURRENT_BELT" |
| 112 | +fi |
| 113 | + |
| 114 | +APPLY=$(bash "$SCRIPT_DIR/scripts/import-profile.sh" --apply "$EXPORT_FILE") |
| 115 | +APPLY_STATUS=$(echo "$APPLY" | jq -r '.status') |
| 116 | +IMPORTED_BELT=$(jq -r '.belt' "$TEST_HOME/.code-sensei/profile.json") |
| 117 | +if [ "$APPLY_STATUS" = "imported" ] && [ "$IMPORTED_BELT" = "green" ] && [ -f "$TEST_HOME/.code-sensei/profile.json.backup" ]; then |
| 118 | + pass "import apply writes profile and creates backup" |
| 119 | +else |
| 120 | + fail "import apply writes profile and creates backup" "status=$APPLY_STATUS belt=$IMPORTED_BELT backup=$( [ -f "$TEST_HOME/.code-sensei/profile.json.backup" ] && echo yes || echo no )" |
| 121 | +fi |
| 122 | + |
| 123 | +echo "" |
| 124 | + |
| 125 | +# ============================================================ |
| 126 | +# TEST GROUP 3: track-command.sh redaction |
| 127 | +# ============================================================ |
| 128 | +echo "▸ track-command.sh redaction" |
| 129 | + |
| 130 | +setup_profile |
| 131 | +rm -f "$TEST_HOME/.code-sensei/session-commands.jsonl" |
| 132 | +cat <<'JSON' | bash "$SCRIPT_DIR/scripts/track-command.sh" >/dev/null 2>&1 |
| 133 | +{"tool_input":{"command":"export OPENAI_API_KEY=sk-test-123 && curl -H \"Authorization: Bearer real-token\" https://user:secret@example.com --token cli-secret"}} |
| 134 | +JSON |
| 135 | + |
| 136 | +LOG_CONTENT=$(cat "$TEST_HOME/.code-sensei/session-commands.jsonl") |
| 137 | +if echo "$LOG_CONTENT" | grep -q '\[REDACTED\]' && ! echo "$LOG_CONTENT" | grep -q 'sk-test-123' && ! echo "$LOG_CONTENT" | grep -q 'real-token' && ! echo "$LOG_CONTENT" | grep -q 'cli-secret' && ! echo "$LOG_CONTENT" | grep -q 'secret@example.com'; then |
| 138 | + pass "command log redacts sensitive values" |
| 139 | +else |
| 140 | + fail "command log redacts sensitive values" "$LOG_CONTENT" |
| 141 | +fi |
| 142 | + |
| 143 | +echo "" |
| 144 | +echo "━━━ Summary ━━━" |
| 145 | +echo -e "Passed: ${GREEN}${PASS}${NC}" |
| 146 | +echo -e "Failed: ${RED}${FAIL}${NC}" |
| 147 | +echo "" |
| 148 | + |
| 149 | +if [ "$FAIL" -ne 0 ]; then |
| 150 | + exit 1 |
| 151 | +fi |
0 commit comments