zrizDocsLearnSecuritySign inSign up

Test an async API with polling, not sleep

Use the poll step of a zriz pipeline to wait for an async result. The test ends when the result comes, and fails if it never does.

Lesson 9 of 10 · zz 0.8.0 or later · Updated · Plain text for agents: /learn/test-async-api-with-polling.md

The problem

Some work is done after the call ends: a queue sends a notice, a job updates a record. A test with a fixed sleep is slow when the sleep is long. It fails at random when the sleep is short.

The test

The example is a shop API. A payment puts an event on a queue. Some time later, the user has a notice of the type payment_completed.

Start from a project that zz init made: the quick start gives the commands. Add four actions to the resource file.

.zriz/resources/target.json:

{
  "type": "http",
  "description": "Your app under test",
  "base-url": "${env.TARGET_URL}",
  "headers": { "Content-Type": "application/json" },
  "actions": {
    "check": { "method": "GET", "path": "/api/health" },
    "register": { "method": "POST", "path": "/api/auth/register" },
    "create-order": { "method": "POST", "path": "/api/orders" },
    "pay": { "method": "POST", "path": "/api/payments" },
    "notifications": { "method": "GET", "path": "/api/notifications" }
  }
}

.zriz/pipelines/payment-notice.json:

{
  "description": "Pay an order, then wait for the notice",
  "steps": [
    {
      "call": "target/register",
      "body": { "email": "u-${gen.uuid}@test.com", "password": "secret123", "name": "Ann" },
      "expect": [["status", "==", 201]],
      "save": { "token": "body.token" }
    },
    {
      "call": "target/create-order",
      "headers": { "Authorization": "Bearer ${ctx.token}" },
      "body": { "items": [{ "sku": "KB-001", "quantity": 1 }] },
      "expect": [["status", "==", 201]],
      "save": { "order-id": "body.order_id", "total": "body.total" }
    },
    {
      "call": "target/pay",
      "headers": { "Authorization": "Bearer ${ctx.token}" },
      "body": { "order_id": "${ctx.order-id}", "amount": "${ctx.total}", "method": "card" },
      "expect": [["status", "==", 201]]
    },
    {
      "poll": "target/notifications",
      "headers": { "Authorization": "Bearer ${ctx.token}" },
      "until": ["body.total", ">=", 1],
      "interval-ms": 500,
      "max-attempts": 20,
      "expect": [["body.items[0].type", "==", "payment_completed"]]
    }
  ]
}

Run it

zz run payment-notice
"status":"pass"

What it proves

Note: "${ctx.total}" alone in a value keeps the number type. The API gets a number, not text.

Next