Files
team/scripts/test-tmux-send-prompt.sh
2026-04-19 21:47:08 +08:00

391 lines
12 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# test-tmux-send-prompt.sh — tmux-send-prompt.sh 的测试套件
#
# 用法: bash scripts/test-tmux-send-prompt.sh
#
# 通过 mock tmux 命令隔离测试,覆盖:
# 1. 参数校验(无参数、缺参数)
# 2. 文本字符串直接发送
# 3. 文件路径 → 生成「请读取」指令
# 4. 相对路径 → 解析为绝对路径
# 5. 不存在的路径 → 视为文本
# 6. 自定义 submit-delay
# 7. 特殊字符(空格、中文、符号)
# 8. 目录路径 → 视为文本
# 9. 空/有内容的文件 → 均生成指令
# 10. tmux 不可用场景
set -euo pipefail
SCRIPT="/workspace/scripts/tmux-send-prompt.sh"
TEST_DIR=$(mktemp -d)
MOCK_DIR="${TEST_DIR}/mock-bin"
LOG_FILE="${TEST_DIR}/tmux-calls.log"
RESULTS=()
# ─── 颜色输出 ───
GREEN='\033[32m'
RED='\033[31m'
YELLOW='\033[33m'
RESET='\033[0m'
pass=0
fail=0
skip=0
# ─── 辅助函数 ───
result() {
local label="$1" ok="$2" detail="${3:-}"
if [[ "$ok" == "PASS" ]]; then
((pass++)) || true
echo -e " ${GREEN}✓ PASS${RESET} ${label}${detail:+ — ${detail}}"
elif [[ "$ok" == "SKIP" ]]; then
((skip++)) || true
echo -e " ${YELLOW}⊘ SKIP${RESET} ${label}${detail:+ — ${detail}}"
else
((fail++)) || true
echo -e " ${RED}✗ FAIL${RESET} ${label}${detail:+ — ${detail}}"
fi
}
cleanup() {
rm -rf "$TEST_DIR"
}
trap cleanup EXIT
# ─── 创建 mock tmux ───
# mock tmux 记录所有调用到日志文件
create_mock_tmux() {
mkdir -p "$MOCK_DIR"
cat > "${MOCK_DIR}/tmux" <<'MOCK'
#!/bin/bash
echo "$@" >> "$(dirname "$0")/../tmux-calls.log"
# capture-pane 返回含 prompt 标记的内容(模拟 cc 就绪)
if [[ "$1" == "capture-pane" ]]; then
echo "Some output"
echo "Some output"
echo " "
fi
# send-keys 不做任何事,直接成功
MOCK
chmod +x "${MOCK_DIR}/tmux"
}
# 清空调用日志
clear_log() {
: > "$LOG_FILE"
}
# 读取调用日志
read_log() {
cat "$LOG_FILE" 2>/dev/null || true
}
# 在 mock 环境中运行脚本
run_script() {
PATH="${MOCK_DIR}:${PATH}" "$SCRIPT" "$@" 2>&1 || true
}
# 获取脚本退出码
run_script_code() {
PATH="${MOCK_DIR}:${PATH}" "$SCRIPT" "$@" >/dev/null 2>&1 && echo "0" || echo "$?"
}
# ─── 创建测试文件 ───
create_test_file() {
local name="$1"
local content="${2:-测试内容}"
local filepath="${TEST_DIR}/${name}"
echo "$content" > "$filepath"
echo "$filepath"
}
# ═══════════════════════════════════════════
echo ""
echo "═══ tmux-send-prompt.sh 测试套件 ═══"
echo ""
# ─── 1. 参数校验 ───
echo "── 1. 参数校验 ──"
# 1a. 无参数
clear_log
code=$(run_script_code)
if [[ "$code" != "0" ]]; then
result "无参数 → 非零退出码" "PASS" "exit=$code"
else
result "无参数 → 非零退出码" "FAIL" "期望非零,实际 exit=0"
fi
# 1b. 只有 pane_id
clear_log
output=$(run_script "%1" 2>&1 || true)
if echo "$output" | grep -qi "需要提供"; then
result "只有 pane_id → 提示缺少文本" "PASS"
else
result "只有 pane_id → 提示缺少文本" "FAIL" "输出: $(echo "$output" | head -3)"
fi
# ─── 2. 文本字符串 ───
echo ""
echo "── 2. 文本字符串直接发送 ──"
create_mock_tmux
# 2a. 短文本
clear_log
output=$(run_script "%1" "你好世界")
log=$(read_log)
if echo "$log" | grep -qF "你好世界"; then
result "短文本 → send-keys 原文发送" "PASS"
else
result "短文本 → send-keys 原文发送" "FAIL" "日志: $(echo "$log" | head -5)"
fi
# 2b. 非文件路径字符串(不存在)
clear_log
output=$(run_script "%1" "这个路径不存在/abc.txt" 2>&1 || true)
log=$(read_log)
if echo "$log" | grep -qF "这个路径不存在/abc.txt"; then
result "不存在的路径 → 视为文本直接发送" "PASS"
else
result "不存在的路径 → 视为文本直接发送" "FAIL"
fi
# 2c. 普通字符串不应包含"请读取"
if ! echo "$log" | grep -q "请读取"; then
result "普通文本不含「请读取」" "PASS"
else
result "普通文本不含「请读取」" "FAIL" "不应出现「请读取」"
fi
# ─── 3. 文件路径 → 生成指令 ───
echo ""
echo "── 3. 文件路径 → 生成「请读取」指令 ──"
# 3a. 绝对路径文件
test_file=$(create_test_file "prompt.md" "检查密码策略配置")
clear_log
output=$(run_script "%1" "$test_file" 2>&1 || true)
log=$(read_log)
expected="请读取 ${test_file} 并执行其中的内容"
if echo "$log" | grep -qF "$expected"; then
result "文件(绝对路径)→ 生成指令" "PASS"
else
result "文件(绝对路径)→ 生成指令" "FAIL" "期望含: $expected"
fi
# 3b. 输出包含检测日志
if echo "$output" | grep -q "检测到文件"; then
result "输出包含文件检测日志" "PASS"
else
result "输出包含文件检测日志" "FAIL"
fi
# 3c. 文件内容不应被直接发送
file_content="检查密码策略配置"
if ! echo "$log" | grep -qF "$file_content"; then
result "文件内容未被直接发送" "PASS"
else
result "文件内容未被直接发送" "FAIL" "文件内容不应出现在 tmux 日志中"
fi
# ─── 4. 相对路径 → 解析为绝对路径 ───
echo ""
echo "── 4. 相对路径解析 ──"
# 4a. 相对路径文件
rel_file=$(create_test_file "relative-test.md" "相对路径测试")
# 使用相对路径
clear_log
cd "$TEST_DIR"
output=$(PATH="${MOCK_DIR}:${PATH}" "$SCRIPT" "%1" "relative-test.md" 2>&1 || true)
log=$(read_log)
if echo "$log" | grep -qF "请读取 ${rel_file}"; then
result "相对路径 → 解析为绝对路径" "PASS"
else
result "相对路径 → 解析为绝对路径" "FAIL" "日志: $(echo "$log" | head -3)"
fi
cd /workspace
# ─── 5. 特殊字符 ───
echo ""
echo "── 5. 特殊字符处理 ──"
# 5a. 含空格的文件名
space_file=$(create_test_file "space test.md" "空格文件内容")
clear_log
output=$(run_script "%1" "$space_file" 2>&1 || true)
log=$(read_log)
if echo "$log" | grep -qF "请读取"; then
result "含空格的文件名 → 正常处理" "PASS"
else
result "含空格的文件名 → 正常处理" "FAIL"
fi
# 5b. 含中文的文本
clear_log
output=$(run_script "%1" "你好,请执行安全审计任务!" 2>&1 || true)
log=$(read_log)
if echo "$log" | grep -qF "你好,请执行安全审计任务!"; then
result "中文文本 → 原样发送" "PASS"
else
result "中文文本 → 原样发送" "FAIL"
fi
# 5c. 含特殊符号的文本
clear_log
output=$(run_script "%1" 'echo "hello" | grep foo' 2>&1 || true)
log=$(read_log)
if echo "$log" | grep -qF 'echo "hello" | grep foo'; then
result "特殊符号文本 → 原样发送" "PASS"
else
result "特殊符号文本 → 原样发送" "FAIL"
fi
# ─── 6. 自定义 submit-delay ───
echo ""
echo "── 6. submit-delay 参数 ──"
# 6a. 默认 delay=1(通过日志验证 send-keys 调用次数)
clear_log
output=$(run_script "%1" "测试默认delay" 2>&1 || true)
log=$(read_log)
send_count=$(echo "$log" | grep -c "send-keys" || true)
if [[ "$send_count" -ge 2 ]]; then
result "默认 delay → send-keys 至少调用 2 次(文本+Enter" "PASS" "实际 ${send_count} 次"
else
result "默认 delay → send-keys 至少调用 2 次" "FAIL" "实际 ${send_count} 次"
fi
# 6b. 自定义 delay
clear_log
output=$(run_script "%1" "自定义delay测试" 2 2>&1 || true)
log=$(read_log)
if echo "$log" | grep -q "send-keys"; then
result "自定义 delay=2 → 正常执行" "PASS"
else
result "自定义 delay=2 → 正常执行" "FAIL"
fi
# ─── 7. 目录路径 ───
echo ""
echo "── 7. 边界情况 ──"
# 7a. 传入目录(不是文件 -f 失败)
clear_log
output=$(run_script "%1" "/tmp" 2>&1 || true)
log=$(read_log)
if echo "$log" | grep -qF "/tmp"; then
result "目录路径 → 视为文本发送" "PASS"
else
result "目录路径 → 视为文本发送" "FAIL"
fi
if ! echo "$log" | grep -q "请读取"; then
result "目录路径 → 不含「请读取」" "PASS"
else
result "目录路径 → 不含「请读取」" "FAIL" "目录不是文件,不应生成指令"
fi
# 7b. 空文件(文件存在但内容为空)
empty_file=$(create_test_file "empty.md" "")
clear_log
output=$(run_script "%1" "$empty_file" 2>&1 || true)
log=$(read_log)
if echo "$log" | grep -qF "请读取 ${empty_file}"; then
result "空文件 → 仍生成「请读取」指令" "PASS"
else
result "空文件 → 仍生成「请读取」指令" "FAIL"
fi
# 7c. 多行内容的文件
multi_file=$(create_test_file "multi.md" "第一行
第二行
第三行
第四行")
clear_log
output=$(run_script "%1" "$multi_file" 2>&1 || true)
log=$(read_log)
if ! echo "$log" | grep -qF "第一行"; then
result "多行文件 → 内容不直接发送" "PASS"
else
result "多行文件 → 内容不直接发送" "FAIL" "多行内容不应出现在日志中"
fi
if echo "$log" | grep -qF "请读取 ${multi_file}"; then
result "多行文件 → 生成简短指令" "PASS"
else
result "多行文件 → 生成简短指令" "FAIL"
fi
# ─── 8. send-keys 调用模式验证 ───
echo ""
echo "── 8. tmux 调用模式 ──"
# 8a. 验证 send-keys 被调用两次(文本 + Enter)
clear_log
output=$(run_script "%1" "模式验证" 2>&1 || true)
log=$(read_log)
enter_count=$(echo "$log" | grep -c "send-keys.*Enter" || true)
text_count=$(echo "$log" | grep -c "send-keys.*模式验证" || true)
if [[ "$text_count" -ge 1 && "$enter_count" -ge 1 ]]; then
result "send-keys 调用模式:文本 + Enter" "PASS" "文本${text_count}次 Enter${enter_count}次"
else
result "send-keys 调用模式:文本 + Enter" "FAIL" "文本${text_count}次 Enter${enter_count}次"
fi
# 8b. 验证目标 pane_id 正确传递
pane_in_log=$(echo "$log" | grep "send-keys" | grep -o "%[0-9]*" | head -1 || true)
if [[ "$pane_in_log" == "%1" ]]; then
result "pane_id %1 正确传递" "PASS"
else
result "pane_id %1 正确传递" "FAIL" "实际: $pane_in_log"
fi
# 8c. 不同 pane_id
clear_log
output=$(run_script "%3" "pane3测试" 2>&1 || true)
log=$(read_log)
pane_in_log=$(echo "$log" | grep "send-keys" | grep -o "%[0-9]*" | head -1 || true)
if [[ "$pane_in_log" == "%3" ]]; then
result "pane_id %3 正确传递" "PASS"
else
result "pane_id %3 正确传递" "FAIL" "实际: $pane_in_log"
fi
# 8d. capture-pane 被调用(等待 prompt + 验证提交)
clear_log
output=$(run_script "%1" "capture测试" 2>&1 || true)
log=$(read_log)
capture_count=$(echo "$log" | grep -c "capture-pane" || true)
if [[ "$capture_count" -ge 1 ]]; then
result "capture-pane 被调用" "PASS" "共 ${capture_count} 次"
else
result "capture-pane 被调用" "FAIL"
fi
# ─── 9. 无 tmux 环境 ───
echo ""
echo "── 9. 无 tmux 环境 ──"
# 不使用 mock,直接运行(系统可能无 tmux 或无 session
if ! command -v tmux &>/dev/null; then
result "系统无 tmux → 脚本报错" "SKIP" "系统未安装 tmux,跳过"
else
# 有 tmux 但无 session → 脚本应在某个步骤失败
output=$( "$SCRIPT" "%99" "无session测试" 2>&1 || true)
exit_code=$?
# 预期:tmux 操作会失败,但由于 set -e 脚本会退出
result "无 tmux session → 脚本异常退出" "PASS" "exit=$exit_code"
fi
# ═══════════════════════════════════════════
echo ""
echo "════════════════════════════════════"
echo -e " ${GREEN}通过: ${pass}${RESET} ${RED}失败: ${fail}${RESET} ${YELLOW}跳过: ${skip}${RESET} 总计: $((pass + fail + skip))"
echo "════════════════════════════════════"
echo ""
if [[ "$fail" -gt 0 ]]; then
exit 1
fi