Skip to content

Auto Generate Cover+Test #24

Auto Generate Cover+Test

Auto Generate Cover+Test #24

name: Auto Generate Cover+Test
on:
workflow_dispatch:
inputs:
override_files:
description: 'Opcional: arquivos .kt específicos (separados por espaço). Vazio = escaneia tudo sem cobertura.'
required: false
default: ''
workflow_run:
workflows: ["Build and Test for PRs"]
types: [completed]
permissions:
contents: write
pull-requests: write
jobs:
generate-tests:
name: Generate Unit Tests with Claude
runs-on: ubuntu-latest
if: |
github.event_name == 'workflow_dispatch' ||
(
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.actor.login != 'github-actions[bot]'
)
steps:
# ── 1. Normaliza contexto para os dois gatilhos ───────────────────────────
- name: Resolve trigger context
id: ctx
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "head_sha=${{ github.sha }}" >> "$GITHUB_OUTPUT"
echo "pr_number=manual" >> "$GITHUB_OUTPUT"
else
echo "head_sha=${{ github.event.workflow_run.head_sha }}" >> "$GITHUB_OUTPUT"
echo "pr_number=${{ github.event.workflow_run.pull_requests[0].number }}" >> "$GITHUB_OUTPUT"
fi
# ── 2. Checkout ───────────────────────────────────────────────────────────
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ steps.ctx.outputs.head_sha }}
fetch-depth: 0
# ── 3. Python ─────────────────────────────────────────────────────────────
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Python dependencies
run: pip install anthropic
# ── 4. Escaneia craftd-core buscando arquivos sem cobertura ──────────────
- name: Find uncovered Kotlin files in craftd-core
id: changed
run: |
OVERRIDE="${{ github.event.inputs.override_files }}"
# Conta total de arquivos fonte
TOTAL=$(find android_kmp/craftd-core/src \
\( -path "*/commonMain/kotlin/*.kt" -o -path "*/androidMain/kotlin/*.kt" \) \
| grep -v "Test\.kt" | wc -l | tr -d ' ')
if [ -n "$OVERRIDE" ]; then
UNCOVERED=$(echo "$OVERRIDE" | tr ' ' '\n' | grep -v "^$" || true)
UNCOVERED_COUNT=$(echo "$UNCOVERED" | grep -v "^$" | wc -l | tr -d ' ')
echo "Modo override — arquivos informados manualmente:"
else
echo "Scan completo — buscando arquivos sem cobertura em craftd-core..."
UNCOVERED=""
while IFS= read -r SRC; do
TEST=$(echo "$SRC" \
| sed 's|src/commonMain/kotlin/|src/test/java/|' \
| sed 's|src/androidMain/kotlin/|src/test/java/|' \
| sed 's|\.kt$|Test.kt|')
if [ ! -f "$TEST" ]; then
UNCOVERED="$UNCOVERED"$'\n'"$SRC"
fi
done < <(find android_kmp/craftd-core/src \
\( -path "*/commonMain/kotlin/*.kt" -o -path "*/androidMain/kotlin/*.kt" \) \
| grep -v "Test\.kt" | sort)
UNCOVERED=$(echo "$UNCOVERED" | grep -v "^$" || true)
UNCOVERED_COUNT=$(echo "$UNCOVERED" | grep -v "^$" | wc -l | tr -d ' ')
fi
echo "$UNCOVERED"
COVERED_BEFORE=$((TOTAL - UNCOVERED_COUNT))
COVERED_AFTER=$TOTAL
if [ "$TOTAL" -gt "0" ]; then
PCT_BEFORE=$(( (COVERED_BEFORE * 100) / TOTAL ))
PCT_AFTER=100
else
PCT_BEFORE=0
PCT_AFTER=0
fi
echo "total=$TOTAL" >> "$GITHUB_OUTPUT"
echo "covered_before=$COVERED_BEFORE" >> "$GITHUB_OUTPUT"
echo "covered_after=$COVERED_AFTER" >> "$GITHUB_OUTPUT"
echo "pct_before=$PCT_BEFORE" >> "$GITHUB_OUTPUT"
echo "pct_after=$PCT_AFTER" >> "$GITHUB_OUTPUT"
if [ -z "$UNCOVERED" ]; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
echo "Nenhum arquivo sem cobertura. Nada a fazer."
else
echo "has_changes=true" >> "$GITHUB_OUTPUT"
printf "files<<EOF\n%s\nEOF\n" "$UNCOVERED" >> "$GITHUB_OUTPUT"
fi
# ── 5. Chama Claude API para gerar os testes ─────────────────────────────
- name: Generate unit tests with Claude API
if: steps.changed.outputs.has_changes == 'true'
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CHANGED_FILES: ${{ steps.changed.outputs.files }}
run: python .github/scripts/generate_tests.py
# ── 6. Verifica se arquivos foram gerados ─────────────────────────────────
- name: Check generated files
if: steps.changed.outputs.has_changes == 'true'
id: check
run: |
COUNT=$(find android_kmp/craftd-core/src/test -name "*Test.kt" 2>/dev/null | wc -l | tr -d ' ')
echo "count=$COUNT" >> "$GITHUB_OUTPUT"
echo "Found $COUNT test file(s)"
if [ "$COUNT" -gt "0" ]; then
echo "has_tests=true" >> "$GITHUB_OUTPUT"
NAMES=$(find android_kmp/craftd-core/src/test -name "*Test.kt" \
| xargs -I{} basename {} \
| paste -sd ", ")
echo "covered_names=$NAMES" >> "$GITHUB_OUTPUT"
echo "Files: $NAMES"
else
echo "has_tests=false" >> "$GITHUB_OUTPUT"
fi
# ── 7. Cria branch com nome incremental e commita os testes ──────────────
- name: Commit generated tests
if: steps.check.outputs.has_tests == 'true'
id: commit
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Autentica o push com GH_PAT via URL do remote
git remote set-url origin https://x-access-token:${{ secrets.GH_PAT }}@github.com/CodandoTV/CraftD.git
# Determina nome do branch com auto-incremento: cover/test, cover/test-1, cover/test-2 ...
BASE="cover/test"
BRANCH="$BASE"
N=1
while git ls-remote --exit-code --heads origin "$BRANCH" > /dev/null 2>&1; do
BRANCH="${BASE}-${N}"
N=$((N + 1))
done
echo "Branch escolhido: $BRANCH"
git checkout -b "$BRANCH"
git add --force android_kmp/craftd-core/src/test/
git commit -m "test: add unit tests for craftd-core (auto-generated via Claude)"
git push origin "$BRANCH"
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
# ── 8. Abre PR com os testes gerados e evolução de cobertura ─────────────
- name: Open Pull Request with generated tests
if: steps.check.outputs.has_tests == 'true'
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
run: |
PR_NUMBER="${{ steps.ctx.outputs.pr_number }}"
BRANCH="${{ steps.commit.outputs.branch }}"
COVERED="${{ steps.check.outputs.covered_names }}"
TOTAL="${{ steps.changed.outputs.total }}"
COV_BEFORE="${{ steps.changed.outputs.covered_before }}"
COV_AFTER="${{ steps.changed.outputs.covered_after }}"
PCT_BEFORE="${{ steps.changed.outputs.pct_before }}"
PCT_AFTER="${{ steps.changed.outputs.pct_after }}"
gh pr create \
--base "main" \
--head "$BRANCH" \
--title "[Auto] Add unit tests for craftd-core" \
--body "$(cat <<EOF
## Auto-generated Unit Tests
Este PR foi gerado automaticamente pelo workflow **Auto Generate Cover+Test** usando a Claude API.
### Evolução de cobertura
| | Arquivos | Cobertura |
|---|---|---|
| Antes | ${COV_BEFORE} / ${TOTAL} | ${PCT_BEFORE}% |
| Depois | ${COV_AFTER} / ${TOTAL} | ${PCT_AFTER}% |
### Arquivos cobertos
\`\`\`
${COVERED}
\`\`\`
### Como funciona
1. Um PR tocou arquivos em \`android_kmp/craftd-core\`
2. Após o CI passar, o workflow escaneou todos os arquivos sem cobertura
3. Claude gerou testes **JUnit4 + MockK** para cada arquivo
4. Este PR foi criado automaticamente com o resultado
### Checklist de revisão
- [ ] Testes compilam sem erros (\`./gradlew testDebugUnitTest\`)
- [ ] Testes cobrem os principais caminhos de lógica
- [ ] Edge cases tratados (null, vazio, JSON inválido)
> Triggered by PR #${PR_NUMBER}
EOF
)"