ci: fold the Tests workflow into SonarQube Analysis and delete it (#123) #127

Merged
bermudalamb merged 2 commits from feature/123-consolidate-test-workflows into main 2026-08-22 11:13:06 -05:00
3 changed files with 55 additions and 140 deletions
Showing only changes of commit c68bda8c30 - Show all commits
+49 -3
View File
@@ -1,5 +1,17 @@
name: SonarQube Analysis
# The only workflow that runs the test suites. tests.yml used to run the unit
# and end-to-end suites as well, on identical triggers, so every pull request
# installed, migrated, built, started the backend and ran the whole end-to-end
# suite twice. With one runner the second copy did not run in parallel, it
# queued. It was deleted and its two summarize steps folded in here. See #123.
#
# Note that the integration suite DOES run here, on every push and pull request,
# despite backend-integration.yml describing it as manual. That quarantine only
# ever applied to tests.yml. It is survivable here because test:integration:cov
# passes --forceExit, which papers over the post-run hang, and because of the
# timeout below.
on:
push:
branches: [main]
@@ -82,8 +94,13 @@ jobs:
run: node migrate.js up
working-directory: backend
# --json/--outputFile appended rather than baked into the script: the
# coverage run and the results file are wanted together here, and nowhere
# else. summarize-jest.js below reads that file.
- name: Backend unit tests with coverage
run: npm run test:unit:cov
id: unit
continue-on-error: true
run: npm run test:unit:cov -- --json --outputFile=unit-results.json
working-directory: backend
# Covers everything in src/routes, which the unit suite does not touch —
@@ -116,11 +133,21 @@ jobs:
# Runs against an istanbul-instrumented dev server, which is what produces
# window.__coverage__ for the fixture to collect.
- name: Frontend end-to-end tests with coverage
run: npm run test:e2e:cov
id: e2e
continue-on-error: true
env:
PLAYWRIGHT_JSON_OUTPUT_NAME: playwright-results.json
# list as well as json, so the log still shows which test failed rather
# than only a file nobody reads until the summary step.
run: npm run test:e2e:cov -- --reporter=list,json
working-directory: frontend
# Keyed off the step outcome rather than failure(). continue-on-error above
# means the job is not in a failed state at this point, so failure() would
# never fire and the log that explains an end-to-end failure would go
# unprinted precisely when it is wanted.
- name: Backend log
if: failure()
if: steps.e2e.outcome == 'failure'
run: cat /tmp/backend.log
# Fails when nothing was collected rather than writing an empty report. An
@@ -135,3 +162,22 @@ jobs:
env:
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# Readable pass and fail counts, which the raw jest and Playwright output
# does not give at a glance. Both run with if: always() so a failing suite
# is still summarised — which is the case they exist for.
- name: Summarize unit tests
if: always()
run: node scripts/summarize-jest.js backend/unit-results.json "Backend Unit Test"
- name: Summarize end-to-end tests
if: always()
run: node scripts/summarize-playwright.js frontend/playwright-results.json
# Last, so a failing suite still produces coverage, a scan and both
# summaries first. Without this step continue-on-error above would turn a
# failing suite into a passing job, which is the one way this change could
# do real damage.
- name: Fail if either suite failed
if: steps.unit.outcome == 'failure' || steps.e2e.outcome == 'failure'
run: exit 1
-137
View File
@@ -1,137 +0,0 @@
name: Tests
# backend-integration lives in its own manual workflow
# (.gitea/workflows/backend-integration.yml) rather than running here. It held
# the runner for 3h12m on 2026-08-18 — 87 seconds of tests followed by a hang
# after the run completed — and blocked frontend-e2e behind it for the same
# three hours. Run it from Actions before merging anything that touches the API
# or the database.
on:
push:
branches: [main]
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:
jobs:
backend-unit:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install deps
run: npm install
working-directory: backend
- name: Run unit tests
id: unit
continue-on-error: true
run: npm run test:unit:json
working-directory: backend
- name: Summarize
if: always()
run: node scripts/summarize-jest.js backend/unit-results.json "Backend Unit Test"
- name: Fail job if tests failed
if: steps.unit.outcome == 'failure'
run: exit 1
frontend-e2e:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: redefined_test
POSTGRES_PASSWORD: redefined_test
POSTGRES_DB: redefined_test
options: >-
--health-cmd "pg_isready -U redefined_test"
--health-interval 5s
--health-timeout 5s
--health-retries 10
env:
PGHOST: postgres
PGPORT: 5432
PGUSER: redefined_test
PGPASSWORD: redefined_test
PGDATABASE: redefined_test
PORT: 3000
DEMO_MODE: 'true'
UPLOADS_DIR: /tmp/redefined-uploads
# NODE_ENV is deliberately unset: `npm install` omits devDependencies when
# NODE_ENV=production, which strips tsc/vite/@playwright/test and breaks the
# build. It would also flip the session cookie to Secure, which the e2e run
# serves over plain http.
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install backend deps
run: npm install
working-directory: backend
- name: Run migrations
run: node migrate.js up
working-directory: backend
- name: Build backend
run: npm run build
working-directory: backend
- name: Start backend
run: |
mkdir -p /tmp/redefined-uploads
node dist/server.js > /tmp/backend.log 2>&1 &
for i in $(seq 1 30); do
if node -e "require('http').get('http://localhost:3000/api/config', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"; then
echo "Backend ready after ${i}s"
exit 0
fi
sleep 1
done
echo "Backend did not become ready within 30s:"
cat /tmp/backend.log
exit 1
working-directory: backend
- name: Install frontend deps
run: npm install
working-directory: frontend
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
working-directory: frontend
- name: Run Playwright tests
id: e2e
continue-on-error: true
env:
PLAYWRIGHT_JSON_OUTPUT_NAME: playwright-results.json
run: npx playwright test --reporter=json
working-directory: frontend
- name: Backend log
if: steps.e2e.outcome == 'failure'
run: cat /tmp/backend.log
- name: Summarize
if: always()
run: node scripts/summarize-playwright.js frontend/playwright-results.json
- name: Fail job if tests failed
if: steps.e2e.outcome == 'failure'
run: exit 1
+6
View File
@@ -12,3 +12,9 @@ test-results/
.superpowers/
.scannerwork/
.nyc_output/
# Test result JSON, written by the CI test steps for the summarize scripts and
# by anyone running the same commands locally. Regenerated every run.
backend/unit-results.json
backend/integration-results.json
frontend/playwright-results.json