#!/bin/bash # test-tmux-send-prompt-integ.sh — tmux-send-prompt.sh 真实集成测试(可观察模式) # # 用法: bash scripts/test-tmux-send-prompt-integ.sh # # 布局: # ┌──────────────────┬──────────────────┐ # │ cc pane (左) │ ctrl pane (右) │ # │ 接收命令并回复 │ 运行测试、显示 │ # └──────────────────┴──────────────────┘ set -euo pipefail # ─── 配置 ─── SESSION="test-send" SCRIPT="/workspace/scripts/tmux-send-prompt.sh" TEST_DIR=$(mktemp -d) # ─── 辅助函数 ─── wait_for_prompt() { local pane_id="$1" timeout="${2:-120}" desc="${3:-cc prompt}" echo -e " \e[36m⏳\e[0m 等待 ${desc}(最多 ${timeout}s)..." for i in $(seq 1 "$timeout"); do local last_lines last_lines=$(tmux capture-pane -t "$pane_id" -p 2>/dev/null | tail -5 || true) if echo "$last_lines" | grep -q '❯'; then echo -e " \e[36m✓\e[0m ${desc} 就绪(等待 ${i}s)" return 0 fi sleep 1 done echo -e " \e[31m✗\e[0m ${desc} 超时" return 1 } # ─── 清理旧 session ─── tmux kill-session -t "$SESSION" 2>/dev/null || true echo "" echo "═══ tmux-send-prompt.sh 集成测试(真实 cc)═══" echo "" # ─── 0. 环境检查 ─── echo "── 0. 环境准备 ──" command -v tmux &>/dev/null || { echo " ✗ 系统未安装 tmux"; exit 0; } command -v claude &>/dev/null || { echo " ✗ claude 未安装"; exit 0; } echo " ✓ tmux + claude 环境就绪" # ─── 1. 创建 tmux session ─── echo "" echo "── 1. 创建测试环境 ──" tmux new-session -d -s "$SESSION" -x 200 -y 50 CC_PANE=$(tmux display-message -t "$SESSION" -p '#{pane_id}') tmux split-window -t "$SESSION" -h CTRL_PANE=$(tmux display-message -t "$SESSION" -p '#{pane_id}') echo " ✓ session=$SESSION cc=$CC_PANE ctrl=$CTRL_PANE" echo " → 在 $CC_PANE 启动 claude..." tmux send-keys -t "$CC_PANE" "source ~/.bashrc && claude --dangerously-skip-permissions --allow-dangerously-skip-permissions" Enter if ! wait_for_prompt "$CC_PANE" 120 "cc 启动"; then echo " ✗ cc 启动失败" tmux capture-pane -t "$CC_PANE" -p | tail -10 exit 1 fi echo " ✓ cc 启动成功" # ─── 2. 生成测试脚本 ─── echo "" echo "── 2. 生成测试脚本 ──" RUNNER="${TEST_DIR}/runner.sh" # 步骤1: 写入 header(单引号 heredoc,不做任何替换) cat > "$RUNNER" <
> "$RUNNER" <<'COLORS' G=$'\e[32m' R=$'\e[31m' Y=$'\e[33m' B=$'\e[1m' N=$'\e[0m' pass=0 fail=0 result() { local label="$1" ok="$2" detail="${3:-}" if [[ "$ok" == "PASS" ]]; then ((pass++)) || true echo " ${G}✓ PASS${N} ${label}${detail:+ — ${detail}}" else ((fail++)) || true echo " ${R}✗ FAIL${N} ${label}${detail:+ — ${detail}}" fi } wait_for_prompt() { local pane_id="$1" timeout="${2:-60}" for i in $(seq 1 "$timeout"); do tmux capture-pane -t "$pane_id" -p 2>/dev/null | tail -5 | grep -q '❯' && return 0 sleep 1 done return 1 } wait_for_response() { local pane_id="$1" keyword="$2" timeout="${3:-90}" for i in $(seq 1 "$timeout"); do tmux capture-pane -t "$pane_id" -p -S -50 2>/dev/null | grep -qF "$keyword" && { sleep 2; return 0; } sleep 1 done return 1 } echo "" echo "${B}═══ 开始测试 ═══${N}" echo "" COLORS # 步骤3: 追加测试场景(单引号 heredoc,变量由运行时 bash 解释) cat >> "$RUNNER" <<'SCENARIOS' # ── 场景1: 短文本字符串 ── echo "${B}── 场景1: 短文本字符串 ──${N}" bash "$SCRIPT" "$CC_PANE" "请回复 TEST_OK_短文本" 2>&1 | while read -r line; do echo " $line"; done if wait_for_response "$CC_PANE" "TEST_OK_短文本"; then result "短文本 → cc 收到并回复" "PASS" else result "短文本 → cc 收到并回复" "FAIL" fi wait_for_prompt "$CC_PANE" 30 # ── 场景2: 文件路径 ── echo "" echo "${B}── 场景2: 文件路径 → cc 自行读取 ──${N}" cat > "${TEST_DIR}/test-prompt.md" <<'PROMPT' 请回复以下确认码:FILE_OK_读取成功 不要做其他任何事情,只回复确认码。 PROMPT echo " ✓ 测试文件已创建: ${TEST_DIR}/test-prompt.md" bash "$SCRIPT" "$CC_PANE" "${TEST_DIR}/test-prompt.md" 2>&1 | while read -r line; do echo " $line"; done sleep 3 if wait_for_response "$CC_PANE" "FILE_OK"; then result "文件路径 → cc 读取文件并回复" "PASS" else result "文件路径 → cc 读取文件并回复" "FAIL" fi wait_for_prompt "$CC_PANE" 30 # ── 场景3: 相对路径文件 ── echo "" echo "${B}── 场景3: 相对路径文件 ──${N}" mkdir -p "${TEST_DIR}/subdir" cat > "${TEST_DIR}/subdir/relative.md" <<'PROMPT' 请回复确认码:RELATIVE_OK PROMPT pushd "$TEST_DIR" > /dev/null bash "$SCRIPT" "$CC_PANE" "subdir/relative.md" 2>&1 | while read -r line; do echo " $line"; done popd > /dev/null sleep 3 if wait_for_response "$CC_PANE" "RELATIVE_OK"; then result "相对路径 → cc 读取成功" "PASS" else result "相对路径 → cc 读取成功" "FAIL" fi wait_for_prompt "$CC_PANE" 30 # ── 场景4: 长文本字符串 ── echo "" echo "${B}── 场景4: 长文本字符串(200+ 字)──${N}" LONG_TEXT="请回复确认码:LONG_OK。以下是一段长文本用于测试:密码管理系统需要满足以下安全要求:第一,所有敏感数据必须使用 AES-256-GCM 加密存储;第二,密码哈希使用 Argon2id 算法,参数为 64MB 内存和 3 次迭代;第三,设备认证使用 Ed25519 椭圆曲线签名;第四,传输层使用 TLS 1.3 双向认证;第五,主密码最少 8 位,必须包含大小写字母、数字和特殊符号。请只回复确认码 LONG_OK。" bash "$SCRIPT" "$CC_PANE" "$LONG_TEXT" 2>&1 | while read -r line; do echo " $line"; done if wait_for_response "$CC_PANE" "LONG_OK"; then result "长文本(200+字)→ cc 收到并回复" "PASS" else result "长文本(200+字)→ cc 收到并回复" "FAIL" fi wait_for_prompt "$CC_PANE" 30 # ── 场景5: 特殊字符 ── echo "" echo "${B}── 场景5: 特殊字符文本 ──${N}" SPECIAL_TEXT='请回复确认码:SPECIAL_OK,包含符号:|、&、$、"hello"、(test)、[array]、{json: true}' bash "$SCRIPT" "$CC_PANE" "$SPECIAL_TEXT" 2>&1 | while read -r line; do echo " $line"; done if wait_for_response "$CC_PANE" "SPECIAL_OK"; then result "特殊字符文本 → cc 收到并回复" "PASS" else result "特殊字符文本 → cc 收到并回复" "FAIL" fi wait_for_prompt "$CC_PANE" 30 # ── 场景6: 自定义 submit-delay ── echo "" echo "${B}── 场景6: 自定义 submit-delay=3 ──${N}" bash "$SCRIPT" "$CC_PANE" "请回复确认码:DELAY_OK" 3 2>&1 | while read -r line; do echo " $line"; done if wait_for_response "$CC_PANE" "DELAY_OK"; then result "自定义 delay=3 → cc 收到并回复" "PASS" else result "自定义 delay=3 → cc 收到并回复" "FAIL" fi wait_for_prompt "$CC_PANE" 30 # ── 场景7: 多行内容文件 ── echo "" echo "${B}── 场景7: 多行内容文件 ──${N}" cat > "${TEST_DIR}/multiline.md" <<'PROMPT' 请按以下步骤回复: 1. 回复确认码:MULTI_OK 2. 不要执行其他任何操作 3. 直接回复确认码即可 感谢配合。 PROMPT bash "$SCRIPT" "$CC_PANE" "${TEST_DIR}/multiline.md" 2>&1 | while read -r line; do echo " $line"; done sleep 3 if wait_for_response "$CC_PANE" "MULTI_OK"; then result "多行文件 → cc 读取并回复" "PASS" else result "多行文件 → cc 读取并回复" "FAIL" fi wait_for_prompt "$CC_PANE" 30 # ── 场景8: 不存在的路径 ── echo "" echo "${B}── 场景8: 不存在的路径 → 视为文本 ──${N}" bash "$SCRIPT" "$CC_PANE" "请回复确认码:NOTEXIST_OK" 2>&1 | while read -r line; do echo " $line"; done if wait_for_response "$CC_PANE" "NOTEXIST_OK"; then result "不存在路径字符串 → cc 收到并回复" "PASS" else result "不存在路径字符串 → cc 收到并回复" "FAIL" fi # ── 结果汇总 ── echo "" echo "════════════════════════════════════" echo " ${G}通过: ${pass}${N} ${R}失败: ${fail}${N} 总计: $((pass + fail))" echo "════════════════════════════════════" echo "" echo "提示: 按 Ctrl+B 再按 D 可脱离 session" echo " 执行 tmux kill-session -t $SESSION 可清理" SCENARIOS chmod +x "$RUNNER" echo " ✓ 测试脚本已生成: $RUNNER" # ─── 3. 切换到测试 session ─── echo "" echo "── 3. 启动测试 → 切换到 session ──" echo " 左 pane: cc(接收命令)" echo " 右 pane: 测试运行器(显示进度)" echo "" echo " ══════════════════════════════════" echo " 即将切换到 tmux session: $SESSION" echo " 按 Ctrl+B 再按 s 可切换回原 session" echo " ══════════════════════════════════" echo "" # 向 ctrl pane 发送执行命令 tmux send-keys -t "$CTRL_PANE" "sleep 1 && bash $RUNNER" Enter # 切换到测试 session if [[ -n "${TMUX:-}" ]]; then exec tmux switch-client -t "$SESSION" else exec tmux attach-session -t "$SESSION" fi