f0e6d2dfb1
Harden gallery authentication and authorization, consolidate gallery workflows, and prevent token-bearing URLs from leaking through nginx request error logs.
161 lines
5.4 KiB
YAML
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
|