stack/.github/workflows/e2e-api-tests.yaml
Bilal Godil 6c7b14b3bc feat: wire mock-saml-idp into CI, snapshots, and seed dummy data
Three smaller pieces that unlock e2e testing:

- .github/workflows/e2e-api-tests.yaml: starts mock-saml-idp on port
  8115 alongside mock-oauth-server, with /idp as the readiness probe.
  Root package.json adds start:mock-saml-idp script and includes the
  mock in dev:basic.

- apps/e2e/tests/snapshot-serializer.ts: strips SAMLRequest /
  SAMLResponse / RelayState query+form params, adds stack-saml-inner-
  to keyed cookie name prefixes (so the per-AuthnRequest CSRF cookie
  doesn't reroll snapshots), and adds regex replacements for SAML xs:ID
  identifiers and IssueInstant/NotBefore/NotOnOrAfter timestamps.

- apps/backend/src/lib/seed-dummy-data.ts: STACK_SEED_ENABLE_SAML=true
  pre-creates acme + globex SAML connections on the dummy project,
  fetching the IdP metadata from the running mock at seed time so the
  seeded cert matches what the mock generated at startup. The mock
  regenerates keys per restart, so re-seed if you restart it. Mock URL
  configurable via STACK_MOCK_SAML_URL (default localhost:8115).
2026-04-29 16:38:03 -07:00

194 lines
7.0 KiB
YAML

name: Runs E2E API Tests
on:
push:
branches:
- main
- dev
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/dev' }}
jobs:
build:
name: E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }})
runs-on: ubicloud-standard-8
env:
NODE_ENV: test
STACK_ENABLE_HARDCODED_PASSKEY_CHALLENGE_FOR_TESTING: yes
STACK_DATABASE_CONNECTION_STRING: "postgres://postgres:PASSWORD-PLACEHOLDER--uqfEC1hmmv@localhost:8128/stackframe"
STACK_EXTERNAL_DB_SYNC_MAX_DURATION_MS: "20000"
STACK_EXTERNAL_DB_SYNC_DIRECT: "false"
STACK_RUN_SETUP_WIZARD_TESTS: ${{ matrix.freestyle-mode != 'prod' && 'true' || '' }}
strategy:
matrix:
node-version: [22.x]
freestyle-mode: [mock, prod]
steps:
- uses: actions/checkout@v6
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
# Even just starting the Docker Compose as a daemon is slow because we have to download and build the images
# so, we run it in the background
- name: Start Docker Compose in background
uses: JarvusInnovations/background-action@v1.0.7
with:
run: docker compose -f docker/dependencies/docker.compose.yaml up --pull always -d &
# we don't need to wait on anything, just need to start the daemon
wait-on: /dev/null
tail: true
wait-for: 3s
log-output-if: true
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Create .env.test.local file for apps/backend
run: cp apps/backend/.env.development apps/backend/.env.test.local
- name: Override Freestyle API key for prod mode
if: matrix.freestyle-mode == 'prod'
run: |
echo "STACK_FREESTYLE_API_KEY=${{ secrets.STACK_FREESTYLE_REAL_API_KEY }}" >> apps/backend/.env.test.local
- name: Create .env.test.local file for apps/dashboard
run: cp apps/dashboard/.env.development apps/dashboard/.env.test.local
- name: Create .env.test.local file for apps/e2e
run: cp apps/e2e/.env.development apps/e2e/.env.test.local
- name: Create .env.test.local file for docs
run: cp docs/.env.development docs/.env.test.local
- name: Create .env.test.local file for examples/cjs-test
run: cp examples/cjs-test/.env.development examples/cjs-test/.env.test.local
- name: Create .env.test.local file for examples/demo
run: cp examples/demo/.env.development examples/demo/.env.test.local
- name: Create .env.test.local file for examples/docs-examples
run: cp examples/docs-examples/.env.development examples/docs-examples/.env.test.local
- name: Create .env.test.local file for examples/e-commerce
run: cp examples/e-commerce/.env.development examples/e-commerce/.env.test.local
- name: Create .env.test.local file for examples/middleware
run: cp examples/middleware/.env.development examples/middleware/.env.test.local
- name: Create .env.test.local file for examples/supabase
run: cp examples/supabase/.env.development examples/supabase/.env.test.local
- name: Create .env.test.local file for examples/convex
run: cp examples/convex/.env.development examples/convex/.env.test.local
- name: Create .env.test.local file for apps/internal-tool
run: cp apps/internal-tool/.env.development apps/internal-tool/.env.test.local
- name: Build
run: pnpm build
- name: Wait on Postgres
run: pnpm run wait-until-postgres-is-ready:pg_isready
- name: Wait on Inbucket
run: pnpm exec wait-on tcp:localhost:8129
- name: Wait on Svix
run: pnpm exec wait-on tcp:localhost:8113
- name: Wait on QStash
run: pnpm exec wait-on tcp:localhost:8125
- name: Wait on ClickHouse
run: pnpm exec wait-on http://localhost:8136/ping
- name: Initialize database
run: pnpm run db:init
- name: Start stack-backend in background
uses: JarvusInnovations/background-action@v1.0.7
with:
run: pnpm run start:backend --log-order=stream &
wait-on: |
http://localhost:8102
tail: true
wait-for: 30s
log-output-if: true
- name: Start stack-dashboard in background
uses: JarvusInnovations/background-action@v1.0.7
with:
run: pnpm run start:dashboard --log-order=stream &
wait-on: |
http://localhost:8101
tail: true
wait-for: 30s
log-output-if: true
- name: Start mock-oauth-server in background
uses: JarvusInnovations/background-action@v1.0.7
with:
run: pnpm run start:mock-oauth-server --log-order=stream &
wait-on: |
http://localhost:8102
tail: true
wait-for: 30s
log-output-if: true
- name: Start mock-saml-idp in background
uses: JarvusInnovations/background-action@v1.0.7
with:
run: pnpm run start:mock-saml-idp --log-order=stream &
wait-on: |
http://localhost:8115/idp
tail: true
wait-for: 30s
log-output-if: true
- name: Start run-email-queue in background
uses: JarvusInnovations/background-action@v1.0.7
with:
run: pnpm -C apps/backend run run-email-queue --log-order=stream &
wait-on: |
http://localhost:8102
tail: true
wait-for: 30s
log-output-if: true
- name: Start run-cron-jobs in background
uses: JarvusInnovations/background-action@v1.0.7
with:
run: pnpm -C apps/backend run run-cron-jobs:test --log-order=stream &
wait-on: |
http://localhost:8102
tail: true
wait-for: 30s
log-output-if: true
- name: Wait 10 seconds
run: sleep 10
- name: Run tests
run: pnpm test run ${{ matrix.freestyle-mode == 'prod' && '--min-workers=1 --max-workers=1' || '' }} ${{ matrix.freestyle-mode == 'prod' && github.ref != 'refs/heads/main' && github.ref != 'refs/heads/dev' && 'mail' || '' }}
- name: Run tests again (attempt 1)
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
run: pnpm test run ${{ matrix.freestyle-mode == 'prod' && '--min-workers=1 --max-workers=1' || '' }}
- name: Run tests again (attempt 2)
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
run: pnpm test run ${{ matrix.freestyle-mode == 'prod' && '--min-workers=1 --max-workers=1' || '' }}
- name: Verify data integrity
run: pnpm run verify-data-integrity --no-bail
- name: Print Docker Compose logs
if: always()
run: docker compose -f docker/dependencies/docker.compose.yaml logs