test: add automated tests and ci workflow

This commit is contained in:
csh
2026-01-08 11:29:44 +08:00
parent 99bef309b7
commit da0ef2b7a2
12 changed files with 3149 additions and 6 deletions
@@ -0,0 +1,116 @@
#!/usr/bin/env bats
# install_codex_skills.sh 测试套件
setup() {
export TEST_DIR="$(mktemp -d)"
export PLAYBOOK_ROOT="$(cd "$BATS_TEST_DIRNAME/../.." && pwd)"
export SCRIPT_PATH="$PLAYBOOK_ROOT/scripts/install_codex_skills.sh"
# 创建临时 HOME 避免污染真实环境
export ORIGINAL_HOME="$HOME"
export HOME="$TEST_DIR/home"
mkdir -p "$HOME"
export CODEX_HOME="$HOME/.codex"
export SKILLS_DST_ROOT="$CODEX_HOME/skills"
cd "$TEST_DIR"
}
teardown() {
export HOME="$ORIGINAL_HOME"
if [ -n "$TEST_DIR" ] && [ -d "$TEST_DIR" ]; then
chmod -R u+w "$TEST_DIR" 2>/dev/null || true
rm -rf "$TEST_DIR"
fi
}
skip_if_no_skills() {
if [ ! -d "$PLAYBOOK_ROOT/codex/skills" ]; then
skip "No codex/skills directory found"
fi
}
first_skill_name() {
ls -1 "$PLAYBOOK_ROOT/codex/skills" 2>/dev/null | head -n 1
}
# ==============================================
# 基础功能测试
# ==============================================
@test "install_codex_skills.sh 脚本存在且可执行" {
[ -f "$SCRIPT_PATH" ]
}
@test "安装 - 创建 skills 目录" {
skip_if_no_skills
[ ! -d "$SKILLS_DST_ROOT" ]
sh "$SCRIPT_PATH"
[ -d "$SKILLS_DST_ROOT" ]
}
@test "安装 - 复制 skill 目录" {
skip_if_no_skills
sh "$SCRIPT_PATH"
SKILL_DIRS=$(find "$SKILLS_DST_ROOT" -mindepth 1 -maxdepth 1 -type d)
[ -n "$SKILL_DIRS" ]
for dir in $SKILL_DIRS; do
[ -f "$dir/SKILL.md" ]
done
}
@test "安装 - 指定单个 skill" {
skip_if_no_skills
SKILL_NAME="$(first_skill_name)"
[ -n "$SKILL_NAME" ]
sh "$SCRIPT_PATH" "$SKILL_NAME"
[ -d "$SKILLS_DST_ROOT/$SKILL_NAME" ]
COUNT=$(find "$SKILLS_DST_ROOT" -mindepth 1 -maxdepth 1 -type d | wc -l)
[ "$COUNT" -eq 1 ]
}
@test "备份 - 同名 skill 目录会创建备份" {
skip_if_no_skills
SKILL_NAME="$(first_skill_name)"
[ -n "$SKILL_NAME" ]
mkdir -p "$SKILLS_DST_ROOT/$SKILL_NAME"
echo "marker" > "$SKILLS_DST_ROOT/$SKILL_NAME/marker.txt"
sh "$SCRIPT_PATH" "$SKILL_NAME"
BACKUP_DIRS=$(find "$SKILLS_DST_ROOT" -maxdepth 1 -type d -name "$SKILL_NAME.bak.*")
[ -n "$BACKUP_DIRS" ]
}
@test "错误处理 - 指定不存在 skill 报错" {
skip_if_no_skills
run sh "$SCRIPT_PATH" nonexistent-skill
[ "$status" -ne 0 ]
}
@test "幂等性 - 多次安装结果一致" {
skip_if_no_skills
sh "$SCRIPT_PATH"
CHECKSUM1=$(find "$SKILLS_DST_ROOT" -type f -name "SKILL.md" ! -path "$SKILLS_DST_ROOT"'/*.bak.*/*' -exec md5sum {} \; | sort | md5sum)
sh "$SCRIPT_PATH"
CHECKSUM2=$(find "$SKILLS_DST_ROOT" -type f -name "SKILL.md" ! -path "$SKILLS_DST_ROOT"'/*.bak.*/*' -exec md5sum {} \; | sort | md5sum)
[ "$CHECKSUM1" = "$CHECKSUM2" ]
}
+251
View File
@@ -0,0 +1,251 @@
#!/usr/bin/env bats
# sync_standards.sh 测试套件
# 测试辅助函数
setup() {
# 创建临时测试目录
export TEST_DIR="$(mktemp -d)"
export PLAYBOOK_ROOT="$(cd "$BATS_TEST_DIRNAME/../.." && pwd)"
# 初始化测试项目
cd "$TEST_DIR"
git init
git config user.name "Test User"
git config user.email "test@example.com"
# 模拟 playbook 快照目录
mkdir -p docs/standards/playbook
cp -r "$PLAYBOOK_ROOT"/{.agents,.gitattributes,docs,scripts} docs/standards/playbook/ 2>/dev/null || true
export SCRIPT_PATH="$TEST_DIR/docs/standards/playbook/scripts/sync_standards.sh"
}
teardown() {
# 清理测试目录
if [ -n "$TEST_DIR" ] && [ -d "$TEST_DIR" ]; then
rm -rf "$TEST_DIR"
fi
}
# ==============================================
# 基础功能测试
# ==============================================
@test "sync_standards.sh 脚本存在且可执行" {
[ -f "$SCRIPT_PATH" ]
}
@test "sync_standards.sh 无参数时同步 tsl 规则集" {
cd "$TEST_DIR"
sh "$SCRIPT_PATH"
# 验证输出目录
[ -d ".agents/tsl" ]
[ -f ".agents/tsl/index.md" ]
}
@test "sync_standards.sh tsl - 同步 TSL 规则集" {
cd "$TEST_DIR"
sh "$SCRIPT_PATH" tsl
# 验证必须文件
[ -d ".agents/tsl" ]
[ -f ".agents/tsl/index.md" ]
[ -f ".agents/tsl/auth.md" ]
[ -f ".agents/tsl/code_quality.md" ]
[ -f ".agents/tsl/performance.md" ]
[ -f ".agents/tsl/testing.md" ]
}
@test "sync_standards.sh cpp - 同步 C++ 规则集" {
cd "$TEST_DIR"
sh "$SCRIPT_PATH" cpp
# 验证必须文件
[ -d ".agents/cpp" ]
[ -f ".agents/cpp/index.md" ]
[ -f ".agents/cpp/auth.md" ]
[ -f ".agents/cpp/code_quality.md" ]
}
@test "sync_standards.sh tsl cpp - 同步多个规则集" {
cd "$TEST_DIR"
sh "$SCRIPT_PATH" tsl cpp
# 验证两个规则集都存在
[ -d ".agents/tsl" ]
[ -d ".agents/cpp" ]
[ -f ".agents/index.md" ]
}
# ==============================================
# .gitattributes 同步测试
# ==============================================
@test ".gitattributes - 默认模式创建 managed block" {
cd "$TEST_DIR"
[ ! -f ".gitattributes" ]
sh "$SCRIPT_PATH" tsl
[ -f ".gitattributes" ]
grep -q "# BEGIN playbook .gitattributes" .gitattributes
grep -q "# END playbook .gitattributes" .gitattributes
}
@test ".gitattributes - 保留现有内容" {
cd "$TEST_DIR"
echo "# My custom rules" > .gitattributes
echo "*.custom binary" >> .gitattributes
sh "$SCRIPT_PATH" tsl
grep -q "# My custom rules" .gitattributes
grep -q "*.custom binary" .gitattributes
}
@test ".gitattributes - 更新已存在的 managed block" {
cd "$TEST_DIR"
cat > .gitattributes << 'EOF'
# BEGIN playbook .gitattributes
# Old content
# END playbook .gitattributes
EOF
sh "$SCRIPT_PATH" tsl
# 验证 block 已更新(不再包含 "Old content"
! grep -q "Old content" .gitattributes
}
# ==============================================
# AGENTS.md 自动生成测试
# ==============================================
@test "AGENTS.md - 不存在时自动创建" {
cd "$TEST_DIR"
[ ! -f "AGENTS.md" ]
sh "$SCRIPT_PATH" tsl
[ -f "AGENTS.md" ]
grep -q ".agents/" AGENTS.md
}
@test "AGENTS.md - 已存在时不覆盖" {
cd "$TEST_DIR"
echo "# My custom AGENTS.md" > AGENTS.md
sh "$SCRIPT_PATH" tsl
grep -q "# My custom AGENTS.md" AGENTS.md
}
# ==============================================
# 备份功能测试
# ==============================================
@test "备份 - .gitattributes 更新前创建备份" {
cd "$TEST_DIR"
echo "# Original content" > .gitattributes
sh "$SCRIPT_PATH" tsl
# 验证备份文件存在
[ -f ".gitattributes.bak."* ] || [ -f ".gitattributes.bak" ]
}
@test "备份 - .agents/ 更新前创建备份" {
cd "$TEST_DIR"
mkdir -p .agents/tsl
echo "# Old index" > .agents/tsl/index.md
sh "$SCRIPT_PATH" tsl
# 验证备份目录存在
[ -d ".agents/tsl.bak."* ] || [ -d ".agents/tsl.bak" ]
}
# ==============================================
# 多语言项目测试
# ==============================================
@test "多语言 - TSL + C++ + Python 规则集共存" {
cd "$TEST_DIR"
# 复制 Python 规则集(如果存在)
if [ -d "$PLAYBOOK_ROOT/.agents/python" ]; then
cp -r "$PLAYBOOK_ROOT/.agents/python" docs/standards/playbook/.agents/
fi
sh "$SCRIPT_PATH" tsl cpp
# 验证规则集不互相覆盖
[ -d ".agents/tsl" ]
[ -d ".agents/cpp" ]
# 验证索引文件正确引用
[ -f ".agents/index.md" ]
}
# ==============================================
# 错误处理测试
# ==============================================
@test "错误处理 - 未找到 playbook 快照时报错" {
cd "$TEST_DIR"
rm -rf docs/standards/playbook/.agents
run sh "$SCRIPT_PATH" tsl
[ "$status" -ne 0 ]
}
@test "错误处理 - 无效语言参数时报错" {
cd "$TEST_DIR"
run sh "$SCRIPT_PATH" invalid_lang
[ "$status" -ne 0 ]
}
# ==============================================
# 环境变量配置测试
# ==============================================
@test "环境变量 - SYNC_GITATTR_MODE=skip 跳过 .gitattributes" {
cd "$TEST_DIR"
export SYNC_GITATTR_MODE=skip
sh "$SCRIPT_PATH" tsl
[ ! -f ".gitattributes" ]
}
@test "环境变量 - SYNC_GITATTR_MODE=overwrite 覆盖 .gitattributes" {
cd "$TEST_DIR"
echo "# Custom content" > .gitattributes
export SYNC_GITATTR_MODE=overwrite
sh "$SCRIPT_PATH" tsl
# 验证自定义内容被覆盖
! grep -q "# Custom content" .gitattributes
}
# ==============================================
# 幂等性测试
# ==============================================
@test "幂等性 - 多次执行结果一致" {
cd "$TEST_DIR"
# 第一次同步
sh "$SCRIPT_PATH" tsl
CHECKSUM1=$(find .agents/tsl -type f -exec md5sum {} \; | sort | md5sum)
# 第二次同步
sh "$SCRIPT_PATH" tsl
CHECKSUM2=$(find .agents/tsl -type f -exec md5sum {} \; | sort | md5sum)
[ "$CHECKSUM1" = "$CHECKSUM2" ]
}
+284
View File
@@ -0,0 +1,284 @@
#!/usr/bin/env bats
# vendor_playbook.sh 测试套件
# 测试辅助函数
setup() {
# 创建临时测试目录
export TEST_DIR="$(mktemp -d)"
export PLAYBOOK_ROOT="$(cd "$BATS_TEST_DIRNAME/../.." && pwd)"
export SCRIPT_PATH="$PLAYBOOK_ROOT/scripts/vendor_playbook.sh"
# 创建目标项目目录
export TARGET_DIR="$(mktemp -d)"
cd "$TARGET_DIR"
git init
git config user.name "Test User"
git config user.email "test@example.com"
}
teardown() {
# 清理测试目录
if [ -n "$TEST_DIR" ] && [ -d "$TEST_DIR" ]; then
chmod -R u+w "$TEST_DIR" 2>/dev/null || true
rm -rf "$TEST_DIR"
fi
if [ -n "$TARGET_DIR" ] && [ -d "$TARGET_DIR" ]; then
chmod -R u+w "$TARGET_DIR" 2>/dev/null || true
rm -rf "$TARGET_DIR"
fi
}
# ==============================================
# 基础功能测试
# ==============================================
@test "vendor_playbook.sh 脚本存在且可执行" {
[ -f "$SCRIPT_PATH" ]
}
@test "vendor_playbook.sh - 无参数时显示用法" {
cd "$TARGET_DIR"
run sh "$SCRIPT_PATH"
[ "$status" -ne 0 ]
}
@test "vendor_playbook.sh - 单语言 vendoring (tsl)" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
# 验证快照目录结构
[ -d "docs/standards/playbook" ]
[ -d "docs/standards/playbook/docs/common" ]
[ -d "docs/standards/playbook/docs/tsl" ]
[ -d "docs/standards/playbook/.agents/tsl" ]
[ -f "docs/standards/playbook/scripts/sync_standards.sh" ]
}
@test "vendor_playbook.sh - 多语言 vendoring (tsl cpp)" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl cpp
# 验证包含两种语言的文档
[ -d "docs/standards/playbook/docs/tsl" ]
[ -d "docs/standards/playbook/docs/cpp" ]
[ -d "docs/standards/playbook/.agents/tsl" ]
[ -d "docs/standards/playbook/.agents/cpp" ]
}
# ==============================================
# 自动同步测试
# ==============================================
@test "vendor_playbook.sh - 自动执行 sync_standards" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
# 验证根目录已同步规则集
[ -d ".agents/tsl" ]
[ -f ".agents/tsl/index.md" ]
[ -f ".gitattributes" ]
}
@test "vendor_playbook.sh - 多语言自动同步" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl cpp
# 验证两个规则集都已同步
[ -d ".agents/tsl" ]
[ -d ".agents/cpp" ]
[ -f ".agents/index.md" ]
}
# ==============================================
# SOURCE.md 生成测试
# ==============================================
@test "vendor_playbook.sh - 生成 SOURCE.md" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
[ -f "docs/standards/playbook/SOURCE.md" ]
grep -q "Source:" docs/standards/playbook/SOURCE.md
grep -q "Commit:" docs/standards/playbook/SOURCE.md
}
@test "vendor_playbook.sh - SOURCE.md 包含 commit hash" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
# 验证包含 commit hash40个十六进制字符)
grep -E "[0-9a-f]{40}" docs/standards/playbook/SOURCE.md
}
@test "vendor_playbook.sh - SOURCE.md 包含时间戳" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
# 验证包含日期格式 YYYY-MM-DD
grep -E "[0-9]{4}-[0-9]{2}-[0-9]{2}" docs/standards/playbook/SOURCE.md
}
# ==============================================
# 裁剪功能测试
# ==============================================
@test "裁剪 - 仅包含指定语言的文档" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
# 验证包含 TSL 文档
[ -d "docs/standards/playbook/docs/tsl" ]
# 验证不包含其他语言(如果原本有 Python)
if [ -d "$PLAYBOOK_ROOT/docs/python" ]; then
[ ! -d "docs/standards/playbook/docs/python" ]
fi
}
@test "裁剪 - 始终包含 common 目录" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
[ -d "docs/standards/playbook/docs/common" ]
[ -f "docs/standards/playbook/docs/common/commit_message.md" ]
}
@test "裁剪 - 包含对应的模板文件" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" cpp
# 验证包含 C++ 模板
[ -d "docs/standards/playbook/templates/cpp" ]
# 验证不包含 Python 模板(如果指定了 cpp
if [ -d "$PLAYBOOK_ROOT/templates/python" ]; then
[ ! -d "docs/standards/playbook/templates/python" ]
fi
}
@test "裁剪 - 包含通用 CI 模板" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
[ -d "docs/standards/playbook/templates/ci" ]
}
# ==============================================
# 目标目录处理测试
# ==============================================
@test "目标目录 - 已存在时覆盖更新" {
cd "$TARGET_DIR"
# 首次 vendor
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
# 在快照中添加标记文件
touch docs/standards/playbook/OLD_MARKER
# 再次 vendor
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
# 验证旧标记不存在(已被覆盖)
[ ! -f "docs/standards/playbook/OLD_MARKER" ]
}
@test "目标目录 - 创建必要的父目录" {
cd "$TARGET_DIR"
# 确保 docs/standards 不存在
[ ! -d "docs/standards" ]
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
[ -d "docs/standards/playbook" ]
}
# ==============================================
# 错误处理测试
# ==============================================
@test "错误处理 - 目标目录不存在时报错" {
cd "$TARGET_DIR"
run sh "$SCRIPT_PATH" /nonexistent/path tsl
[ "$status" -ne 0 ]
}
@test "错误处理 - 无效语言参数时报错" {
cd "$TARGET_DIR"
run sh "$SCRIPT_PATH" "$TARGET_DIR" invalid_lang
[ "$status" -ne 0 ]
}
@test "错误处理 - 目标目录不是 git 仓库时警告" {
cd "$TARGET_DIR"
rm -rf .git
run sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
# 应该给出警告但不失败
[ "$status" -eq 0 ]
}
# ==============================================
# 完整性测试
# ==============================================
@test "完整性 - 验证所有必要文件已复制" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
# 验证关键文件
[ -f "docs/standards/playbook/README.md" ]
[ -f "docs/standards/playbook/docs/index.md" ]
[ -f "docs/standards/playbook/.gitattributes" ]
[ -f "docs/standards/playbook/scripts/sync_standards.sh" ]
[ -f "docs/standards/playbook/SOURCE.md" ]
}
@test "完整性 - 验证脚本可执行性" {
cd "$TARGET_DIR"
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
# 验证同步脚本可执行
run sh docs/standards/playbook/scripts/sync_standards.sh tsl
[ "$status" -eq 0 ]
}
# ==============================================
# 多次执行测试
# ==============================================
@test "幂等性 - 多次 vendor 结果一致" {
cd "$TARGET_DIR"
# 第一次 vendor
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
CHECKSUM1=$(find docs/standards/playbook -type f -name "*.md" ! -name "SOURCE.md" -exec md5sum {} \; | sort | md5sum)
# 第二次 vendor
sh "$SCRIPT_PATH" "$TARGET_DIR" tsl
CHECKSUM2=$(find docs/standards/playbook -type f -name "*.md" ! -name "SOURCE.md" -exec md5sum {} \; | sort | md5sum)
[ "$CHECKSUM1" = "$CHECKSUM2" ]
}