Saltar al contenido
mypipelines
Pipelines Actions Gradle Buscar
Contracts (Hardhat/Solidity)· Reusable workflow ·on: workflow_call

Contracts Test

Contracts - Test & Coverage

.github/workflows/contracts-test.yml

.github/workflows/contracts-test.yml
name: Contracts - Test & Coverage
on:
workflow_call:
inputs:
runner:
description: 'Runner type'
required: false
type: string
default: 'ubuntu-latest'
node_version:
description: 'Node.js version'
required: false
type: string
default: '22'
package_manager:
description: 'Package manager (npm, yarn, or pnpm)'
required: false
type: string
default: 'pnpm'
pnpm_version:
description: 'pnpm version (used when package_manager: pnpm)'
required: false
type: string
default: '10'
artifact_name:
description: 'Name of build artifact to download (compiled contracts). Empty = recompile.'
required: false
type: string
default: ''
run_coverage:
description: 'Run solidity-coverage'
required: false
type: boolean
default: false
coverage_threshold:
description: 'Minimum line coverage percentage (0-100). 0 = disabled.'
required: false
type: number
default: 0
run_gas_reporter:
description: 'Enable hardhat-gas-reporter (REPORT_GAS=true)'
required: false
type: boolean
default: false
upload_reports:
description: 'Upload coverage and gas reports as artifacts'
required: false
type: boolean
default: false
outputs:
result:
description: 'Test result (success/failure)'
value: ${{ jobs.test.outputs.result }}
coverage_percentage:
description: 'Line coverage percentage'
value: ${{ jobs.test.outputs.coverage }}
tests_passed:
description: 'Number of tests passed'
value: ${{ jobs.test.outputs.passed }}
tests_failed:
description: 'Number of tests failed'
value: ${{ jobs.test.outputs.failed }}
jobs:
test:
name: Test & Coverage
runs-on: ${{ inputs.runner }}
timeout-minutes: 30
outputs:
result: ${{ steps.test-run.outcome }}
coverage: ${{ steps.coverage.outputs.percentage }}
passed: ${{ steps.test-summary.outputs.passed }}
failed: ${{ steps.test-summary.outputs.failed }}
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup pnpm
if: inputs.package_manager == 'pnpm'
uses: pnpm/action-setup@v4
- name: Setup Node.js ${{ inputs.node_version }}
uses: actions/setup-node@v5
with:
node-version: ${{ inputs.node_version }}
cache: ${{ inputs.package_manager }}
- name: Install dependencies
run: |
case "${{ inputs.package_manager }}" in
pnpm) pnpm install --frozen-lockfile ;;
yarn) yarn install --frozen-lockfile ;;
*) npm ci ;;
esac
- name: Download compiled artifacts
if: inputs.artifact_name != ''
uses: actions/download-artifact@v5
with:
name: ${{ inputs.artifact_name }}
path: .
# ============================================
# RUN TESTS
# ============================================
- name: Run hardhat test
id: test-run
env:
REPORT_GAS: ${{ inputs.run_gas_reporter }}
run: |
case "${{ inputs.package_manager }}" in
pnpm) pnpm hardhat test 2>&1 | tee test-output.log || true ;;
yarn) yarn hardhat test 2>&1 | tee test-output.log || true ;;
*) npx hardhat test 2>&1 | tee test-output.log || true ;;
esac
- name: Parse test results
id: test-summary
if: always()
run: |
PASSED=0
FAILED=0
if [ -f "test-output.log" ]; then
# Mocha output: "X passing" and "Y failing"
PASSED=$(grep -oE '[0-9]+ passing' test-output.log | grep -oE '[0-9]+' | tail -1 || echo "0")
FAILED=$(grep -oE '[0-9]+ failing' test-output.log | grep -oE '[0-9]+' | tail -1 || echo "0")
if [ -z "$PASSED" ]; then PASSED=0; fi
if [ -z "$FAILED" ]; then FAILED=0; fi
fi
echo "passed=$PASSED" >> $GITHUB_OUTPUT
echo "failed=$FAILED" >> $GITHUB_OUTPUT
TOTAL=$((PASSED + FAILED))
echo "### Test Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Status | Count |" >> $GITHUB_STEP_SUMMARY
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Passed | $PASSED |" >> $GITHUB_STEP_SUMMARY
echo "| Failed | $FAILED |" >> $GITHUB_STEP_SUMMARY
echo "| **Total** | **$TOTAL** |" >> $GITHUB_STEP_SUMMARY
# ============================================
# COVERAGE (solidity-coverage)
# ============================================
- name: Run solidity-coverage
if: inputs.run_coverage
run: |
case "${{ inputs.package_manager }}" in
pnpm) pnpm hardhat coverage 2>&1 | tee coverage-output.log || true ;;
yarn) yarn hardhat coverage 2>&1 | tee coverage-output.log || true ;;
*) npx hardhat coverage 2>&1 | tee coverage-output.log || true ;;
esac
- name: Parse coverage report
id: coverage
if: inputs.run_coverage && always()
run: |
COVERAGE=0
if [ -f "coverage/coverage-summary.json" ]; then
COVERAGE=$(node -e "
const report = require('./coverage/coverage-summary.json');
const pct = report.total.lines.pct;
console.log(typeof pct === 'number' ? pct.toFixed(1) : '0');
" 2>/dev/null || echo "0")
fi
echo "percentage=$COVERAGE" >> $GITHUB_OUTPUT
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Code Coverage (Solidity)" >> $GITHUB_STEP_SUMMARY
echo "**Line Coverage: ${COVERAGE}%**" >> $GITHUB_STEP_SUMMARY
- name: Check coverage threshold
if: inputs.run_coverage && inputs.coverage_threshold > 0 && always()
run: |
COVERAGE="${{ steps.coverage.outputs.percentage }}"
THRESHOLD="${{ inputs.coverage_threshold }}"
if [ "$(echo "$COVERAGE < $THRESHOLD" | bc -l)" -eq 1 ]; then
echo "::error::Coverage ${COVERAGE}% is below threshold of ${THRESHOLD}%"
exit 1
fi
echo "Coverage ${COVERAGE}% meets threshold of ${THRESHOLD}%"
# ============================================
# FAIL JOB ON TEST FAILURES
# ============================================
- name: Check test results
if: always()
run: |
FAILED="${{ steps.test-summary.outputs.failed }}"
if [ -n "$FAILED" ] && [ "$FAILED" -gt 0 ]; then
echo "::error::$FAILED test(s) failed"
exit 1
fi
# ============================================
# UPLOAD REPORTS
# ============================================
- name: Upload coverage reports
if: inputs.upload_reports && inputs.run_coverage && always()
uses: actions/upload-artifact@v5
with:
name: contracts-coverage-${{ github.run_id }}
path: coverage/
retention-days: 7
if-no-files-found: warn
- name: Upload gas report
if: inputs.upload_reports && inputs.run_gas_reporter && always()
uses: actions/upload-artifact@v5
with:
name: contracts-gas-report-${{ github.run_id }}
path: |
gas-report.txt
test-output.log
retention-days: 7
if-no-files-found: warn