Files
picpeak/.github/workflows/tests.yml
T
Paul Nothaft f0e6d2dfb1 fix: enforce gallery access and consolidate gallery workflows (#1357)
Harden gallery authentication and authorization, consolidate gallery workflows, and prevent token-bearing URLs from leaking through nginx request error logs.
2026-09-08 15:34:09 +02:00

161 lines
5.4 KiB
YAML

name: Tests
# Runs the backend Jest suite and the frontend Vitest suite on every PR.
# Both suites already exist and cover the CRM service layer (quoteService,
# contractService, invoiceService.*, customerHoursService, eventService.
# calendar) plus the photo / settings / OG / auth surface — wiring them
# into CI makes regressions visible at PR time instead of post-merge.
#
# Triggers on any change that could affect either suite. The backend
# job intentionally omits frontend paths and vice versa so unrelated
# PRs don't pay both build costs.
on:
push:
branches: [main, beta, stable]
pull_request:
branches: [main, beta, stable]
workflow_dispatch:
permissions:
contents: read
jobs:
backend:
runs-on: ubuntu-latest
# 20, not 10. This job normally finishes in ~3 minutes, but it is the only
# one that boots Postgres and runs the full integration suite, so it is the
# only one exposed to runner contention — observed spread has reached 9.2
# minutes, and a release PR (#1088) was cancelled at 10.3 with every test
# passing and jest still running. A cancelled job reads as a red X on a
# green branch, which costs a re-run and a diagnosis every time it happens.
#
# The cap is a runaway guard, not a performance budget; 20 leaves real
# headroom over the worst observed run while still killing a hung suite
# well inside the hour GitHub would otherwise allow. frontend and ml keep
# 10 — they take seconds and have never come close.
timeout-minutes: 20
# The .picpeak restore suites gate their real-Postgres cases behind
# PICPEAK_PG_TEST_URL and `describe.skip` themselves out when it is
# unset — so until now they never ran here. That hid the half that
# matters: sequence resync, operator/role preservation across a
# cross-instance restore, and (with #1041) whether a SQLite-shaped
# row actually lands in Postgres with the right STORED VALUES rather
# than merely not throwing. Everything else in the suite still runs
# on SQLite; this service only un-gates those cases.
services:
postgres:
image: postgres:15-alpine
env:
POSTGRES_USER: picpeak
POSTGRES_PASSWORD: testpass
POSTGRES_DB: picpeak_test
options: >-
--health-cmd "pg_isready -U picpeak -d picpeak_test"
--health-interval 2s
--health-timeout 2s
--health-retries 30
ports:
- 5432:5432
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: backend/package-lock.json
- name: Install backend deps
working-directory: ./backend
run: npm ci
- name: Run Jest suite
working-directory: ./backend
env:
# backupService tests would otherwise try a real S3 round-trip.
# The S3 path itself is covered separately by the integration
# suite when MinIO is provisioned.
SKIP_S3_TESTS: 'true'
# Un-gates the real-Postgres cases in the .picpeak restore suites
# (see the `services:` note above). Absent it they silently skip.
PICPEAK_PG_TEST_URL: 'postgres://picpeak:testpass@127.0.0.1:5432/picpeak_test'
run: npx jest --ci
frontend:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install frontend deps
working-directory: ./frontend
run: npm ci
- name: Lint frontend (including Rules of Hooks)
working-directory: ./frontend
run: npm run lint
- name: Run Vitest suite
working-directory: ./frontend
run: npm test -- --run
nginx:
runs-on: ubuntu-latest
timeout-minutes: 5
strategy:
matrix:
# Match the two shipped frontend Dockerfiles.
image: ['nginx:1.28-alpine', 'nginx:1.30-alpine']
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Verify token-safe nginx logging
env:
NGINX_TEST_IMAGE: ${{ matrix.image }}
run: python3 tests/nginx/test_request_logging.py
# Optional face-detection sidecar (#1074). Runs on every PR regardless of
# whether the feature is enabled anywhere — these tests need no model
# weights (they stub the pipeline out) and cover the auth boundary, the
# request guards and the alignment geometry, which is where a mistake is a
# security problem or a silent accuracy problem rather than a visible bug.
ml:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
# Matches ml/Dockerfile's base image, so a wheel that resolves here
# resolves in the image too.
python-version: '3.12'
cache: 'pip'
cache-dependency-path: ml/requirements.txt
- name: Install ml deps
working-directory: ./ml
run: pip install -r requirements.txt pytest httpx
- name: Run pytest suite
working-directory: ./ml
run: python -m pytest tests/ -q