Skip to content

Merge pull request #1937 from beebozy/fix-db-timeout-lock #2527

Merge pull request #1937 from beebozy/fix-db-timeout-lock

Merge pull request #1937 from beebozy/fix-db-timeout-lock #2527

Workflow file for this run

name: Coverage
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: write
jobs:
coverage:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
POSTGRES_DB: test_db
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Verify package-lock.json exists
run: |
if [ ! -f package-lock.json ]; then
npm install --package-lock-only --ignore-scripts
fi
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libvips-dev libjpeg-dev libpng-dev libtiff-dev libgif-dev librsvg2-dev libpoppler-cpp-dev libcairo2-dev libpango1.0-dev libjpeg8-dev build-essential
- name: Install dependencies
run: npm ci
- name: Wait for Postgres to be ready
run: |
for i in $(seq 1 30); do
pg_isready -h localhost -p 5432 -U test_user && break
echo "Waiting for Postgres... attempt $i/30"
sleep 1
done
- name: Run database migrations
run: npm run migrate:up 2>&1
continue-on-error: true
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db
- name: Run tests with coverage
id: coverage
run: |
npm run test:coverage -- --coverageReporters=json-summary --coverageReporters=text --coverageReporters=lcov --maxWorkers=2 --bail --forceExit 2>&1 | tee coverage-output.txt
EXIT_CODE=${PIPESTATUS[0]}
echo "exit_code=$EXIT_CODE" >> "$GITHUB_OUTPUT"
exit $EXIT_CODE
timeout-minutes: 10
continue-on-error: true
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db
REDIS_URL: redis://localhost:6379
STELLAR_ISSUER_SECRET: S-DUMMY-SECRET-KEY-FOR-TESTING-PURPOSES-ONLY
NODE_ENV: test
NODE_OPTIONS: --experimental-vm-modules
CI: true
- name: Extract coverage metrics
id: metrics
run: |
if [ -f coverage/coverage-summary.json ]; then
STMTS=$(node -e "const c=require('./coverage/coverage-summary.json');console.log(c.total.statements.pct)")
BRANCHES=$(node -e "const c=require('./coverage/coverage-summary.json');console.log(c.total.branches.pct)")
FUNCS=$(node -e "const c=require('./coverage/coverage-summary.json');console.log(c.total.functions.pct)")
LINES=$(node -e "const c=require('./coverage/coverage-summary.json');console.log(c.total.lines.pct)")
else
STMTS="N/A"
BRANCHES="N/A"
FUNCS="N/A"
LINES="N/A"
fi
echo "statements=$STMTS" >> "$GITHUB_OUTPUT"
echo "branches=$BRANCHES" >> "$GITHUB_OUTPUT"
echo "functions=$FUNCS" >> "$GITHUB_OUTPUT"
echo "lines=$LINES" >> "$GITHUB_OUTPUT"
# Thresholds — keep in sync with jest.config.js
THRESHOLD_STMTS=70
THRESHOLD_BRANCHES=70
THRESHOLD_FUNCS=70
THRESHOLD_LINES=70
PASSED=true
if [ "$STMTS" != "N/A" ]; then
node -e "process.exit(($STMTS < $THRESHOLD_STMTS) ? 1 : 0)" || PASSED=false
node -e "process.exit(($BRANCHES < $THRESHOLD_BRANCHES) ? 1 : 0)" || PASSED=false
node -e "process.exit(($FUNCS < $THRESHOLD_FUNCS) ? 1 : 0)" || PASSED=false
node -e "process.exit(($LINES < $THRESHOLD_LINES) ? 1 : 0)" || PASSED=false
fi
echo "passed=$PASSED" >> "$GITHUB_OUTPUT"
echo "threshold_stmts=$THRESHOLD_STMTS" >> "$GITHUB_OUTPUT"
echo "threshold_branches=$THRESHOLD_BRANCHES" >> "$GITHUB_OUTPUT"
echo "threshold_funcs=$THRESHOLD_FUNCS" >> "$GITHUB_OUTPUT"
echo "threshold_lines=$THRESHOLD_LINES" >> "$GITHUB_OUTPUT"
- name: Comment coverage on PR
if: github.event_name == 'pull_request'
continue-on-error: true
uses: actions/github-script@v7
with:
script: |
const stmts = '${{ steps.metrics.outputs.statements }}';
const branches = '${{ steps.metrics.outputs.branches }}';
const funcs = '${{ steps.metrics.outputs.functions }}';
const lines = '${{ steps.metrics.outputs.lines }}';
const passed = '${{ steps.metrics.outputs.passed }}' === 'true';
const thStmts = '${{ steps.metrics.outputs.threshold_stmts }}';
const thBranches = '${{ steps.metrics.outputs.threshold_branches }}';
const thFuncs = '${{ steps.metrics.outputs.threshold_funcs }}';
const thLines = '${{ steps.metrics.outputs.threshold_lines }}';
const icon = (val, threshold) => parseFloat(val) >= parseFloat(threshold) ? '✅' : '❌';
const status = passed ? '✅ Coverage thresholds met' : '❌ Coverage below thresholds';
const body = `## 📊 Coverage Report
${status}
| Metric | Current | Threshold | Status |
|--------|---------|-----------|--------|
| Statements | ${stmts}% | ${thStmts}% | ${icon(stmts, thStmts)} |
| Branches | ${branches}% | ${thBranches}% | ${icon(branches, thBranches)} |
| Functions | ${funcs}% | ${thFuncs}% | ${icon(funcs, thFuncs)} |
| Lines | ${lines}% | ${thLines}% | ${icon(lines, thLines)} |
> Coverage enforcement is configured in \`jest.config.js\`. Thresholds only go up — never down.
`.replace(/^ +/gm, '');
// Find and update existing comment or create new
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find(c => c.body.includes('📊 Coverage Report'));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}
- name: Enforce coverage thresholds
if: steps.metrics.outputs.passed == 'false'
run: |
echo "⚠️ Coverage is below the required thresholds."
echo ""
echo " Statements: ${{ steps.metrics.outputs.statements }}% (need ${{ steps.metrics.outputs.threshold_stmts }}%)"
echo " Branches: ${{ steps.metrics.outputs.branches }}% (need ${{ steps.metrics.outputs.threshold_branches }}%)"
echo " Functions: ${{ steps.metrics.outputs.functions }}% (need ${{ steps.metrics.outputs.threshold_funcs }}%)"
echo " Lines: ${{ steps.metrics.outputs.lines }}% (need ${{ steps.metrics.outputs.threshold_lines }}%)"
echo ""
echo "Please add tests to cover the untested code."