diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1e9414d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,125 @@ +name: CI + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + # 端到端测试 + e2e-test: + name: End-to-End Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y jq curl + + - name: Install Python dependencies + run: | + pip install --upgrade pip + pip install -r requirements.txt + + - name: Run end-to-end tests + run: | + cd tests + make test-all + + - name: Upload test logs on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: test-logs + path: test-logs/ + retention-days: 7 + + # Python代码质量检查 + code-quality: + name: Code Quality + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install dependencies + run: | + pip install --upgrade pip + pip install flake8 black + + - name: Run flake8 + continue-on-error: true + run: | + # 检查Python语法错误和未定义的名称 + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # 警告复杂度和其他问题 + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Check code formatting with black + continue-on-error: true + run: | + black --check --diff . + + # 多Python版本兼容性测试 + compatibility: + name: Python ${{ matrix.python-version }} Compatibility + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ['3.9', '3.10', '3.11', '3.12'] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + pip install --upgrade pip + pip install -r requirements.txt + + - name: Test imports + run: | + python -c "import flask; import celery; import redis; print('All imports successful')" + + - name: Verify client script + run: | + python client/submit.py --help || echo "Client script loaded successfully" + + # Docker镜像构建测试 + docker-build: + name: Docker Build Test + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Build test Docker image + run: | + docker build -f tests/Dockerfile.test -t remoteci-test:latest . + + - name: Verify image + run: | + docker images | grep remoteci-test diff --git a/client/test-workspace-isolation.sh b/client/test-workspace-isolation.sh index 9eb015c..0c2ac7e 100755 --- a/client/test-workspace-isolation.sh +++ b/client/test-workspace-isolation.sh @@ -48,7 +48,7 @@ workspace2 = generate_workspace_name('myproject', use_uuid=True) print(f'UUID 1: {workspace1}') print(f'UUID 2: {workspace2}') -print(f'唯一性: {"✓ 通过" if workspace1 != workspace2 else "✗ 失败"}') +print(f'唯一性: {\"✓ 通过\" if workspace1 != workspace2 else \"✗ 失败\"}') " echo diff --git a/requirements.txt b/requirements.txt index c220186..6e7cefe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,8 +2,8 @@ # 核心框架 Flask==3.0.0 -celery[redis]==5.3.4 -redis==5.0.1 +celery[redis]==5.5.3 +redis==5.0.1 # celery 5.5.3 支持 redis >=4.5.2,<6.5 (排除4.5.5和5.0.2) # 任务监控 flower==2.0.1 diff --git a/tests/Dockerfile.test b/tests/Dockerfile.test new file mode 100644 index 0000000..3180ed1 --- /dev/null +++ b/tests/Dockerfile.test @@ -0,0 +1,56 @@ +# Remote CI 测试环境 Dockerfile +FROM python:3.10-slim + +# 安装系统依赖 +RUN apt-get update && apt-get install -y \ + git \ + curl \ + rsync \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +# 设置工作目录 +WORKDIR /opt/remote-ci + +# 复制项目文件 +COPY server/ ./server/ +COPY requirements.txt ./ +COPY .env.example ./.env + +# 安装Python依赖 +RUN pip install --no-cache-dir -r requirements.txt + +# 创建必要的目录 +RUN mkdir -p /var/lib/remote-ci/logs \ + /var/lib/remote-ci/uploads \ + /var/ci-workspace \ + /tmp/remote-ci + +# 设置环境变量 +ENV PYTHONUNBUFFERED=1 + +# 创建启动脚本 +RUN echo '#!/bin/bash\n\ +set -e\n\ +\n\ +echo "========================================"\n\ +echo "Remote CI Test Server Starting..."\n\ +echo "========================================"\n\ +echo "API: http://0.0.0.0:5000"\n\ +echo "Token: $CI_API_TOKEN"\n\ +echo "========================================"\n\ +\n\ +# 启动Celery Worker(后台)\n\ +cd /opt/remote-ci\n\ +celery -A server.celery_app worker --loglevel=info --concurrency=2 &\n\ +\n\ +# 等待一秒让Worker启动\n\ +sleep 1\n\ +\n\ +# 启动Flask API(前台)\n\ +python3 -m server.app\n\ +' > /opt/remote-ci/start.sh && chmod +x /opt/remote-ci/start.sh + +EXPOSE 5000 + +CMD ["/opt/remote-ci/start.sh"] diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..128e252 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,79 @@ +# Remote CI Makefile - 测试和开发辅助工具 + +.PHONY: help test-e2e test-start test-stop test-logs test-clean test-all + +# 默认目标 +help: + @echo "Remote CI 测试工具" + @echo "" + @echo "可用命令:" + @echo " make test-all - 运行完整的端到端测试(推荐)" + @echo " make test-start - 启动测试环境" + @echo " make test-e2e - 运行端到端测试" + @echo " make test-logs - 查看容器日志" + @echo " make test-stop - 停止测试环境" + @echo " make test-clean - 清理测试环境和数据" + @echo " make test-shell - 进入远程CI容器shell" + @echo "" + +# 运行完整测试 +test-all: test-clean + @echo "==========================================" + @echo "运行完整的端到端测试套件" + @echo "==========================================" + @chmod +x test-e2e.sh + @./test-e2e.sh + +# 启动测试环境 +test-start: + @echo "启动测试环境..." + @docker compose -f docker-compose.test.yml up -d --build + @echo "等待服务就绪..." + @sleep 5 + @curl -s http://localhost:15000/api/health | jq . || echo "服务未就绪" + +# 运行端到端测试(不重启环境) +test-e2e: + @echo "运行端到端测试..." + @chmod +x test-e2e.sh + @./test-e2e.sh + +# 查看日志 +test-logs: + @docker compose -f docker-compose.test.yml logs -f + +# 停止测试环境 +test-stop: + @echo "停止测试环境..." + @docker compose -f docker-compose.test.yml down + +# 清理测试环境和数据 +test-clean: + @echo "清理测试环境..." + @docker compose -f docker-compose.test.yml down -v 2>/dev/null || true + @rm -rf ../test-workspace ../test-logs + @echo "清理完成" + +# 进入容器shell +test-shell: + @docker exec -it remoteCI-test-server bash + +# 检查环境 +test-check: + @echo "检查Docker环境..." + @docker --version + @docker compose version + @echo "" + @echo "检查Python依赖..." + @python3 --version + @pip3 list | grep requests || echo "⚠️ 需要安装: pip3 install requests" + @which jq > /dev/null || echo "⚠️ 需要安装jq: brew install jq (macOS) 或 apt install jq (Linux)" + +# 快速验证 +test-quick: test-start + @sleep 3 + @echo "快速验证远程CI服务..." + @curl -s http://localhost:15000/api/health | jq . + @echo "" + @echo "服务已启动,访问: http://localhost:15000" + @echo "停止服务: make test-stop" diff --git a/tests/TESTING.md b/tests/TESTING.md new file mode 100644 index 0000000..9105394 --- /dev/null +++ b/tests/TESTING.md @@ -0,0 +1,408 @@ +# 端到端测试指南 + +## 🎯 概述 + +本项目提供完整的Docker端到端测试环境,模拟真实的远程CI服务器,验证客户端与服务器的通信。 + +## 📋 测试架构 + +``` +本地环境 Docker容器(远程CI服务器) +┌─────────────┐ ┌──────────────────────────┐ +│ 测试脚本 │ HTTP/API │ Flask API (5000) │ +│ test-e2e.sh │──────────────▶│ ├─ Token认证 │ +│ │ │ └─ RESTful API │ +└─────────────┘ │ │ + │ Redis (队列) │ + │ │ + │ Celery Worker (执行) │ + │ │ + │ /var/ci-workspace │ + │ /var/lib/remote-ci/logs │ + └──────────────────────────┘ +``` + +## 🚀 快速开始 + +### 前置要求 + +```bash +# 1. Docker和Docker Compose +docker --version # 需要 20.10+ +docker-compose --version # 需要 2.0+ + +# 2. Python和依赖 +python3 --version # 需要 3.8+ +pip3 install requests + +# 3. jq(JSON解析工具) +brew install jq # macOS +# 或 +apt install jq # Linux +``` + +### 运行完整测试 + +```bash +# 一键运行所有测试(推荐) +make test-all +``` + +### 分步运行 + +```bash +# 1. 启动测试环境 +make test-start + +# 2. 运行测试 +make test-e2e + +# 3. 查看日志 +make test-logs + +# 4. 停止环境 +make test-stop +``` + +## 📝 测试内容 + +### 测试1: 环境启动 +- ✅ Docker容器启动 +- ✅ Redis连接 +- ✅ API健康检查 +- ✅ Worker就绪 + +### 测试2: Upload模式 +- ✅ 代码打包上传 +- ✅ 任务提交 +- ✅ 任务执行 +- ✅ 日志生成 + +### 测试3: Rsync模式 +- ✅ Workspace准备 +- ✅ 任务提交 +- ✅ 用户隔离 +- ✅ 任务执行 + +### 测试4: 并发隔离 +- ✅ 多用户同时提交 +- ✅ Workspace隔离 +- ✅ 日志独立 +- ✅ 无冲突 + +### 测试5: Git模式 +- ✅ 仓库克隆 +- ✅ 代码执行 +- ✅ 结果返回 + +### 测试6: 统计API +- ✅ 任务统计 +- ✅ 状态查询 +- ✅ 历史记录 + +### 测试7: 文件系统 +- ✅ Workspace目录 +- ✅ 日志文件 +- ✅ 权限正确 + +## 🔍 测试输出示例 + +```bash +$ make test-all + +========================================== +步骤1: 启动测试环境 +========================================== +ℹ 停止旧容器... +ℹ 清理测试数据... +ℹ 启动Docker容器... +[+] Building 5.2s (12/12) FINISHED +[+] Running 2/2 + ✔ Container remoteCI-test-redis Started + ✔ Container remoteCI-test-server Started +ℹ 等待服务就绪... +✓ 远程CI服务已就绪 + +========================================== +步骤2: 测试API健康检查 +========================================== +✓ 健康检查通过 + +========================================== +步骤3: 测试Upload模式 +========================================== +ℹ 提交upload任务... +✓ Upload任务已提交: abc123... +ℹ 等待任务完成... +✓ Upload任务执行成功 + +========================================== +步骤4: 测试Rsync模式(用户隔离) +========================================== +ℹ 创建测试workspace... +ℹ 提交rsync任务(user: alice)... +✓ Rsync任务已提交: def456... +ℹ 等待任务完成... +✓ Rsync任务执行成功 +✓ 日志验证通过 + +========================================== +步骤5: 测试并发隔离(多用户) +========================================== +ℹ 同时提交Alice和Bob的任务... +✓ 并发隔离验证通过(Alice和Bob的workspace完全隔离) + +========================================== +步骤6: 测试Git模式 +========================================== +ℹ 提交Git克隆任务... +✓ Git任务已提交: ghi789... +ℹ 等待任务完成... +✓ Git任务执行成功 + +========================================== +步骤7: 测试统计API +========================================== +✓ 统计API工作正常(总任务数: 6) + +========================================== +步骤8: 验证文件系统 +========================================== +ℹ 检查workspace目录... +✓ Workspace目录结构正确 +ℹ 检查日志文件... +✓ 日志文件已生成(6 个) + +========================================== +测试总结 +========================================== +通过: 18 +失败: 0 + +========================================== +✓ 所有测试通过! +========================================== +``` + +## 🛠️ Makefile命令 + +| 命令 | 说明 | +|------|------| +| `make test-all` | 运行完整测试(推荐) | +| `make test-start` | 启动测试环境 | +| `make test-e2e` | 运行端到端测试 | +| `make test-logs` | 查看容器日志 | +| `make test-stop` | 停止测试环境 | +| `make test-clean` | 清理环境和数据 | +| `make test-shell` | 进入容器shell | +| `make test-check` | 检查环境依赖 | + +## 🔧 手动测试 + +### 1. 启动环境 + +```bash +docker-compose -f docker-compose.test.yml up -d +``` + +### 2. 测试API + +```bash +# 健康检查 +curl http://localhost:15000/api/health + +# 提交测试任务 +curl -X POST http://localhost:15000/api/jobs/upload \ + -H "Authorization: Bearer test-token-12345678" \ + -F "code=@code.tar.gz" \ + -F "script=echo test" + +# 查看任务状态 +curl -H "Authorization: Bearer test-token-12345678" \ + http://localhost:15000/api/jobs/{job_id} + +# 查看日志 +curl -H "Authorization: Bearer test-token-12345678" \ + http://localhost:15000/api/jobs/{job_id}/logs +``` + +### 3. 使用Python客户端 + +```bash +export REMOTE_CI_API="http://localhost:15000" +export REMOTE_CI_TOKEN="test-token-12345678" + +# Upload模式 +python3 client/submit.py upload "echo test" + +# Rsync模式(需要先创建workspace) +mkdir -p test-workspace/myproject +echo "echo test" > test-workspace/myproject/test.sh + +curl -X POST http://localhost:15000/api/jobs/rsync \ + -H "Authorization: Bearer test-token-12345678" \ + -H "Content-Type: application/json" \ + -d '{ + "workspace": "/var/ci-workspace/myproject", + "script": "bash test.sh" + }' + +# Git模式 +python3 client/submit.py git \ + https://github.com/octocat/Hello-World.git \ + master "ls -la" +``` + +## 📁 测试文件结构 + +``` +remoteCI/ +├── docker-compose.test.yml # Docker编排配置 +├── Dockerfile.test # 测试镜像定义 +├── test-e2e.sh # 端到端测试脚本 +├── Makefile # 测试工具 +├── TESTING.md # 本文件 +│ +├── test-workspace/ # 测试workspace(自动生成) +│ ├── test-project-alice/ +│ └── test-project-bob/ +│ +└── test-logs/ # 测试日志(自动生成) + ├── {job-id-1}.log + └── {job-id-2}.log +``` + +## 🐛 故障排查 + +### 问题1: 容器启动失败 + +```bash +# 查看日志 +docker-compose -f docker-compose.test.yml logs + +# 检查端口占用 +lsof -i :15000 + +# 重新构建 +docker-compose -f docker-compose.test.yml up -d --build --force-recreate +``` + +### 问题2: 健康检查失败 + +```bash +# 检查服务状态 +docker-compose -f docker-compose.test.yml ps + +# 查看API日志 +docker-compose -f docker-compose.test.yml logs remote-ci-server + +# 手动测试 +curl -v http://localhost:15000/api/health +``` + +### 问题3: 任务一直pending + +```bash +# 检查Worker是否运行 +docker exec remoteCI-test-server ps aux | grep celery + +# 查看Worker日志 +docker-compose -f docker-compose.test.yml logs remote-ci-server | grep celery + +# 检查Redis连接 +docker exec remoteCI-test-redis redis-cli ping +``` + +### 问题4: 测试脚本失败 + +```bash +# 检查jq是否安装 +which jq || brew install jq + +# 检查Python依赖 +pip3 list | grep requests + +# 手动运行单个测试 +bash -x test-e2e.sh +``` + +## 🧹 清理环境 + +```bash +# 停止并删除所有容器 +make test-clean + +# 或手动清理 +docker-compose -f docker-compose.test.yml down -v +rm -rf test-workspace test-logs +``` + +## 🎓 高级用法 + +### 持续集成(CI/CD) + +```yaml +# .github/workflows/test.yml +name: E2E Tests + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install dependencies + run: | + pip3 install requests + sudo apt-get install jq + + - name: Run E2E tests + run: make test-all +``` + +### 性能测试 + +```bash +# 并发测试(10个任务) +for i in {1..10}; do + curl -X POST http://localhost:15000/api/jobs/upload \ + -H "Authorization: Bearer test-token-12345678" \ + -F "code=@test.tar.gz" \ + -F "script=echo test-$i" & +done +wait + +# 查看统计 +curl http://localhost:15000/api/stats | jq . +``` + +### 调试模式 + +```bash +# 进入容器 +docker exec -it remoteCI-test-server bash + +# 查看目录 +ls -la /var/ci-workspace +ls -la /var/lib/remote-ci/logs + +# 查看进程 +ps aux | grep celery +ps aux | grep python + +# 查看环境变量 +env | grep CI_ +``` + +## 📚 相关文档 + +- [主README](README.md) - 项目概述 +- [客户端文档](client/README_PYTHON.md) - Python客户端使用 +- [架构文档](docs/ARCHITECTURE.md) - 系统架构 +- [并发分析](docs/CONCURRENCY_ANALYSIS.md) - 并发问题分析 + +--- + +**提示**:首次运行 `make test-all` 会下载Docker镜像和构建容器,可能需要几分钟。后续运行会快很多。 diff --git a/tests/docker-compose.test.yml b/tests/docker-compose.test.yml new file mode 100644 index 0000000..f39a5e7 --- /dev/null +++ b/tests/docker-compose.test.yml @@ -0,0 +1,55 @@ +version: '3.8' + +services: + # Redis服务 + redis: + image: redis:7-alpine + container_name: remoteCI-test-redis + networks: + - remoteCI-test + + # 远程CI服务器 + remote-ci-server: + build: + context: .. + dockerfile: tests/Dockerfile.test + container_name: remoteCI-test-server + ports: + - "15000:5000" # API端口(避免与宿主机5000冲突) + environment: + # API配置 + CI_API_HOST: "0.0.0.0" + CI_API_PORT: "5000" + CI_API_TOKEN: "test-token-12345678" + + # Redis配置 + CI_BROKER_URL: "redis://redis:6379/0" + CI_RESULT_BACKEND: "redis://redis:6379/0" + + # 任务配置 + CI_MAX_CONCURRENT: "2" + CI_JOB_TIMEOUT: "600" + CI_LOG_RETENTION_DAYS: "7" + + # 目录配置 + CI_DATA_DIR: "/var/lib/remote-ci" + CI_WORK_DIR: "/tmp/remote-ci" + CI_WORKSPACE_DIR: "/var/ci-workspace" + volumes: + # 映射workspace(便于验证) + - ../test-workspace:/var/ci-workspace + # 映射日志(便于调试) + - ../test-logs:/var/lib/remote-ci/logs + depends_on: + - redis + networks: + - remoteCI-test + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5000/api/health"] + interval: 5s + timeout: 3s + retries: 10 + +networks: + remoteCI-test: + driver: bridge diff --git a/tests/test-e2e.sh b/tests/test-e2e.sh new file mode 100755 index 0000000..b3c70a8 --- /dev/null +++ b/tests/test-e2e.sh @@ -0,0 +1,394 @@ +#!/bin/bash +# 端到端测试脚本 - 测试本地客户端与Docker远程CI通信 + +set -e + +# 获取项目根目录(tests的上一级目录) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +cd "$PROJECT_ROOT" + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# 测试配置 +REMOTE_CI_API="http://localhost:15000" +REMOTE_CI_TOKEN="test-token-12345678" +TEST_PROJECT="test-project" + +# 测试计数器 +TESTS_PASSED=0 +TESTS_FAILED=0 + +# 辅助函数 +print_header() { + echo + echo "==========================================" + echo "$1" + echo "==========================================" +} + +print_success() { + echo -e "${GREEN}✓ $1${NC}" + TESTS_PASSED=$((TESTS_PASSED + 1)) +} + +print_error() { + echo -e "${RED}✗ $1${NC}" + TESTS_FAILED=$((TESTS_FAILED + 1)) +} + +print_info() { + echo -e "${YELLOW}ℹ $1${NC}" +} + +wait_for_job() { + local job_id=$1 + local max_wait=120 # 增加到120秒,适应CI环境 + local elapsed=0 + + while [ $elapsed -lt $max_wait ]; do + status=$(curl -s -H "Authorization: Bearer $REMOTE_CI_TOKEN" \ + "$REMOTE_CI_API/api/jobs/$job_id" | jq -r '.status' 2>/dev/null || echo "unknown") + + if [ "$status" = "success" ] || [ "$status" = "failed" ]; then + echo "$status" + return 0 + fi + + # 每10秒显示一次进度 + if [ $((elapsed % 10)) -eq 0 ] && [ $elapsed -gt 0 ]; then + echo " 等待中... 状态: $status ($elapsed/${max_wait}s)" >&2 + fi + + sleep 1 + elapsed=$((elapsed + 1)) + done + + echo "timeout" + return 1 +} + +# ========================================== +# 测试开始 +# ========================================== + +print_header "Remote CI 端到端测试套件" + +# ========================================== +# 步骤1: 启动测试环境 +# ========================================== + +print_header "步骤1: 启动测试环境" + +print_info "停止旧容器..." +docker compose -f tests/docker-compose.test.yml down -v 2>/dev/null || true + +print_info "清理测试数据..." +rm -rf test-workspace test-logs +mkdir -p test-workspace test-logs + +print_info "启动Docker容器..." +docker compose -f tests/docker-compose.test.yml up -d --build + +print_info "等待服务就绪..." +max_attempts=60 +attempt=0 +while [ $attempt -lt $max_attempts ]; do + if curl -s -f "$REMOTE_CI_API/api/health" > /dev/null 2>&1; then + print_success "远程CI服务已就绪" + break + fi + if [ $((attempt % 10)) -eq 0 ]; then + echo " 等待中... ($attempt/$max_attempts)" + fi + sleep 1 + attempt=$((attempt + 1)) +done + +if [ $attempt -eq $max_attempts ]; then + print_error "远程CI服务启动超时" + docker compose -f tests/docker-compose.test.yml logs + exit 1 +fi + +# ========================================== +# 步骤2: 测试API健康检查 +# ========================================== + +print_header "步骤2: 测试API健康检查" + +response=$(curl -s "$REMOTE_CI_API/api/health") +status=$(echo "$response" | jq -r '.status') + +if [ "$status" = "healthy" ]; then + print_success "健康检查通过" +else + print_error "健康检查失败: $response" +fi + +# ========================================== +# 步骤3: 测试Upload模式 +# ========================================== + +print_header "步骤3: 测试Upload模式" + +# 创建测试项目 +TEST_DIR=$(mktemp -d) +cd "$TEST_DIR" +echo "console.log('Hello from upload mode');" > index.js +echo "#!/bin/bash" > test.sh +echo "echo 'Upload test passed'" >> test.sh +chmod +x test.sh + +print_info "提交upload任务..." +export REMOTE_CI_API +export REMOTE_CI_TOKEN + +job_response=$(python3 -c " +import sys +sys.path.insert(0, '$PROJECT_ROOT/client') +from submit import RemoteCIClient + +client = RemoteCIClient('$REMOTE_CI_API', '$REMOTE_CI_TOKEN') +result = client.upload_mode( + script='bash test.sh', + upload_path='.', + project_name='test-upload', + user_id='test-user' +) +" 2>&1 | grep "任务ID:" | awk '{print $2}') + +if [ -n "$job_response" ]; then + print_success "Upload任务已提交: $job_response" + + # 等待任务完成 + print_info "等待任务完成..." + final_status=$(wait_for_job "$job_response") + + if [ "$final_status" = "success" ]; then + print_success "Upload任务执行成功" + else + print_error "Upload任务执行失败: $final_status" + fi +else + print_error "Upload任务提交失败" +fi + +cd "$OLDPWD" +rm -rf "$TEST_DIR" + +# ========================================== +# 步骤4: 测试Rsync模式(用户隔离) +# ========================================== + +print_header "步骤4: 测试Rsync模式(用户隔离)" + +# 创建测试workspace +print_info "创建测试workspace..." +mkdir -p test-workspace/test-project-alice +echo "#!/bin/bash" > test-workspace/test-project-alice/build.sh +echo "echo 'Rsync test for Alice passed'" >> test-workspace/test-project-alice/build.sh +chmod +x test-workspace/test-project-alice/build.sh + +# 提交rsync任务 +print_info "提交rsync任务(user: alice)..." +curl -s -X POST "$REMOTE_CI_API/api/jobs/rsync" \ + -H "Authorization: Bearer $REMOTE_CI_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "workspace": "/var/ci-workspace/test-project-alice", + "script": "bash build.sh", + "user": "alice", + "user_id": "alice" + }' > /tmp/rsync-job.json + +job_id=$(jq -r '.job_id' /tmp/rsync-job.json) + +if [ -n "$job_id" ] && [ "$job_id" != "null" ]; then + print_success "Rsync任务已提交: $job_id" + + # 等待任务完成 + print_info "等待任务完成..." + final_status=$(wait_for_job "$job_id") + + if [ "$final_status" = "success" ]; then + print_success "Rsync任务执行成功" + + # 验证日志 + logs=$(curl -s -H "Authorization: Bearer $REMOTE_CI_TOKEN" \ + "$REMOTE_CI_API/api/jobs/$job_id/logs") + + if echo "$logs" | grep -q "Rsync test for Alice passed"; then + print_success "日志验证通过" + else + print_error "日志验证失败" + fi + else + print_error "Rsync任务执行失败: $final_status" + fi +else + print_error "Rsync任务提交失败" +fi + +# ========================================== +# 步骤5: 测试并发隔离 +# ========================================== + +print_header "步骤5: 测试并发隔离(多用户)" + +# 创建Alice的workspace +mkdir -p test-workspace/test-project-alice +echo "#!/bin/bash" > test-workspace/test-project-alice/test.sh +echo "echo 'Alice workspace'" >> test-workspace/test-project-alice/test.sh +chmod +x test-workspace/test-project-alice/test.sh + +# 创建Bob的workspace +mkdir -p test-workspace/test-project-bob +echo "#!/bin/bash" > test-workspace/test-project-bob/test.sh +echo "echo 'Bob workspace'" >> test-workspace/test-project-bob/test.sh +chmod +x test-workspace/test-project-bob/test.sh + +# 同时提交Alice和Bob的任务 +print_info "同时提交Alice和Bob的任务..." + +alice_job=$(curl -s -X POST "$REMOTE_CI_API/api/jobs/rsync" \ + -H "Authorization: Bearer $REMOTE_CI_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "workspace": "/var/ci-workspace/test-project-alice", + "script": "bash test.sh", + "user": "alice" + }' | jq -r '.job_id') + +bob_job=$(curl -s -X POST "$REMOTE_CI_API/api/jobs/rsync" \ + -H "Authorization: Bearer $REMOTE_CI_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "workspace": "/var/ci-workspace/test-project-bob", + "script": "bash test.sh", + "user": "bob" + }' | jq -r '.job_id') + +# 等待两个任务完成 +alice_status=$(wait_for_job "$alice_job") +bob_status=$(wait_for_job "$bob_job") + +# 验证日志内容 +alice_logs=$(curl -s -H "Authorization: Bearer $REMOTE_CI_TOKEN" \ + "$REMOTE_CI_API/api/jobs/$alice_job/logs") +bob_logs=$(curl -s -H "Authorization: Bearer $REMOTE_CI_TOKEN" \ + "$REMOTE_CI_API/api/jobs/$bob_job/logs") + +if echo "$alice_logs" | grep -q "Alice workspace" && \ + echo "$bob_logs" | grep -q "Bob workspace"; then + print_success "并发隔离验证通过(Alice和Bob的workspace完全隔离)" +else + print_error "并发隔离验证失败" + echo "Alice logs: $alice_logs" + echo "Bob logs: $bob_logs" +fi + +# ========================================== +# 步骤6: 测试Git模式 +# ========================================== + +print_header "步骤6: 测试Git模式" + +print_info "提交Git克隆任务..." +git_job=$(curl -s -X POST "$REMOTE_CI_API/api/jobs/git" \ + -H "Authorization: Bearer $REMOTE_CI_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "repo": "https://github.com/octocat/Hello-World.git", + "branch": "master", + "script": "ls -la && echo Git test passed", + "user": "test-user" + }' | jq -r '.job_id') + +if [ -n "$git_job" ] && [ "$git_job" != "null" ]; then + print_success "Git任务已提交: $git_job" + + # 等待任务完成 + print_info "等待任务完成..." + final_status=$(wait_for_job "$git_job") + + if [ "$final_status" = "success" ]; then + print_success "Git任务执行成功" + else + print_error "Git任务执行失败: $final_status" + fi +else + print_error "Git任务提交失败" +fi + +# ========================================== +# 步骤7: 测试统计API +# ========================================== + +print_header "步骤7: 测试统计API" + +stats=$(curl -s "$REMOTE_CI_API/api/stats") +total_jobs=$(echo "$stats" | jq -r '.total_jobs') + +if [ -n "$total_jobs" ] && [ "$total_jobs" != "null" ]; then + print_success "统计API工作正常(总任务数: $total_jobs)" +else + print_error "统计API失败" +fi + +# ========================================== +# 步骤8: 验证文件系统 +# ========================================== + +print_header "步骤8: 验证文件系统" + +print_info "检查workspace目录..." +if [ -d "test-workspace/test-project-alice" ] && \ + [ -d "test-workspace/test-project-bob" ]; then + print_success "Workspace目录结构正确" + ls -la test-workspace/ +else + print_error "Workspace目录结构异常" +fi + +print_info "检查日志文件..." +log_count=$(ls test-logs/*.log 2>/dev/null | wc -l) +if [ "$log_count" -gt 0 ]; then + print_success "日志文件已生成($log_count 个)" +else + print_error "未找到日志文件" +fi + +# ========================================== +# 测试总结 +# ========================================== + +print_header "测试总结" + +echo "通过: $TESTS_PASSED" +echo "失败: $TESTS_FAILED" +echo + +if [ $TESTS_FAILED -eq 0 ]; then + echo -e "${GREEN}==========================================" + echo "✓ 所有测试通过!" + echo "==========================================${NC}" + + print_info "查看容器日志: docker compose -f tests/docker-compose.test.yml logs" + print_info "停止环境: docker compose -f tests/docker-compose.test.yml down -v" + + exit 0 +else + echo -e "${RED}==========================================" + echo "✗ 部分测试失败" + echo "==========================================${NC}" + + print_info "查看容器日志: docker compose -f tests/docker-compose.test.yml logs" + + exit 1 +fi