Run tests against a local Zuplo development server before deploying anywhere.
Catch issues earlier and avoid deploying broken changes.
Code
name: Local Test Then Deployon: push: branches: - main pull_request:jobs: local-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 - name: Install dependencies run: npm install - name: Start local server and run tests run: | # Start the local dev server in the background npx zuplo dev & DEV_PID=$! # Poll until the server answers, with a hard deadline. Never use a # fixed sleep: it is flaky when the runner is slow and wasted CI # minutes when it is fast. echo "Waiting for local server to start..." deadline=$((SECONDS + 60)) until curl --fail --silent --output /dev/null http://localhost:9000/health; do if (( SECONDS >= deadline )); then echo "Local server did not start within 60s" >&2 kill $DEV_PID exit 1 fi sleep 1 done # Run tests against local server npx zuplo test --endpoint http://localhost:9000 # Stop the dev server kill $DEV_PID deploy: needs: local-test runs-on: ubuntu-latest # Only deploy on push to main, not on PRs if: github.event_name == 'push' && github.ref == 'refs/heads/main' env: ZUPLO_API_KEY: ${{ secrets.ZUPLO_API_KEY }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 - name: Install dependencies run: npm install - name: Deploy to Zuplo run: npx zuplo deploy --api-key "$ZUPLO_API_KEY" --environment "${{ github.ref_name }}"
This workflow:
Starts a local Zuplo server in the CI environment.
Polls it until it answers, with a bounded deadline.
Runs your test suite against localhost.
Proceeds to deployment only if local tests pass.
Deploys to Zuplo (only on pushes to main).
The poll replaces a fixed sleep. A sleep is a guess about startup time that is
wrong in both directions, and it is the most common reason a gateway test suite
"needs" a retry. The example polls /health — add an unauthenticated health
route if your gateway does not have one. See
health checks.
Why test locally first
Faster feedback — local tests run without waiting for deployment.
Catch syntax errors — the local server validates your configuration.
Test policies — verify authentication, rate limiting, and other policies
work correctly.
No wasted deployments — don't deploy changes that fail tests.
Combine with remote tests
For maximum confidence, test both locally and against the deployed environment:
Code
jobs: local-test: # ... local testing job ... deploy-and-test: needs: local-test # ... deploy and run tests against live environment ...