test: E2E #23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [master, main] | |
| pull_request: | |
| branches: [master, main] | |
| jobs: | |
| lint-and-typecheck: | |
| name: Lint & Typecheck | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| # Repo .npmrc sets `ignore-scripts=true` for local-dev safety. CI is a | |
| # controlled environment and tsx/esbuild rely on the postinstall fallback | |
| # to fetch the platform binary (`@esbuild/<os>-<arch>`) when optional-dep | |
| # resolution doesn't pick it up — so we re-enable scripts here. | |
| - name: Install dependencies | |
| env: | |
| npm_config_ignore_scripts: 'false' | |
| run: npm install --legacy-peer-deps --include=optional | |
| - name: Typecheck | |
| run: npm run typecheck | |
| - name: Build | |
| run: npm run build | |
| - name: Test (unit) + coverage | |
| run: npm run test:coverage | |
| - name: Upload unit coverage | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-unit | |
| path: coverage/lcov.info | |
| if-no-files-found: warn | |
| # Surfaces unused exports / orphaned files / unused deps. `--no-exit-code` | |
| # so the report is informational; flip to required once the baseline | |
| # is enforced. | |
| - name: Knip (unused-code report) | |
| run: npm run knip | |
| # Production-only audit so we don't trip on devDependency-only CVEs that | |
| # never ship to consumers. `high` level matches the publish-gate bar. | |
| - name: npm audit (production deps) | |
| run: npm run audit:prod | |
| test-integration: | |
| name: Integration Tests | |
| runs-on: ubuntu-latest | |
| services: | |
| postgres: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_USER: postgres | |
| POSTGRES_PASSWORD: postgres | |
| POSTGRES_DB: multitenancy_test | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 10 | |
| redis: | |
| image: redis:7.0.5-alpine | |
| ports: | |
| - 6379:6379 | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 10 | |
| # mock-oauth2-server publishes a wire-compliant OIDC discovery | |
| # doc + JWKS + token endpoint. Used by | |
| # tests/integration/services/sso_oidc_real.spec.ts to prove the | |
| # package's SSO codepath interops with a real third-party OIDC | |
| # implementation (not just the in-spec fake IdP that mirrors our | |
| # assumptions). Skips silently when `MOCK_OIDC_BASE_URL` is unset. | |
| # (MinIO can't run as a service container because GitHub Actions | |
| # doesn't allow CMD overrides — see the `Start MinIO` step below.) | |
| mock-oidc: | |
| image: ghcr.io/navikt/mock-oauth2-server:2.1.10 | |
| ports: | |
| - 8080:8080 | |
| env: | |
| TZ: UTC | |
| NODE_ENV: test | |
| HOST: 127.0.0.1 | |
| PORT: 3333 | |
| APP_KEY: a-32-character-long-secret-key!! | |
| LOG_LEVEL: error | |
| TENANT_HEADER_KEY: x-tenant-id | |
| DB_HOST: 127.0.0.1 | |
| DB_PORT: 5432 | |
| DB_USER: postgres | |
| DB_PASSWORD: postgres | |
| DB_DATABASE: multitenancy_test | |
| REDIS_HOST: 127.0.0.1 | |
| REDIS_PORT: 6379 | |
| QUEUE_REDIS_HOST: 127.0.0.1 | |
| QUEUE_REDIS_PORT: 6379 | |
| QUEUE_REDIS_DB: 1 | |
| CACHE_REDIS_HOST: 127.0.0.1 | |
| CACHE_REDIS_PORT: 6379 | |
| CACHE_REDIS_DB: 2 | |
| # Optional: a Stripe *test-mode* key (sk_test_…). When set, the | |
| # real-API smoke test (tests/integration/billing/stripe_real_smoke.spec.ts) | |
| # runs and verifies every Stripe SDK call-site against the live test | |
| # endpoint instead of MockStripe. When unset/empty the test reports | |
| # itself skipped (visibly, not silently). Add it under | |
| # Settings → Secrets and variables → Actions as STRIPE_TEST_API_KEY. | |
| STRIPE_TEST_API_KEY: ${{ secrets.STRIPE_TEST_API_KEY }} | |
| # Real S3-compatible store (MinIO running on localhost) — drives | |
| # the BackupService S3 roundtrip spec. Removing these env vars | |
| # makes the spec skip rather than fail. | |
| BACKUP_S3_ENDPOINT: http://127.0.0.1:9000 | |
| BACKUP_S3_BUCKET: lasagna-test | |
| BACKUP_S3_REGION: us-east-1 | |
| AWS_ACCESS_KEY_ID: minioadmin | |
| AWS_SECRET_ACCESS_KEY: minioadmin | |
| # Real OIDC server (mock-oauth2-server running as service | |
| # container) — drives the SsoService interop spec. `default` is | |
| # the issuer path mock-oauth2-server uses for unspecified | |
| # issuers. | |
| MOCK_OIDC_BASE_URL: http://127.0.0.1:8080/default | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| - name: Install dependencies | |
| env: | |
| npm_config_ignore_scripts: 'false' | |
| run: npm install --legacy-peer-deps --include=optional | |
| # GitHub Actions `services:` doesn't allow CMD overrides, so we | |
| # can't put minio there directly — `minio/minio` boots into a | |
| # `--help` listing without `server /data`. Run it as a regular | |
| # docker container instead, then probe its readiness endpoint | |
| # before the tests start. | |
| - name: Start MinIO (S3-compatible) | |
| run: | | |
| docker run -d --rm --name lasagna-minio \ | |
| -p 9000:9000 \ | |
| -e MINIO_ROOT_USER=minioadmin \ | |
| -e MINIO_ROOT_PASSWORD=minioadmin \ | |
| minio/minio:latest server /data | |
| for i in 1 2 3 4 5 6 7 8 9 10; do | |
| if curl -fsS http://127.0.0.1:9000/minio/health/ready > /dev/null; then | |
| echo "minio ready"; exit 0 | |
| fi | |
| sleep 1 | |
| done | |
| echo "minio failed to become ready"; docker logs lasagna-minio; exit 1 | |
| # mock-oauth2-server runs as a service container above. Probe its | |
| # discovery doc to fail fast if the image changed its boot | |
| # behaviour rather than letting individual specs hang. | |
| - name: Probe mock-oauth2-server | |
| run: | | |
| for i in 1 2 3 4 5 6 7 8 9 10; do | |
| if curl -fsS "$MOCK_OIDC_BASE_URL/.well-known/openid-configuration" > /dev/null; then | |
| echo "mock-oidc ready"; exit 0 | |
| fi | |
| sleep 1 | |
| done | |
| echo "mock-oidc failed to become ready"; exit 1 | |
| - name: Test (integration) + coverage | |
| run: npm run test:integration:coverage | |
| - name: Upload integration coverage | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-integration | |
| path: coverage/lcov.info | |
| if-no-files-found: warn | |
| test-e2e-demo: | |
| name: E2E (demo app) | |
| runs-on: ubuntu-latest | |
| services: | |
| postgres: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_USER: app | |
| POSTGRES_PASSWORD: app | |
| POSTGRES_DB: lasagna_demo | |
| ports: | |
| - 55432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -U app -d lasagna_demo" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 10 | |
| redis: | |
| image: redis:7-alpine | |
| ports: | |
| - 56379:6379 | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 10 | |
| mailcatcher: | |
| image: schickling/mailcatcher | |
| ports: | |
| - 1025:1025 | |
| - 1080:1080 | |
| env: | |
| TZ: UTC | |
| NODE_ENV: development | |
| HOST: 127.0.0.1 | |
| PORT: 3333 | |
| APP_KEY: a-32-character-long-secret-key!! | |
| LOG_LEVEL: error | |
| TENANT_HEADER_KEY: x-tenant-id | |
| APP_DOMAIN: localhost | |
| DB_HOST: 127.0.0.1 | |
| DB_PORT: 55432 | |
| DB_USER: app | |
| DB_PASSWORD: app | |
| DB_DATABASE: lasagna_demo | |
| REDIS_HOST: 127.0.0.1 | |
| REDIS_PORT: 56379 | |
| QUEUE_REDIS_HOST: 127.0.0.1 | |
| QUEUE_REDIS_PORT: 56379 | |
| QUEUE_REDIS_DB: 1 | |
| CACHE_REDIS_HOST: 127.0.0.1 | |
| CACHE_REDIS_PORT: 56379 | |
| CACHE_REDIS_DB: 2 | |
| BACKUP_STORAGE_PATH: ./storage/backups | |
| DEMO_ADMIN_TOKEN: demo-admin-token-change-me | |
| MAILCATCHER_HOST: 127.0.0.1 | |
| MAILCATCHER_PORT: 1025 | |
| MAIL_FROM_ADDRESS: demo@example.test | |
| MAIL_FROM_NAME: Demo Multitenancy | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| # `pg_dump` / `pg_restore` / `psql` on PATH so backups_real.spec.ts + | |
| # the import / clone tests run instead of skipping. The suite skips | |
| # gracefully when these are absent, but we want them green in CI. | |
| - name: Install postgresql-client | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends postgresql-client | |
| - name: Install dependencies | |
| env: | |
| npm_config_ignore_scripts: 'false' | |
| run: npm install --legacy-peer-deps --include=optional | |
| # Demo's `@adonisjs-lasagna/saas-tenancy` is a workspace symlink that | |
| # resolves through `build/`, so a fresh build is mandatory before the | |
| # e2e suite imports from it. | |
| - name: Build package | |
| run: npm run build | |
| - name: Backoffice setup (creates backoffice schema + tenants table) | |
| working-directory: examples/api | |
| run: npx tsx ace.ts backoffice:setup | |
| - name: Run e2e suite | |
| working-directory: examples/api | |
| run: npx tsx ace.ts test e2e |