434 lines
15 KiB
YAML
434 lines
15 KiB
YAML
name: Docker E2E Tests
|
||
|
||
# 在 Docker 環境中運行完整 E2E 測試
|
||
# 包含所有轉換工具和翻譯服務
|
||
|
||
on:
|
||
push:
|
||
branches: ["main"]
|
||
paths:
|
||
- "src/**"
|
||
- "tests/**"
|
||
- "Dockerfile"
|
||
- ".github/workflows/docker-e2e-tests.yml"
|
||
pull_request:
|
||
branches: ["main"]
|
||
paths:
|
||
- "src/**"
|
||
- "tests/**"
|
||
- "Dockerfile"
|
||
workflow_dispatch:
|
||
inputs:
|
||
run_translation_tests:
|
||
description: "Run translation tests (requires API keys)"
|
||
required: false
|
||
default: false
|
||
type: boolean
|
||
|
||
env:
|
||
REGISTRY: ghcr.io
|
||
IMAGE_NAME: ${{ github.repository }}
|
||
|
||
concurrency:
|
||
group: ${{ github.workflow }}-${{ github.ref }}
|
||
cancel-in-progress: true
|
||
|
||
jobs:
|
||
# ============================================
|
||
# Job 1: Build test image
|
||
# ============================================
|
||
build-test-image:
|
||
name: Build Test Image
|
||
runs-on: ubuntu-24.04
|
||
permissions:
|
||
contents: read
|
||
packages: write
|
||
|
||
outputs:
|
||
image: ${{ steps.image.outputs.image }}
|
||
|
||
steps:
|
||
- name: Checkout repository
|
||
uses: actions/checkout@v4
|
||
|
||
# 使用專門的 action 進行激進的磁碟清理
|
||
- name: Free disk space (aggressive)
|
||
uses: jlumbroso/free-disk-space@main
|
||
with:
|
||
tool-cache: true
|
||
android: true
|
||
dotnet: true
|
||
haskell: true
|
||
large-packages: true
|
||
docker-images: true
|
||
swap-storage: true
|
||
|
||
- name: Additional disk cleanup
|
||
run: |
|
||
echo "🧹 Additional disk cleanup..."
|
||
sudo rm -rf /usr/local/share/boost || true
|
||
sudo rm -rf /usr/share/swift || true
|
||
sudo rm -rf /opt/hostedtoolcache/CodeQL || true
|
||
docker system prune -af --volumes || true
|
||
echo "📊 Available space after cleanup:"
|
||
df -h /
|
||
|
||
- name: Set up Docker Buildx
|
||
uses: docker/setup-buildx-action@v3
|
||
|
||
- name: Login to GitHub Container Registry
|
||
uses: docker/login-action@v3
|
||
with:
|
||
registry: ${{ env.REGISTRY }}
|
||
username: ${{ github.actor }}
|
||
password: ${{ secrets.GITHUB_TOKEN }}
|
||
|
||
- name: downcase REPO
|
||
run: echo "REPO=${GITHUB_REPOSITORY@L}" >> "${GITHUB_ENV}"
|
||
|
||
- name: Build and push test image
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: .
|
||
push: true
|
||
tags: ${{ env.REGISTRY }}/${{ env.REPO }}:test-${{ github.sha }}
|
||
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.REPO }}:buildcache-linux-amd64
|
||
cache-to: type=inline
|
||
# 🔥 CACHE_BUST 強制重新執行模型下載層
|
||
build-args: |
|
||
CACHE_BUST=${{ github.run_id }}${{ github.run_attempt }}
|
||
|
||
- name: Set image output
|
||
id: image
|
||
run: echo "image=${{ env.REGISTRY }}/${{ env.REPO }}:test-${{ github.sha }}" >> $GITHUB_OUTPUT
|
||
|
||
# ============================================
|
||
# Job 2: Run E2E tests in Docker
|
||
# ============================================
|
||
e2e-tests:
|
||
name: E2E Tests in Docker
|
||
runs-on: ubuntu-24.04
|
||
needs: build-test-image
|
||
timeout-minutes: 30
|
||
|
||
services:
|
||
# Run the test image as a service
|
||
convertx:
|
||
image: ${{ needs.build-test-image.outputs.image }}
|
||
credentials:
|
||
username: ${{ github.actor }}
|
||
password: ${{ secrets.GITHUB_TOKEN }}
|
||
ports:
|
||
- 3000:3000
|
||
options: >-
|
||
--health-cmd "curl -f http://localhost:3000/healthcheck || exit 1"
|
||
--health-interval 10s
|
||
--health-timeout 5s
|
||
--health-retries 10
|
||
--health-start-period 30s
|
||
|
||
steps:
|
||
- name: Checkout repository
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Wait for service to be healthy
|
||
run: |
|
||
echo "⏳ Waiting for ConvertX service..."
|
||
for i in {1..30}; do
|
||
if curl -f http://localhost:3000/healthcheck 2>/dev/null; then
|
||
echo "✅ Service is healthy!"
|
||
break
|
||
fi
|
||
echo "Waiting... ($i/30)"
|
||
sleep 5
|
||
done
|
||
|
||
- name: Run API health check
|
||
run: |
|
||
echo "🔍 Testing API endpoints..."
|
||
|
||
# Health check
|
||
curl -f http://localhost:3000/healthcheck
|
||
echo ""
|
||
|
||
# Check if main page loads
|
||
curl -f -o /dev/null -w "%{http_code}" http://localhost:3000/
|
||
echo ""
|
||
|
||
echo "✅ API health checks passed!"
|
||
|
||
# ============================================
|
||
# Job 3: Run comprehensive E2E tests inside container
|
||
# ============================================
|
||
comprehensive-e2e:
|
||
name: Comprehensive E2E Tests
|
||
runs-on: ubuntu-24.04
|
||
needs: build-test-image
|
||
timeout-minutes: 60
|
||
|
||
steps:
|
||
- name: Checkout repository
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Free disk space (aggressive)
|
||
uses: jlumbroso/free-disk-space@main
|
||
with:
|
||
tool-cache: true
|
||
android: true
|
||
dotnet: true
|
||
haskell: true
|
||
large-packages: true
|
||
docker-images: true
|
||
swap-storage: false
|
||
|
||
- name: Login to GitHub Container Registry
|
||
uses: docker/login-action@v3
|
||
with:
|
||
registry: ${{ env.REGISTRY }}
|
||
username: ${{ github.actor }}
|
||
password: ${{ secrets.GITHUB_TOKEN }}
|
||
|
||
- name: Pull test image
|
||
run: docker pull ${{ needs.build-test-image.outputs.image }}
|
||
|
||
- name: Run E2E tests inside Docker container
|
||
run: |
|
||
echo "🧪 Running comprehensive E2E tests inside Docker..."
|
||
|
||
# 確保輸出目錄存在
|
||
mkdir -p "${{ github.workspace }}/test-results"
|
||
|
||
docker run --rm \
|
||
--name convertx-e2e-test \
|
||
-v "${{ github.workspace }}/tests:/app/tests" \
|
||
-v "${{ github.workspace }}/test-results:/app/test-results" \
|
||
-e CI=true \
|
||
-e NODE_ENV=test \
|
||
--entrypoint /bin/bash \
|
||
${{ needs.build-test-image.outputs.image }} \
|
||
-c "
|
||
echo '📋 系統資訊 System info:'
|
||
uname -a
|
||
|
||
echo ''
|
||
echo '🔧 可用轉換工具 Available conversion tools:'
|
||
echo ' inkscape:' \$(inkscape --version 2>/dev/null | head -1 || echo '未找到 not found')
|
||
echo ' pandoc:' \$(pandoc --version 2>/dev/null | head -1 || echo '未找到 not found')
|
||
echo ' ffmpeg:' \$(ffmpeg -version 2>/dev/null | head -1 || echo '未找到 not found')
|
||
echo ' libreoffice:' \$(soffice --version 2>/dev/null || echo '未找到 not found')
|
||
echo ' imagemagick:' \$(magick --version 2>/dev/null | head -1 || echo '未找到 not found')
|
||
echo ' calibre:' \$(ebook-convert --version 2>/dev/null | head -1 || echo '未找到 not found')
|
||
echo ' potrace:' \$(potrace -v 2>/dev/null | head -1 || echo '未找到 not found')
|
||
echo ' dasel:' \$(dasel --version 2>/dev/null || echo '未找到 not found')
|
||
echo ' resvg:' \$(resvg --version 2>/dev/null || echo '未找到 not found')
|
||
echo ' vips:' \$(vips --version 2>/dev/null || echo '未找到 not found')
|
||
echo ' pdf2zh:' \$(pdf2zh --version 2>/dev/null || echo '未找到 not found')
|
||
echo ' babeldoc:' \$(babeldoc --version 2>/dev/null || echo '未找到 not found')
|
||
|
||
echo ''
|
||
echo '📦 安裝測試依賴 Installing test dependencies...'
|
||
cd /app
|
||
bun install --frozen-lockfile || bun install
|
||
|
||
echo ''
|
||
echo '🧪 執行 E2E 測試 Running E2E tests...'
|
||
|
||
# 執行綜合測試(允許失敗)
|
||
bun test tests/e2e/comprehensive.e2e.test.ts --timeout 600000 || echo '⚠️ 綜合測試有部分失敗'
|
||
|
||
# 執行格式矩陣測試(允許失敗)
|
||
bun test tests/e2e/format-matrix.e2e.test.ts --timeout 300000 || echo '⚠️ 格式矩陣測試有部分失敗'
|
||
|
||
# 執行基礎轉換器測試(允許失敗)
|
||
bun test tests/e2e/converters.e2e.test.ts --timeout 120000 || echo '⚠️ 轉換器測試有部分失敗'
|
||
|
||
echo ''
|
||
echo '✅ E2E 測試完成!'
|
||
|
||
# 複製結果到輸出目錄
|
||
mkdir -p /app/test-results
|
||
cp -r tests/e2e/output/* /app/test-results/ 2>/dev/null || echo '⚠️ 無測試輸出可複製'
|
||
ls -la /app/test-results/ 2>/dev/null || echo '📁 測試結果目錄為空'
|
||
"
|
||
|
||
# 檢查輸出目錄內容
|
||
echo "📁 測試結果目錄內容:"
|
||
ls -la "${{ github.workspace }}/test-results/" || echo "目錄為空或不存在"
|
||
|
||
- name: Create placeholder if no results
|
||
if: always()
|
||
run: |
|
||
mkdir -p test-results
|
||
if [ -z "$(ls -A test-results 2>/dev/null)" ]; then
|
||
echo '{"message": "無測試結果輸出", "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' > test-results/placeholder.json
|
||
fi
|
||
|
||
- name: Upload test results
|
||
uses: actions/upload-artifact@v4
|
||
if: always()
|
||
with:
|
||
name: e2e-test-results
|
||
path: test-results/
|
||
retention-days: 7
|
||
if-no-files-found: warn
|
||
|
||
# ============================================
|
||
# Job 4: Translation tests (使用免費翻譯服務)
|
||
# 使用 Google/Bing 免費翻譯,不需要付費 API 金鑰
|
||
# ============================================
|
||
translation-tests:
|
||
name: Translation Tests (Free Services)
|
||
runs-on: ubuntu-24.04
|
||
needs: build-test-image
|
||
if: ${{ github.event.inputs.run_translation_tests == 'true' || github.event_name == 'push' }}
|
||
timeout-minutes: 30
|
||
|
||
steps:
|
||
- name: Checkout repository
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Free disk space (aggressive)
|
||
uses: jlumbroso/free-disk-space@main
|
||
with:
|
||
tool-cache: true
|
||
android: true
|
||
dotnet: true
|
||
haskell: true
|
||
large-packages: true
|
||
docker-images: true
|
||
swap-storage: false
|
||
|
||
- name: Login to GitHub Container Registry
|
||
uses: docker/login-action@v3
|
||
with:
|
||
registry: ${{ env.REGISTRY }}
|
||
username: ${{ github.actor }}
|
||
password: ${{ secrets.GITHUB_TOKEN }}
|
||
|
||
- name: Pull test image
|
||
run: docker pull ${{ needs.build-test-image.outputs.image }}
|
||
|
||
- name: Run translation tests
|
||
run: |
|
||
echo "🌍 執行翻譯測試(使用免費服務:Google/Bing)..."
|
||
|
||
# 確保輸出目錄存在
|
||
mkdir -p "${{ github.workspace }}/translation-results"
|
||
|
||
docker run --rm \
|
||
--name convertx-translation-test \
|
||
-v "${{ github.workspace }}/tests:/app/tests" \
|
||
-v "${{ github.workspace }}/translation-results:/app/translation-results" \
|
||
-e CI=true \
|
||
-e PDFMATHTRANSLATE_SERVICE=google \
|
||
-e BABELDOC_SERVICE=google \
|
||
--entrypoint /bin/bash \
|
||
${{ needs.build-test-image.outputs.image }} \
|
||
-c "
|
||
cd /app
|
||
bun install --frozen-lockfile || bun install
|
||
|
||
echo '🌍 使用免費服務執行翻譯測試...'
|
||
bun test tests/e2e/translation.e2e.test.ts --timeout 600000 || echo '⚠️ 翻譯測試有部分失敗'
|
||
|
||
mkdir -p /app/translation-results
|
||
cp -r tests/e2e/output/translation/* /app/translation-results/ 2>/dev/null || echo '⚠️ 無翻譯輸出可複製'
|
||
ls -la /app/translation-results/ 2>/dev/null || echo '📁 翻譯結果目錄為空'
|
||
"
|
||
|
||
# 檢查輸出目錄內容
|
||
echo "📁 翻譯結果目錄內容:"
|
||
ls -la "${{ github.workspace }}/translation-results/" || echo "目錄為空或不存在"
|
||
|
||
- name: Create placeholder if no results
|
||
if: always()
|
||
run: |
|
||
mkdir -p translation-results
|
||
if [ -z "$(ls -A translation-results 2>/dev/null)" ]; then
|
||
echo '{"message": "無翻譯結果輸出", "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' > translation-results/placeholder.json
|
||
fi
|
||
|
||
- name: Upload translation results
|
||
uses: actions/upload-artifact@v4
|
||
if: always()
|
||
with:
|
||
name: translation-test-results
|
||
path: translation-results/
|
||
retention-days: 7
|
||
if-no-files-found: warn
|
||
|
||
# ============================================
|
||
# Job 5: Cleanup test image
|
||
# ============================================
|
||
cleanup:
|
||
name: Cleanup Test Image
|
||
runs-on: ubuntu-24.04
|
||
needs: [e2e-tests, comprehensive-e2e]
|
||
if: always()
|
||
permissions:
|
||
packages: write
|
||
|
||
steps:
|
||
- name: Delete test image
|
||
uses: actions/github-script@v7
|
||
continue-on-error: true
|
||
with:
|
||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||
script: |
|
||
const tag = `test-${{ github.sha }}`;
|
||
console.log(`🗑️ Attempting to delete test image with tag: ${tag}`);
|
||
|
||
// Note: This requires ghcr.io API access which may need additional setup
|
||
// For now, images will be cleaned up by retention policies
|
||
console.log('Test images will be cleaned up by retention policies');
|
||
|
||
# ============================================
|
||
# Job 6: Test summary
|
||
# ============================================
|
||
summary:
|
||
name: Test Summary
|
||
runs-on: ubuntu-24.04
|
||
needs: [e2e-tests, comprehensive-e2e]
|
||
if: always()
|
||
|
||
steps:
|
||
- name: Download test results
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
pattern: "*-test-results"
|
||
path: test-results/
|
||
merge-multiple: true
|
||
continue-on-error: true
|
||
|
||
- name: Check downloaded results
|
||
run: |
|
||
echo "📁 已下載的測試結果:"
|
||
ls -la test-results/ 2>/dev/null || echo "無測試結果可下載"
|
||
|
||
- name: Generate summary
|
||
run: |
|
||
echo "# 🧪 Docker E2E 測試結果" >> $GITHUB_STEP_SUMMARY
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "## 測試狀態" >> $GITHUB_STEP_SUMMARY
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "| 任務 | 狀態 |" >> $GITHUB_STEP_SUMMARY
|
||
echo "|------|------|" >> $GITHUB_STEP_SUMMARY
|
||
echo "| E2E 測試 | ${{ needs.e2e-tests.result }} |" >> $GITHUB_STEP_SUMMARY
|
||
echo "| 綜合 E2E 測試 | ${{ needs.comprehensive-e2e.result }} |" >> $GITHUB_STEP_SUMMARY
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
|
||
if [ -f "test-results/format-matrix/matrix-summary.md" ]; then
|
||
echo "## 格式矩陣摘要" >> $GITHUB_STEP_SUMMARY
|
||
cat test-results/format-matrix/matrix-summary.md >> $GITHUB_STEP_SUMMARY
|
||
fi
|
||
|
||
if [ -f "test-results/comprehensive/conversion-matrix.json" ]; then
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "## 轉換矩陣" >> $GITHUB_STEP_SUMMARY
|
||
echo '```json' >> $GITHUB_STEP_SUMMARY
|
||
head -50 test-results/comprehensive/conversion-matrix.json >> $GITHUB_STEP_SUMMARY
|
||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||
fi
|
||
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "📅 測試完成時間: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY
|