v0.0.3 published

This commit is contained in:
2025-06-02 11:27:10 +08:00
commit dca4f11e0d
61 changed files with 16453 additions and 0 deletions

556
test/v0.0.3/linux/start.sh Normal file
View File

@@ -0,0 +1,556 @@
#!/bin/bash
# Docker Hexo Static Blog v0.0.3 - Linux Complete Test Suite Startup Script
# 完整测试套件启动脚本
# set -e # Re-enabled for production use
# 颜色定义 (Corrected for echo -e and printf)
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 配置参数
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
DOCKERFILE_DIR="$(cd "$SCRIPT_DIR/../../../" && pwd)"
LOG_DIR="$SCRIPT_DIR/logs"
# SUITE_LOG is defined in main after mkdir -p LOG_DIR
# 测试脚本数组
TESTS=(
"build_test.sh:构建测试"
"run_test.sh:运行测试"
"functional_test.sh:功能测试"
"log_rotation_test.sh:日志轮转测试"
"cleanup_test.sh:清理测试"
)
# 函数:显示横幅
show_banner() {
echo -e "${CYAN}"
echo " ╔══════════════════════════════════════════════════════════════╗"
echo " ║ Docker Hexo Static Blog v0.0.3 ║"
echo " ║ 完整测试套件 (Linux) ║"
echo " ║ ║"
echo " ║ • 自动化构建和部署测试 ║"
echo " ║ • 功能完整性验证 ║"
echo " ║ • 日志轮转和权限测试 ║"
echo " ║ • 安全配置验证 ║"
echo " ║ • 性能监控 ║"
echo " ╚══════════════════════════════════════════════════════════════╝"
echo -e "${NC}"
}
# 函数:记录日志
log() {
echo -e "${BLUE}[$(LC_ALL=C date '+%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$SUITE_LOG"
local tee_status=${PIPESTATUS[1]}
if [ $tee_status -ne 0 ]; then
echo -e "${RED}CRITICAL: tee in log failed (status $tee_status) writing to $SUITE_LOG${NC}" >&2
fi
return 0
}
log_success() {
echo -e "${GREEN}[$(LC_ALL=C date '+%Y-%m-%d %H:%M:%S')] ✓${NC} $1" | tee -a "$SUITE_LOG"
local tee_status=${PIPESTATUS[1]}
if [ $tee_status -ne 0 ]; then
echo -e "${RED}CRITICAL: tee in log_success failed (status $tee_status) writing to $SUITE_LOG${NC}" >&2
fi
return 0
}
log_error() {
echo -e "${RED}[$(LC_ALL=C date '+%Y-%m-%d %H:%M:%S')] ✗${NC} $1" | tee -a "$SUITE_LOG"
local tee_status=${PIPESTATUS[1]}
if [ $tee_status -ne 0 ]; then
# Avoid using log_error here to prevent recursion if SUITE_LOG is the problem
echo -e "${RED}CRITICAL: tee in log_error failed (status $tee_status) writing to $SUITE_LOG${NC}" >&2
fi
return 0
}
log_warning() {
echo -e "${YELLOW}[$(LC_ALL=C date '+%Y-%m-%d %H:%M:%S')] ⚠${NC} $1" | tee -a "$SUITE_LOG"
local tee_status=${PIPESTATUS[1]}
if [ $tee_status -ne 0 ]; then
echo -e "${RED}CRITICAL: tee in log_warning failed (status $tee_status) writing to $SUITE_LOG${NC}" >&2
fi
return 0
}
log_step() {
echo -e "${CYAN}[$(LC_ALL=C date '+%Y-%m-%d %H:%M:%S')] ▶${NC} $1" | tee -a "$SUITE_LOG"
local tee_status=${PIPESTATUS[1]}
if [ $tee_status -ne 0 ]; then
echo -e "${RED}CRITICAL: tee in log_step failed (status $tee_status) writing to $SUITE_LOG${NC}" >&2
fi
return 0
}
# 函数:显示进度条
show_progress() {
local current=$1
local total=$2
local width=50
local percentage=$((current * 100 / total))
local filled=$((current * width / total))
local empty=$((width - filled))
printf "\r${CYAN}进度: [${NC}" # Corrected format string
printf "%*s" "$filled" | tr ' ' '=' # Corrected tr command and quoted variable
printf "%*s" "$empty" | tr ' ' ' ' # Corrected tr command and quoted variable
printf "${CYAN}] %d%% (%d/%d)${NC}" "$percentage" "$current" "$total" # Quoted variables
}
# 函数:检查前置条件
check_prerequisites() {
log_step "检查测试前置条件..."
local errors=0
# 检查 Docker
if ! command -v docker &> /dev/null; then
log_error "Docker 未安装或不在 PATH 中"
((errors++))
fi
# 检查 Docker 服务
if ! docker info &> /dev/null; then
log_error "Docker 服务未运行"
((errors++))
fi
# 检查 Dockerfile
if [ ! -f "$DOCKERFILE_DIR/Dockerfile_v0.0.3" ]; then
log_error "Dockerfile_v0.0.3 不存在于 $DOCKERFILE_DIR"
((errors++))
fi
# 检查测试脚本
for test_info in "${TESTS[@]}"; do
script_name="${test_info%%:*}"
if [ ! -f "$SCRIPT_DIR/$script_name" ]; then
log_error "测试脚本不存在: $script_name"
((errors++))
elif [ ! -x "$SCRIPT_DIR/$script_name" ]; then
log_warning "测试脚本没有执行权限: $script_name (将自动修复)"
chmod +x "$SCRIPT_DIR/$script_name"
fi
done
if [ $errors -gt 0 ]; then
log_error "发现 $errors 个前置条件问题,无法继续测试"
exit 1 # Exit if prerequisites fail
fi
log_success "前置条件检查通过"
}
# 函数:准备测试环境
prepare_test_environment() {
log_step "准备测试环境..."
# 创建日志目录 (already created in main, but ensure it)
mkdir -p "$LOG_DIR"
# 清理旧的测试资源(可选)
if [ "${CLEAN_START:-false}" = "true" ]; then
log "清理旧的测试资源..."
# Allow these commands to fail without exiting the script immediately
docker stop hexo-blog-test 2>/dev/null || true
docker rm hexo-blog-test 2>/dev/null || true
docker rmi hexo-test:v0.0.3 2>/dev/null || true # Ensure correct image name used elsewhere
fi
# 设置测试脚本权限
chmod +x "$SCRIPT_DIR"/*.sh
log_success "测试环境准备完成"
}
# 函数:运行单个测试
run_test() {
local test_info="$1"
local script_name="${test_info%%:*}"
local test_description="${test_info##*:}"
log_step "执行测试: $test_description ($script_name)"
local test_log="$LOG_DIR/${script_name%.sh}.log"
local start_time
start_time=$(LC_ALL=C date +%s)
# 执行测试脚本
# The actual script ($script_name) should handle its own set -e behavior.
# If it fails, its non-zero exit code will be caught here.
if LC_ALL=C bash "$SCRIPT_DIR/$script_name" > "$test_log" 2>&1; then
local end_time
end_time=$(LC_ALL=C date +%s)
local duration=$((end_time - start_time))
log_success "$test_description 测试通过 (耗时: ${duration}s)"
return 0 # Test script succeeded
else
local exit_code=$? # Capture exit code of the failing script
local end_time
end_time=$(LC_ALL=C date +%s)
local duration=$((end_time - start_time))
log_error "$test_description 测试失败 (退出码: $exit_code, 耗时: ${duration}s)"
log_error "详细日志: $test_log"
# 显示失败的最后几行
echo -e "${RED}错误详情 (最后10行):${NC}"
tail -10 "$test_log" | sed \'s/^/ /\' # Ensure sed doesn\'t cause issues
return 1 # Test script failed
fi
}
# 函数:运行所有测试
run_all_tests() {
log_step "开始执行完整测试套件..."
local total_tests=${#TESTS[@]}
local passed_tests=0
local failed_tests=0
local suite_start_time
suite_start_time=$(LC_ALL=C date +%s)
echo "" # Newline before progress
for i in "${!TESTS[@]}"; do
local current=$((i + 1))
show_progress $current $total_tests
echo "" # Newline after progress bar, before test execution logs
if run_test "${TESTS[$i]}"; then
((passed_tests++))
else
((failed_tests++))
if [ "${CONTINUE_ON_FAILURE:-true}" = "true" ]; then # Default to true
log_warning "测试失败,但继续执行剩余测试..."
else
log_error "测试失败,停止执行"
break # Exit loop
fi
fi
echo "" # Newline after test result
done
local suite_end_time
suite_end_time=$(LC_ALL=C date +%s)
local total_duration=$((suite_end_time - suite_start_time))
# Final progress update to 100% if all tests run
if [ $failed_tests -eq 0 ] || [ "${CONTINUE_ON_FAILURE:-true}" = "true" ]; then
show_progress $total_tests $total_tests
echo "" # Newline after final progress
fi
echo ""
log_step "测试套件执行完成"
log "总耗时: ${total_duration}s"
log "通过测试: $passed_tests/$total_tests"
log "失败测试: $failed_tests/$total_tests"
# 生成测试报告
generate_test_report $passed_tests $failed_tests $total_duration
if [ $failed_tests -eq 0 ]; then
log_success "所有测试都通过了!🎉"
return 0 # Overall success
else
log_error "$failed_tests 个测试失败"
return 1 # Overall failure
fi
}
# 函数:生成测试报告
generate_test_report() {
local passed=$1
local failed=$2
local duration=$3
local total=$((passed + failed))
local success_rate=0
if [ $total -gt 0 ]; then
success_rate=$(( passed * 100 / total ))
fi
local report_file="$LOG_DIR/test_suite_report.txt"
log_step "生成测试报告..."
# Use cat with explicit EOF marker
cat > "$report_file" <<-EOF
Docker Hexo Static Blog v0.0.3 - 完整测试套件报告 (Linux)
========================================================
测试时间: $(LC_ALL=C date)
测试环境: Linux ($(uname -r))
Docker 版本: $(docker --version)
测试位置: $SCRIPT_DIR
测试结果概要:
- 总测试数: $total
- 通过测试: $passed
- 失败测试: $failed
- 成功率: $success_rate%
- 总耗时: ${duration}s
详细测试结果:
EOF
for test_info in "${TESTS[@]}"; do
local script_name="${test_info%%:*}"
local test_description="${test_info##*:}"
local test_log_file="$LOG_DIR/${script_name%.sh}.log" # Renamed to avoid conflict
echo "- $test_description:" >> "$report_file"
if [ -f "$test_log_file" ]; then
# Check for success markers more robustly
# Assuming test scripts output specific success strings or exit 0
# For now, rely on the run_test logic that populates passed/failed counts
# This report section can be enhanced if tests output specific markers
if grep -q -E "SUCCESS|成功|✓|测试通过" "$test_log_file" 2>/dev/null || \
( [ -s "$test_log_file" ] && ! grep -q -E "FAIL|失败|✗|测试失败" "$test_log_file" 2>/dev/null && \
grep -q "构建成功" "$test_log_file" 2>/dev/null ) ; then # Example for build_test
echo " 状态: ✓ 通过" >> "$report_file"
elif grep -q -E "FAIL|失败|✗|测试失败" "$test_log_file" 2>/dev/null; then
echo " 状态: ✗ 失败" >> "$report_file"
else
# If log exists but no clear pass/fail, mark as indeterminate or check exit code if stored
echo " 状态: ? 结果未知 (检查日志)" >> "$report_file"
fi
echo " 日志: $test_log_file" >> "$report_file"
else
echo " 状态: ? 未执行或日志丢失" >> "$report_file"
fi
echo "" >> "$report_file"
done
# 添加系统信息
echo "系统信息:" >> "$report_file"
echo "- 操作系统: $(uname -s)" >> "$report_file"
echo "- 内核版本: $(uname -r)" >> "$report_file"
echo "- 架构: $(uname -m)" >> "$report_file"
# Ensure free and df commands don't fail due to locale
echo "- 可用内存: $(LC_ALL=C free -h | grep '^Mem:' | awk '{print $7}')" >> "$report_file"
echo "- 磁盘空间: $(LC_ALL=C df -h . | tail -1 | awk '{print $4}')" >> "$report_file"
log_success "测试报告已生成: $report_file"
}
# 函数:显示帮助信息
show_help() {
cat <<-EOF
Docker Hexo Static Blog v0.0.3 - Linux 完整测试套件
用法: $0 [选项]
选项:
--help, -h 显示此帮助信息
--clean-start 清理旧资源后开始测试 (设置 CLEAN_START=true)
--stop-on-failure 第一个测试失败时停止 (设置 CONTINUE_ON_FAILURE=false)
--list 列出所有可用的测试
--test <script_name> 只运行指定的测试脚本 (例如: build_test.sh)
--report-only 只生成报告,不运行测试 (需要现有日志)
环境变量:
CLEAN_START=true 等同于 --clean-start
CONTINUE_ON_FAILURE=false 等同于 --stop-on-failure
示例:
$0 # 运行完整测试套件
$0 --clean-start # 清理后运行测试
$0 --test build_test.sh # 只运行构建测试
测试脚本:
EOF
for test_info in "${TESTS[@]}"; do
local script_name="${test_info%%:*}"
local test_description="${test_info##*:}"
printf " %-25s %s\\n" "$script_name" "$test_description"
done
}
# 函数:列出测试
list_tests() {
echo "可用的测试脚本:"
echo ""
for i in "${!TESTS[@]}"; do
local test_info="${TESTS[$i]}"
local script_name="${test_info%%:*}"
local test_description="${test_info##*:}"
printf "%d. %-25s - %s\\n" $((i + 1)) "$script_name" "$test_description"
done
}
# 函数:运行单个指定测试
run_single_test() {
local target_script="$1"
# Find the test_info for the target_script
local found_test_info=""
for test_info_item in "${TESTS[@]}"; do
local script_name_item="${test_info_item%%:*}"
if [ "$script_name_item" = "$target_script" ]; then
found_test_info="$test_info_item"
break
fi
done
if [ -z "$found_test_info" ]; then
log_error "未找到测试脚本: $target_script"
echo "可用的测试脚本:"
list_tests # Call list_tests function
return 1
fi
# Prepare environment for single test run
# Note: SUITE_LOG might not be set if main() isn't fully run.
# For simplicity, single test runs will log to their own file and console.
# More robust single test logging would require initializing SUITE_LOG.
mkdir -p "$LOG_DIR" # Ensure log dir exists
export SUITE_LOG="$LOG_DIR/single_test_run_$(date +%Y%m%d_%H%M%S).log" # Temporary suite log for this run
echo "Running single test: $target_script. Main log: $SUITE_LOG" > "$SUITE_LOG"
log_step "运行单个测试: $target_script"
# check_prerequisites # Optional: run for single test
prepare_test_environment # Run prepare, it handles CLEAN_START
run_test "$found_test_info"
local test_exit_code=$?
if [ $test_exit_code -eq 0 ]; then
log_success "$target_script 测试通过"
else
log_error "$target_script 测试失败"
fi
return $test_exit_code
}
# 主函数
main() {
# Ensure LOG_DIR exists before SUITE_LOG is defined
mkdir -p "$LOG_DIR"
# Define SUITE_LOG here so all log functions can use it
# This was previously in the parameter parsing block for main call
export SUITE_LOG="$LOG_DIR/test_suite_$(LC_ALL=C date +%Y%m%d_%H%M%S).log"
show_banner
log "Docker Hexo Static Blog v0.0.3 完整测试套件启动"
log "================================================"
log "脚本位置: $SCRIPT_DIR"
log "Dockerfile 位置: $DOCKERFILE_DIR"
log "日志位置: $LOG_DIR (主套件日志: $SUITE_LOG)"
check_prerequisites
prepare_test_environment # Handles CLEAN_START logic
run_all_tests
# run_all_tests returns 0 for all pass, 1 for any failure
return $? # Propagate the exit status of run_all_tests
}
# --- Main script execution starts here ---
# Default behavior: continue on failure
export CONTINUE_ON_FAILURE="${CONTINUE_ON_FAILURE:-true}"
# Default behavior: don't clean start unless specified
export CLEAN_START="${CLEAN_START:-false}"
# Argument parsing
if [ $# -eq 0 ]; then
main
exit $? # Exit with main's status
fi
while [ $# -gt 0 ]; do
case "$1" in
--help|-h)
show_help
exit 0
;;
--list)
list_tests
exit 0
;;
--clean-start)
export CLEAN_START=true
# If it's the only arg, run main. If others follow, they'll be processed.
shift
if [ $# -eq 0 ]; then main; exit $?; fi
;;
--stop-on-failure)
export CONTINUE_ON_FAILURE=false
shift
if [ $# -eq 0 ]; then main; exit $?; fi
;;
--test)
if [ -z "$2" ]; then
# Temporarily set SUITE_LOG for this error message if not already set
export SUITE_LOG="${SUITE_LOG:-$LOG_DIR/error_$(date +%Y%m%d_%H%M%S).log}"
mkdir -p "$(dirname "$SUITE_LOG")"
log_error "请指定要运行的测试脚本名称 (例如: build_test.sh)"
show_help
exit 1
fi
run_single_test "$2"
exit $? # Exit with single test's status
;;
--report-only)
# Ensure SUITE_LOG is defined for logging within generate_test_report
export SUITE_LOG="${SUITE_LOG:-$LOG_DIR/report_only_$(date +%Y%m%d_%H%M%S).log}"
mkdir -p "$(dirname "$SUITE_LOG")"
if [ -d "$LOG_DIR" ] && [ "$(find "$LOG_DIR" -name \'*.log\' -print -quit)" ]; then
# Dummy values for passed/failed/duration as we are only generating report from existing logs
generate_test_report 0 0 0
exit $?
else
log_error "没有找到测试日志目录 $LOG_DIR 或日志文件以生成报告。"
exit 1
fi
;;
*)
# If main hasn't run yet due to options, run it now.
# This handles the case where options like --clean-start are given,
# and then the script is expected to run the full suite.
if ! ps -p $$ -o comm= | grep -q "start.sh"; then # Basic check if main was already invoked
main
exit $?
else
# If options were processed and we still have an unknown one, it's an error.
export SUITE_LOG="${SUITE_LOG:-$LOG_DIR/error_$(date +%Y%m%d_%H%M%S).log}"
mkdir -p "$(dirname "$SUITE_LOG")"
log_error "未知参数: $1"
show_help
exit 1
fi
;;
esac
# shift # Shift only if argument was consumed and not an exit/main call
done
# If loop finishes and main hasn't been called (e.g. only options like --clean-start were set)
# This logic might be complex depending on desired option interactions.
# A common pattern is to set flags and have one call to main() at the end.
# The current structure calls main or exits within the case statement for most paths.
# If we reach here, it implies options were processed that set flags, and now main should run.
if [ "${#BASH_SOURCE[@]}" -eq 1 ] && [ -z "${_MAIN_CALLED_VIA_OPTIONS:-}" ]; then
# Check if main was called by an option that then shifted out all args
# This is a fallback if options like --clean-start are given,
# and the parameter parsing was modified to call main if --clean-start is the only arg.
# So this path might not be strictly needed anymore.
: # Do nothing, main should have been called or script exited.
fi
# Final exit status should be from main or specific option handlers.
# Bash scripts exit with the status of the last command if not specified.
# The explicit `exit $?` after main calls ensures this.