emulate-seed
Emulate Seed Configs
Generate and manage seed configs for emulate (Apache-2.0) — Vercel Labs' stateful API emulation tool. Each category has individual rule files in rules/ loaded on-demand.
Not mocks. Emulate provides full state machines with cascading deletes, cursor pagination, webhook delivery, and HMAC signature verification. Create a PR via the API and it appears in GET /repos/:owner/:repo/pulls. Delete a repo and its issues, PRs, and webhooks cascade-delete.
New in 2026-04 (emulate 0.4.x)
- Modular
@emulators/*packages — each service is its own package (@emulators/github,@emulators/stripe, etc.); top-levelemulatere-exportscreateEmulatorand the CLI. - 4 new services (12 total):
mongoatlas:4007,okta:4008,resend:4009,stripe:4010with drop-in seed YAML blocks. - Resend local inbox —
GET http://localhost:4009/inboxreturns captured emails for assertions without hitting a real provider. - Stripe hosted checkout — real session redirect flow +
checkout.session.completed/expiredwebhook delivery, suitable for E2E payment tests. - MongoDB Atlas — Admin API v2 (projects/clusters/DB users) + Data API v1 with full CRUD + aggregate.
- Okta OIDC — full discovery, JWKS,
authorize/token/userinfo/revoke/introspectplus Users/Groups/Apps CRUD. - Entra / Apple / Slack expansions (v0.4.0) — PKCE + refresh rotation (Entra), RS256 JWKS (Apple), OAuth v2 consent UI (Slack).
@emulators/adapter-next— catch-all Next.js route handler runs emulators on the same origin as the app; fixes OAuth callback URL drift on Vercel preview deploys.
Quick Reference
| Category | Rules | Impact | When to Use |
|---|---|---|---|
| Seed Config | 1 | HIGH | Setting up emulate.config.yaml for test environments |
| Service Selection | 1 | MEDIUM | Choosing GitHub/Vercel/Google for your tests |
| Webhook Setup | 1 | MEDIUM | Testing webhook delivery with HMAC verification |
| Parallel CI | 1 | HIGH | Running tests in parallel without port collisions |
| Auth Tokens | 1 | MEDIUM | Seeding tokens mapped to emulated users |
Total: 5 rules across 5 categories
Quick Start
# Install (packages published under @emulators/* scope)
npm install --save-dev emulate
# Start all services
npx emulate
# Start specific services with seed data
npx emulate --service github,stripe --seed ./emulate.config.yaml
# Generate a starter config
npx emulate init --service github
Services (0.4.1 — 12 emulators)
| Service | Default Port | Coverage |
|---|---|---|
| Vercel | :4000 |
Projects, deployments, domains, env vars, teams |
| GitHub | :4001 |
Repos, PRs, issues, comments, reviews, Actions, webhooks, orgs, teams |
| Google OAuth | :4002 |
OAuth 2.0 authorize, token exchange, userinfo |
| Slack | :4003 |
Chat, conversations, users, reactions, OAuth v2 with consent UI |
| Apple Auth | :4004 |
Sign in with Apple — OIDC discovery, JWKS (RS256), auth flow, token exchange |
| Microsoft Entra | :4005 |
OAuth 2.0/OIDC v2.0, authorization code + PKCE, refresh token rotation, v1 token endpoint, Graph /users/{id} |
| AWS | :4006 |
S3 buckets, SQS queues, IAM users/roles, STS identity |
| MongoDB Atlas (0.4+) | :4007 |
Admin API v2 (projects, clusters, DB users) + Data API v1 (full CRUD + aggregate) |
| Okta (0.4+) | :4008 |
OIDC discovery, JWKS, authorize/token/userinfo/revoke/introspect, Users/Groups/Apps CRUD |
| Resend (0.4+) | :4009 |
Send + batch (100/req), list/retrieve/cancel, domains, API keys, audiences, contacts, local inbox (GET /inbox) |
| Stripe (0.4+) | :4010 |
Customers, payment methods, customer sessions, payment intents, charges, products, prices, hosted checkout session w/ webhook delivery |
| Clerk | (on-demand) | Users, sessions, organizations |
See references/api-coverage.md for full endpoint lists.
Next.js Adapter (0.4+) — @emulators/adapter-next
Runs emulators on the same origin as your Next.js app via a catch-all route handler. Fixes the OAuth callback URL drift problem on Vercel preview deploys — no more http://localhost:4001 redirect mismatches.
// next.config.js
const { withEmulate } = require('@emulators/adapter-next')
module.exports = withEmulate({ /* your next config */ })
// app/api/[...emulate]/route.ts
import { createEmulateHandler } from '@emulators/adapter-next'
export const { GET, POST } = createEmulateHandler({
services: ['github', 'stripe', 'resend'],
persistence: { /* load(), save() or built-in filePersistence */ },
})
Seed Config Structure
A seed config pre-populates the emulator with tokens, users, repos, and projects so tests start from a known state.
# emulate.config.yaml
tokens:
dev_token:
login: yonatangross
scopes: [repo, workflow, admin:org]
ci_token:
login: ci-bot
scopes: [repo]
github:
users:
- login: yonatangross
name: Yonatan Gross
- login: ci-bot
name: CI Bot
repos:
- owner: yonatangross
name: my-project
private: false
default_branch: main
topics: [typescript, testing]
vercel:
users:
- username: yonatangross
email: yonaigross@gmail.com
projects:
- name: my-docs
framework: next
# NEW in 0.4.x — drop-in seed blocks
okta:
users:
- login: alice@example.com
firstName: Alice
lastName: Smith
groups: [{ name: Everyone }, { name: Admins }]
apps: [{ name: My Web App }]
authorization_servers:
- name: default
audiences: ["api://default"]
resend:
domains: [{ name: example.com }]
api_keys: [{ name: default }]
# In tests: GET http://localhost:4009/inbox to assert captured emails
stripe:
customers:
- name: Test Customer
email: customer@example.com
products: [{ name: Pro Plan }, { name: Starter Plan }]
prices:
- { product: Pro Plan, unit_amount: 4900, currency: usd, recurring: { interval: month } }
- { product: Starter Plan, unit_amount: 1900, currency: usd, recurring: { interval: month } }
# Webhook delivery fires on checkout.session.completed / expired
mongoatlas:
projects: [{ name: my-project }]
clusters: [{ project: my-project, name: my-cluster }]
database_users: [{ project: my-project, username: app-user }]
See rules/seed-config.md for full schema and best practices.
Programmatic SDK
Service packages live under the
@emulators/*scope (e.g.,@emulators/github,@emulators/stripe). The programmatic API (createEmulator) is exported from the top-levelemulatepackage.
import { createEmulator } from 'emulate'
const github = await createEmulator({ service: 'github', port: 4001 })
// github.url -> 'http://localhost:4001'
// State is real — create a PR and it appears in the list
const res = await fetch(`${github.url}/repos/org/repo/pulls`, {
method: 'POST',
headers: { Authorization: 'Bearer dev_token' },
body: JSON.stringify({ title: 'Test PR', head: 'feature', base: 'main' })
})
const prs = await fetch(`${github.url}/repos/org/repo/pulls`)
// -> includes the PR we just created
// Cleanup
github.reset() // Synchronous state wipe
await github.close() // Shut down server
See references/sdk-patterns.md for advanced patterns (multi-service, lifecycle hooks).
Webhook Delivery
Emulate delivers real webhooks with HMAC-SHA256 signatures when state changes:
import crypto from 'crypto'
function verifyWebhook(payload: string, signature: string, secret: string): boolean {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex')
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
}
See rules/webhook-setup.md for webhook receiver patterns.
CI Integration
# .github/workflows/test.yml
jobs:
test:
steps:
- uses: actions/checkout@v4
- name: Start emulate
run: npx emulate --service github --seed .emulate/ci.yaml &
- name: Wait for emulate
run: sleep 2
- name: Run tests
run: npm test
env:
GITHUB_API_BASE: http://localhost:4001
VERCEL_API_BASE: http://localhost:4000
Parallel Test Execution
Each test worker gets its own port to avoid race conditions:
// vitest.config.ts
const workerPort = 4001 + parseInt(process.env.VITEST_WORKER_ID || '0')
See rules/parallel-ci.md for full parallel isolation patterns.
Decision Matrix
| Tool | When to Use | Stateful? | Platforms |
|---|---|---|---|
| emulate (FIRST CHOICE) | GitHub/Vercel/Google/Slack/Apple/Entra/AWS/Okta/Resend/Stripe/MongoDB testing | YES | All 12 services |
| Pact | Contract verification between services | No | Any |
| MSW | In-browser/Node HTTP mocking | No | Any |
| Nock | Node.js HTTP intercept | No | Any |
| WireMock | HTTP stub server | Partial | Any |
Use emulate when:
- Testing code that calls GitHub, Vercel, Google, Slack, Apple, Entra, AWS, Okta, Resend, Stripe, or MongoDB Atlas
- You need state persistence across multiple API calls in a test
- You want webhook delivery with real HMAC signatures (GitHub, Stripe)
- You need cascading side-effects (delete repo -> PRs cascade-delete)
- You need to assert on sent emails without hitting a real provider (Resend local
/inbox) - You need hosted Stripe checkout sessions with real redirect flow in tests
Use MSW/Nock when:
- Mocking arbitrary HTTP APIs not covered by emulate
- You need in-browser interception (MSW)
- Tests only need single request/response pairs
Related Skills
testing-integration— Integration test patterns (emulate as first choice for API tests)testing-e2e— End-to-end test patterns with emulated backendstesting-unit— Unit test patterns (use emulate for API-dependent units)security-patterns— Auth token patterns (emulate token seeding)
CLI Reference
See references/cli-reference.md for all CLI flags and commands.
SDK Patterns
See references/sdk-patterns.md for programmatic createEmulate() usage.