Files
team/scripts/test-tmux-msg.sh
2026-04-19 22:23:34 +08:00

357 lines
11 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-msg.sh — tmux-msg.sh / tmux-msg-notify.sh 单元测试
#
# 用法: bash scripts/test-tmux-msg.sh
#
# 通过 mock tmux 命令隔离测试,覆盖消息协议的 12 个场景
set -euo pipefail
MSG_SCRIPT="/workspace/scripts/tmux-msg.sh"
NOTIFY_SCRIPT="/workspace/scripts/tmux-msg-notify.sh"
LOG_SCRIPT="/workspace/scripts/tmux-msg-log.sh"
SEND_SCRIPT="/workspace/scripts/tmux-send-prompt.sh"
TEST_DIR=$(mktemp -d)
MOCK_DIR="${TEST_DIR}/mock-bin"
MSG_DIR="${TEST_DIR}/.tmux-messages"
LOG_FILE="${TEST_DIR}/test-output.log"
# 替代 /workspace/.tmux-messages 为测试目录
export TMUX_MSG_DIR_OVERRIDE="$MSG_DIR"
# ─── 颜色输出 ───
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 ───
create_mock_tmux() {
mkdir -p "$MOCK_DIR"
cat > "${MOCK_DIR}/tmux" <<'MOCK'
#!/bin/bash
LOG="$(dirname "$0")/../tmux-calls.log"
echo "$@" >> "$LOG"
if [[ "$1" == "capture-pane" ]]; then
echo "Some output"
echo " "
fi
if [[ "$1" == "display-message" ]]; then
echo "✳ MockPane"
fi
if [[ "$1" == "send-keys" ]]; then
# 模拟发送成功
exit 0
fi
MOCK
chmod +x "${MOCK_DIR}/tmux"
}
clear_log() {
: > "${TEST_DIR}/tmux-calls.log"
}
read_log() {
cat "${TEST_DIR}/tmux-calls.log" 2>/dev/null || true
}
# 创建测试用的消息目录
setup_msg_dir() {
mkdir -p "${MSG_DIR}/prompts"
mkdir -p "${MSG_DIR}/$(date +%Y/%m/%d)"
: > "${MSG_DIR}/index.jsonl"
}
# ═══════════════════════════════════════════
echo ""
echo "═══ tmux-msg 协议单元测试 ═══"
echo ""
# ─── 1. send 文本字符串 ───
echo "── 1. send 文本字符串 ──"
create_mock_tmux
setup_msg_dir
clear_log
output=$(PATH="${MOCK_DIR}:${PATH}" TMUX_MSG_DIR="$MSG_DIR" TMUX_SEND_LOG="${TEST_DIR}/send.log" TMUX_MSG_NO_VERIFY=1 bash "$MSG_SCRIPT" send %2 "测试任务描述" --from %1 2>&1 || true)
msg_id=$(echo "$output" | grep -oE '^msg-[0-9]+-[0-9]+-[0-9a-f]+$' | head -1)
if [[ -n "$msg_id" ]]; then
result "send → 返回有效 msg_id" "PASS" "$msg_id"
else
result "send → 返回有效 msg_id" "FAIL" "实际: $msg_id"
fi
# 验证信封文件创建
envelope=$(find "$MSG_DIR" -name "${msg_id}.json" -type f 2>/dev/null | head -1)
if [[ -n "$envelope" ]]; then
result "send → 信封文件已创建" "PASS"
else
result "send → 信封文件已创建" "FAIL"
fi
# 验证索引更新
if [[ -f "${MSG_DIR}/index.jsonl" ]] && grep -q "$msg_id" "${MSG_DIR}/index.jsonl"; then
result "send → index.jsonl 已更新" "PASS"
else
result "send → index.jsonl 已更新" "FAIL"
fi
# ─── 2. send 包含回复指令 ───
echo ""
echo "── 2. send 包含回复指令 ──"
# 检查提示词文件包含 notify 指令
prompt_file="${MSG_DIR}/prompts/${msg_id}.md"
if [[ -f "$prompt_file" ]] && grep -q "tmux-msg-notify" "$prompt_file"; then
result "send → 提示词包含通知指令" "PASS"
else
result "send → 提示词包含通知指令" "FAIL"
fi
if [[ -f "$prompt_file" ]] && grep -q "跨面板任务" "$prompt_file"; then
result "send → 提示词包含任务头" "PASS"
else
result "send → 提示词包含任务头" "FAIL"
fi
if [[ -f "$prompt_file" ]] && grep -q "%1" "$prompt_file"; then
result "send → 提示词包含来源 pane" "PASS"
else
result "send → 提示词包含来源 pane" "FAIL"
fi
# ─── 3. 消息 ID 格式 ───
echo ""
echo "── 3. 消息 ID 格式 ──"
output2=$(PATH="${MOCK_DIR}:${PATH}" TMUX_MSG_DIR="$MSG_DIR" TMUX_SEND_LOG="${TEST_DIR}/send.log" TMUX_MSG_NO_VERIFY=1 bash "$MSG_SCRIPT" send %3 "第二个任务" 2>&1 || true)
msg_id2=$(echo "$output2" | grep -oE '^msg-[0-9]+-[0-9]+-[0-9a-f]+$' | head -1)
if [[ -n "$msg_id2" ]]; then
result "消息 ID 格式正确" "PASS" "$msg_id2"
else
result "消息 ID 格式正确" "FAIL" "实际: $msg_id2"
fi
if [[ "$msg_id" != "$msg_id2" ]]; then
result "消息 ID 唯一" "PASS"
else
result "消息 ID 唯一" "FAIL" "两次 send 返回相同 ID"
fi
# ─── 4. pane 标题解析 ───
echo ""
echo "── 4. pane 标题解析 ──"
if [[ -f "$envelope" ]]; then
from_title=$(python3 -c "import json; print(json.load(open('$envelope'))['from_title'])" 2>/dev/null)
if [[ "$from_title" == "✳ MockPane" ]]; then
result "pane 标题从 mock 正确解析" "PASS" "$from_title"
else
result "pane 标题从 mock 正确解析" "FAIL" "实际: $from_title"
fi
fi
# ─── 5. notify 成功 ───
echo ""
echo "── 5. notify 成功 ──"
setup_msg_dir
# 先手动创建一个消息信封用于 notify 测试
test_msg_id="msg-20260419-120000-test"
day_dir=$(date +%Y/%m/%d)
mkdir -p "${MSG_DIR}/${day_dir}"
cat > "${MSG_DIR}/${day_dir}/${test_msg_id}.json" <<ENVEOF
{"msg_id":"${test_msg_id}","direction":"request","from_pane":"%1","from_title":"PM","to_pane":"%2","to_title":"后端","task_summary":"测试任务","task_file":null,"status":"sent","created_at":"2026-04-19T12:00:00+08:00","delivered_at":"2026-04-19T12:00:01+08:00","replied_at":null,"reply_msg_id":null,"reply_summary":null,"error":null}
ENVEOF
echo "{\"msg_id\":\"${test_msg_id}\",\"direction\":\"request\",\"from\":\"%1\",\"to\":\"%2\",\"status\":\"sent\",\"summary\":\"测试任务\",\"ts\":\"2026-04-19T12:00:00+08:00\"}" >> "${MSG_DIR}/index.jsonl"
clear_log
output=$(PATH="${MOCK_DIR}:${PATH}" TMUX_MSG_DIR="$MSG_DIR" TMUX_SEND_LOG="${TEST_DIR}/send.log" TMUX_PANE="%2" bash "$NOTIFY_SCRIPT" "$test_msg_id" "%1" "已完成测试" 2>&1 || true)
# 验证原消息状态更新
updated_status=$(python3 -c "import json; print(json.load(open('${MSG_DIR}/${day_dir}/${test_msg_id}.json'))['status'])" 2>/dev/null)
if [[ "$updated_status" == "completed" ]]; then
result "notify → 原消息状态更新为 completed" "PASS"
else
result "notify → 原消息状态更新为 completed" "FAIL" "实际: $updated_status"
fi
# 验证回复信封创建
reply_envelope="${MSG_DIR}/${day_dir}/reply-${test_msg_id}.json"
if [[ -f "$reply_envelope" ]]; then
result "notify → 回复信封已创建" "PASS"
else
result "notify → 回复信封已创建" "FAIL"
fi
# 验证 notify 调用了 send-prompt 发送回复
log=$(read_log)
if echo "$log" | grep -q "send-keys"; then
result "notify → 调用了 tmux send-keys" "PASS"
else
result "notify → 调用了 tmux send-keys" "FAIL"
fi
# ─── 6. notify 未知 msg_id 容错 ───
echo ""
echo "── 6. notify 未知 msg_id ──"
clear_log
output=$(PATH="${MOCK_DIR}:${PATH}" TMUX_MSG_DIR="$MSG_DIR" TMUX_SEND_LOG="${TEST_DIR}/send.log" TMUX_PANE="%2" bash "$NOTIFY_SCRIPT" "msg-nonexistent" "%1" "测试" 2>&1 || true)
if echo "$output" | grep -q "未找到消息"; then
result "notify 未知 msg_id → 宽容处理" "PASS"
else
result "notify 未知 msg_id → 宽容处理" "FAIL" "输出: $output"
fi
# ─── 7. 目录结构 ───
echo ""
echo "── 7. 目录结构 ──"
today_dir=$(date +%Y/%m/%d)
if [[ -d "${MSG_DIR}/${today_dir}" ]]; then
result "日期分区目录已创建" "PASS" "${MSG_DIR}/${today_dir}"
else
result "日期分区目录已创建" "FAIL"
fi
if [[ -d "${MSG_DIR}/prompts" ]]; then
result "prompts 目录已创建" "PASS"
else
result "prompts 目录已创建" "FAIL"
fi
# ─── 8. index.jsonl 一致性 ───
echo ""
echo "── 8. index.jsonl 一致性 ──"
line_count=$(wc -l < "${MSG_DIR}/index.jsonl" | tr -d ' ')
if [[ "$line_count" -ge 1 ]]; then
result "index.jsonl 有内容" "PASS" "$line_count 行"
else
result "index.jsonl 有内容" "FAIL" "空文件"
fi
# 验证每行都是有效 JSON
invalid_lines=0
while IFS= read -r line; do
[[ -z "$line" ]] && continue
if ! echo "$line" | python3 -c "import sys,json; json.loads(sys.stdin.read())" 2>/dev/null; then
invalid_lines=$((invalid_lines + 1))
fi
done < "${MSG_DIR}/index.jsonl"
if [[ "$invalid_lines" -eq 0 ]]; then
result "index.jsonl 所有行都是有效 JSON" "PASS"
else
result "index.jsonl 所有行都是有效 JSON" "FAIL" "${invalid_lines} 行无效"
fi
# ─── 9. log 工具查询 ───
echo ""
echo "── 9. log 工具查询 ──"
output=$(TMUX_MSG_DIR="$MSG_DIR" bash "$LOG_SCRIPT" recent 2>&1)
if echo "$output" | grep -q "消息"; then
result "log recent → 返回消息列表" "PASS"
else
result "log recent → 返回消息列表" "FAIL"
fi
output=$(TMUX_MSG_DIR="$MSG_DIR" bash "$LOG_SCRIPT" show "$test_msg_id" 2>&1)
if echo "$output" | grep -q "$test_msg_id"; then
result "log show → 返回消息详情" "PASS"
else
result "log show → 返回消息详情" "FAIL"
fi
output=$(TMUX_MSG_DIR="$MSG_DIR" bash "$LOG_SCRIPT" stats 2>&1)
if echo "$output" | grep -q "总计"; then
result "log stats → 返回统计" "PASS"
else
result "log stats → 返回统计" "FAIL"
fi
# ─── 10. list 过滤 ───
echo ""
echo "── 10. list 过滤 ──"
output=$(TMUX_MSG_DIR="$MSG_DIR" bash "$LOG_SCRIPT" list --from %1 2>&1)
if echo "$output" | grep -q "%1"; then
result "list --from → 过滤生效" "PASS"
else
result "list --from → 过滤生效" "FAIL"
fi
output=$(TMUX_MSG_DIR="$MSG_DIR" bash "$LOG_SCRIPT" by-pane %2 2>&1)
if echo "$output" | grep -q "%2"; then
result "by-pane → 过滤生效" "PASS"
else
result "by-pane → 过滤生效" "FAIL"
fi
# ─── 11. send 参数校验 ───
echo ""
echo "── 11. send 参数校验 ──"
output=$(PATH="${MOCK_DIR}:${PATH}" TMUX_MSG_DIR="$MSG_DIR" TMUX_SEND_LOG="${TEST_DIR}/send.log" bash "$MSG_SCRIPT" send 2>&1 || true)
if echo "$output" | grep -q "用法\|需要"; then
result "send 无参数 → 提示用法" "PASS"
else
result "send 无参数 → 提示用法" "FAIL"
fi
# ─── 12. 信封字段完整性 ───
echo ""
echo "── 12. 信封字段完整性 ──"
required_fields="msg_id direction from_pane from_title to_pane to_title task_summary status created_at"
missing_fields=""
for field in $required_fields; do
if ! python3 -c "import json; d=json.load(open('$envelope')); assert '$field' in d" 2>/dev/null; then
missing_fields="${missing_fields} ${field}"
fi
done
if [[ -z "$missing_fields" ]]; then
result "信封字段完整" "PASS"
else
result "信封字段完整" "FAIL" "缺少:${missing_fields}"
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