Guides

Running the examples

Start with the checkout, POSIX shell, and build in Getting started. Run commands from the repository root. Where a plan needs the fixture server, keep it in Terminal 1 in the foreground and run vero from Terminal 2. Stop it with Ctrl-C before switching modes: only one fixture can bind port 18080 at a time. The fixture is a disposable demonstration API, not a prerequisite for testing your own service.

Plan Extra requirement Command in Terminal 2
rate-limit.yaml Plain fixture bin/vero run examples/rate-limit.yaml
orders.yaml Plain fixture; demonstration API key ORDERS_API_KEY=example-key-1234 bin/vero run examples/orders.yaml
wait-for-api.yaml Plain fixture bin/vero run examples/wait-for-api.yaml
graphql.yaml Plain fixture bin/vero run examples/graphql.yaml
ws.yaml Plain fixture bin/vero run examples/ws.yaml
exec.yaml POSIX shell for the checked-in migration script; no fixture bin/vero run examples/exec.yaml
fixture.yaml Python 3 for the checked-in migration script; no fixture bin/vero run examples/fixture.yaml
scripts.yaml Plain fixture; Node with global fetch, Python 3 on PATH bin/vero run examples/scripts.yaml
sqlite.yaml Fixture started with SQLite persistence bin/vero run examples/sqlite.yaml
postgres.yaml Dedicated Postgres test database and two role DSNs bin/vero run examples/postgres.yaml
grpc.yaml A gRPC server for orders.proto; the fixture has none see below
playwright.yaml Node >=20, npm, pinned Playwright/Chromium; plain fixture Complete Playwright recipe

For plain-fixture rows, start Terminal 1 with bin/vero-testserver, wait for it to print vero-testserver listening on http://127.0.0.1:18080 (limit 3), then run the listed command in Terminal 2. The demonstration API key is not a credential for your service. Every plan on this page was run from a checkout while writing it, and the summary lines below are from those runs; the plan each one exercises is on the example plans page with its source.

The fixture server

bin/vero-testserver is a small orders API with a token bucket per API key, written for these plans and the repository's tests. --help lists its routes.

Flag Default Meaning
--addr 127.0.0.1:18080 address to listen on
--limit 3 token bucket capacity per API key; buckets never refill, only a reset does
--sqlite file off also write orders to this SQLite file (table orders), for the sqlite.yaml plan
--postgres dsn off also write orders to this Postgres database (table orders), connecting as a role that can write
--tls off serve HTTPS with a self-signed certificate generated at start, offering HTTP/2 and HTTP/1.1
--http1-only off with --tls, offer only HTTP/1.1

Besides /healthz, /auth/token and the /orders routes, it has a /fixtures/ family the step pages use to provoke things: /fixtures/status?code=N, /fixtures/slow?ms=N, /fixtures/redirect?n=N, /fixtures/large?bytes=N, /fixtures/gzip, /fixtures/set-cookie, /fixtures/echo, /fixtures/upload, /fixtures/graphql, /fixtures/ws, /fixtures/ready?after=N, /fixtures/drop?after=N, /fixtures/big-id and /fixtures/duplicate-keys. A request to a limited route past the budget is a 429 with Retry-After; POST /admin/ratelimit/reset refills one key's bucket.

What each plan shows

rate-limit.yaml is the case vero exists for: four requests against a budget of three, the fourth a 429, and three tasks spending one key's budget under one exclusive holds, so they never overlap and the count holds at any --jobs. It resets the key in a lifecycle.reset step.

4 passed  0 failed  0 errored  0 timed out  0 skipped  0 blocked  0 not run   (4 tasks, 12 requests, 0.0s)

orders.yaml logs in with an API key from a secretRef, creates an order, reads it back, and deletes it in a finally task. The key is scrubbed from every output (Secrets). The teardown shows in the summary:

5 passed  0 failed  0 errored  0 timed out  0 skipped  0 blocked  0 not run   (5 tasks, 5 requests, 0.0s)   teardown: 1 passed

wait-for-api.yaml polls /healthz with a waitFor step until it answers, then uses the API. The attempt count is a line of its own (the waitFor step):

waitFor  wait-for-api › step 1  passed after 1 attempts, waited 0.9ms
2 passed  0 failed  0 errored  0 timed out  0 skipped  0 blocked  0 not run   (2 tasks, 2 requests, 0.0s)

graphql.yaml posts two queries to /fixtures/graphql, one reading the data and one expecting an error by reading body.errors itself (the graphql step). ws.yaml opens /fixtures/ws twice, once to echo a JSON and a text message and once to collect three pushed messages (the ws step). Each counts its upgrade as one request:

2 passed  0 failed  0 errored  0 timed out  0 skipped  0 blocked  0 not run   (2 tasks, 2 requests, 0.0s)
2 passed  0 failed  0 errored  0 timed out  0 skipped  0 blocked  0 not run   (2 tasks, 2 requests, 0.1s)

exec.yaml runs examples/bin/migrate as an argv list, no shell, in its own process group, against a SQLite URL (the exec step). It sends nothing:

1 passed  0 failed  0 errored  0 timed out  0 skipped  0 blocked  0 not run   (1 tasks, 0 requests, 0.0s)

fixture.yaml migrates a catalog.db beside the plan with an exec step, seeds two countries through a connection declared writes: true, and reads them back through a read-only one (the fixture step). The write is labelled in the summary, and a second run writes nothing because the statement says on conflict do nothing:

fixture  seed-countries › step 1  passed through connection catalog-admin: wrote 2 row(s), committed
3 passed  0 failed  0 errored  0 timed out  0 skipped  0 blocked  0 not run   (3 tasks, 0 requests, 0.1s)
fixture  seed-countries › step 1  passed through connection catalog-admin: wrote 0 row(s), committed
3 passed  0 failed  0 errored  0 timed out  0 skipped  0 blocked  0 not run   (3 tasks, 0 requests, 0.0s)

scripts.yaml runs a Node script and a Python script through the script step, each logging in and creating an order, and an http step checks the Node order through the result the script returned (Script steps). The runtimes found at load are printed first:

runtime node: /run/current-system/sw/bin/node v26.10.0
runtime python: /run/current-system/sw/bin/python3 Python 3.13.15
3 passed  0 failed  0 errored  0 timed out  0 skipped  0 blocked  0 not run   (3 tasks, 1 requests, 0.4s)

The request count is 1 because the scripts' own requests are made by the scripts; vero counts only what it sent.

sqlite.yaml and postgres.yaml create an order over HTTP and check the stored row with a read-only sql step (the sql step). Both passed here, SQLite against the fixture's file and Postgres against the dedicated test database described below:

3 passed  0 failed  0 errored  0 timed out  0 skipped  0 blocked  0 not run   (3 tasks, 2 requests, 0.0s)

grpc.yaml makes a unary call and reads a server stream, described by examples/grpc/orders.proto (the grpc step). No shipped fixture serves gRPC, so this page did not run it; the repository's test suite runs it against a server it starts in-process, and the ws and grpc steps against real servers records runs against grpcbin. To run it yourself, point VERO_GRPC_TARGET at a server that implements the two methods.

playwright.yaml drives Chromium through a Node script and checks the placed order over HTTP; its install steps are in the Playwright guide.

SQLite

Stop the plain fixture if running. In Terminal 1, from the checkout root:

bin/vero-testserver --sqlite examples/orders.db

In Terminal 2, run the sqlite.yaml row above. The server flag resolves examples/orders.db from the shell's current working directory; the plan's dsn: orders.db resolves beside examples/sqlite.yaml. Both point to the same file. The fixture creates the orders table and writes rows; the plan reads the persisted order and checks its status, total, and tenant. No external database server or SQLite CLI is required. Keep an existing database if it contains data you need; use a dedicated test file for the fixture.

Postgres

Provide an existing dedicated test database, not a production schema. Provision it and its two roles before running the example; this guide is not a database installer. The writer role must connect, use and create in the chosen schema, create or use the six-column orders table (id, sku, qty, status, total_cents, tenant_id), and insert and delete rows. The fixture issues CREATE TABLE IF NOT EXISTS; an incompatible existing table is not replaced. Use an empty test database or schema rather than overwriting an existing table. The reader role needs CONNECT on the chosen database, USAGE on the schema, and SELECT on orders, including default SELECT grants for new tables created by the writer. The reader should be read-only; vero also uses read-only transactions, and vero plan warns when the reader's role could write. deploy/local/postgres-init.yaml illustrates this writer/reader grant contract, but provisioning the reader's chosen database and credentials remains your responsibility.

Replace host, database, user names and passwords in these illustrative templates. URI-encode reserved characters in credentials; configure TLS for your database rather than assuming an insecure connection string. In Terminal 1, with the plain fixture stopped:

export VERO_TEST_PG_FIXTURE_DSN='postgres://writer:encoded-password@db.example.test:5432/vero_test?sslmode=require'
bin/vero-testserver --postgres "$VERO_TEST_PG_FIXTURE_DSN"

In Terminal 2:

export VERO_TEST_PG_DSN='postgres://reader:encoded-password@db.example.test:5432/vero_test?sslmode=require'
bin/vero run examples/postgres.yaml

The writer fixture creates the table and persists each order; the reader plan checks the row through SQL. make infra-up and make pg-dsn are maintainer conveniences with cluster assumptions, not required Postgres setup for vero. No Kubernetes cluster is needed on this path.

Paths and targets

Script entry files, exec workdirs, proto files and SQLite DSNs resolve relative to the plan directory; CLI plan paths resolve from the shell's current directory. If copying an example, keep its sibling scripts, protos or schema files with it. Most fixture examples hardcode http://127.0.0.1:18080: to target another fixture address, edit env.baseUrl in a copy of the plan. Only the Playwright plan consumes VERO_BASE_URL, and only the gRPC plan consumes VERO_GRPC_TARGET. The rate-limit plan's reset, authentication, and orders endpoints are specific to the sample API; adapt steps and assertions to your own service's contract, not just its host.

This page is docs/content/guides/running-examples.md in the repository.

verodocs