# Test preview environments

A preview environment is a full Zuplo deployment of a branch, on the same edge
network as production, at its own URL. That makes it the highest-fidelity place
to run your test suite before a change merges — closer to production than any
local server, and without the queueing and drift of a shared staging
environment.

Because `zuplo test` takes the target as a flag, the files you run against
`http://localhost:9000` are the files you run against the preview URL. Nothing
in the suite changes.

## How branches become environments

Push a branch to a connected repository and Zuplo deploys it:

- The repository's default branch deploys to **Production**.
- Every other branch deploys to a **Preview** environment with its own URL.

For the full mapping, see
[branch-based deployments](./branch-based-deployments.mdx). For how preview
environments differ from working copies, see [environments](./environments.mdx).

## Get the URL into your tests

Preview URLs are derived from the branch name and project, so hard-coding one is
a maintenance problem. Read the URL from the environment instead.

### From the Zuplo GitHub integration

The integration reports a GitHub Deployment for each build, and the deployment
status carries the environment URL. In a workflow triggered by the
`deployment_status` event, that value is
`github.event.deployment_status.environment_url`. This is the mechanism used in
[testing GitHub deployments](./github-deployment-testing.mdx), and it is the
approach to prefer: no URL construction, and the tests cannot start before the
deployment exists.

### From your own pipeline

If you deploy from your own CI with `zuplo deploy`, capture the URL from the
deploy command's output and pass it through:

```bash
OUTPUT=$(npx zuplo deploy --api-key "$ZUPLO_API_KEY" --environment "$BRANCH" 2>&1)
echo "$OUTPUT"
DEPLOY_URL=$(echo "$OUTPUT" | grep -oP 'Deployed to \K(https://[^ ]+)')

npx zuplo test --endpoint "$DEPLOY_URL"
```

For the complete workflow, see
[GitHub Actions: deploy and test](./ci-cd-github/deploy-and-test.mdx). For
pipeline patterns for other providers, see [custom CI/CD](./custom-ci-cd.mdx).

## Wait for the environment before testing

A deployment reporting success is not the same as a gateway serving traffic, and
the gap between the two is the single most common cause of "flaky" gateway
tests. The first few requests fail, the run is retried, and the real problem — a
race in the pipeline — is filed as test flakiness.

Poll a cheap unauthenticated route with a hard deadline instead of sleeping:

```bash
deadline=$((SECONDS + 120))
until curl --fail --silent --output /dev/null "$API_URL/health"; do
  if ((SECONDS >= deadline)); then
    echo "Gateway at $API_URL not ready after 120s" >&2
    exit 1
  fi
  sleep 2
done

npx zuplo test --endpoint "$API_URL"
```

If your gateway has no health route, add one. See
[health checks](./health-checks.mdx).

## Per-environment services and secrets

Preview environments have their own configuration, and a test that passes
locally can fail on a preview for reasons that have nothing to do with your
change:

- **Environment variables.** Values are set per environment in the Zuplo Portal.
  A variable that exists in production but not on the preview is a real defect
  the preview run catches — but assert on the behavior, not on the variable.
- **API key buckets.** API key authentication is backed by a bucket tied to the
  environment. Keys minted for one environment do not authenticate against
  another, so CI needs a fixture key for the environment it is testing.
- **Rate limit buckets.** Rate limit state is per environment too. This works in
  your favor: a preview environment's limits are not being consumed by
  production traffic, which is part of what makes the rate limit recipe
  deterministic.

Keep fixture credentials in your CI provider's secret store and pass them to
`zuplo test` as environment variables. Inside the test they are available on
`TestHelper.environment`:

```bash
TENANT_A_JWT="$PREVIEW_TENANT_A_JWT" npx zuplo test --endpoint "$API_URL"
```

## Scope what runs where

The same files can run everywhere, but that does not mean every test runs
everywhere:

| Target                  | What to run                                               |
| ----------------------- | --------------------------------------------------------- |
| `http://localhost:9000` | Everything. Fast feedback while editing.                  |
| Preview deployment      | Everything. This is the run that gates the merge.         |
| Production              | A small, read-only smoke subset selected with `--filter`. |

Name the production-safe tests consistently — a `smoke:` prefix works well — and
select them by name:

```bash
npx zuplo test --endpoint https://api.example.com --filter "smoke"
```

Nothing with side effects belongs in that subset. See
[gateway test recipes](./testing-recipes.mdx) for the test data hygiene rules
that make this safe.

## Clean up preview environments

Preview environments outlive the branch unless something removes them. Delete
the branch and the environment goes with it when you use the GitHub integration.
If you deploy from your own pipeline, add an explicit cleanup step on branch
delete. See
[cleanup on branch delete](./ci-cd-github/cleanup-on-branch-delete.mdx).

## Related

- [Get started with zuplo test](./testing-getting-started.mdx)
- [Gateway test recipes](./testing-recipes.mdx)
- [Testing GitHub deployments](./github-deployment-testing.mdx)
- [Environments](./environments.mdx)
