zrizDocsLearnSecuritySign inSign up

Test that duplicate requests create one row

Send one request three times in a zriz pipeline, then count the rows in the database. One row proves that a retry makes no duplicate.

Lesson 5 of 10 · zz 0.8.0 or later · Updated · Plain text for agents: /learn/test-duplicate-requests-create-one-row.md

The problem

A client sends a request again after a timeout, or a user clicks two times. If the API stores each copy, the database gets duplicate rows. The status code does not show this: only the database does.

The test

The example is a shop API with a MySQL database. A second sign-up with one e-mail must get 409.

Start from a project that zz init made: the quick start gives the commands. Add the action register, the database value, and the database resource. The connection string stays on your machine, because SHOP_DB is in sensitive.

.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" }
  }
}

.zriz/environments/local.json:

{
  "values": {
    "TARGET_URL": "http://localhost:9080",
    "SHOP_DB": "user:pass@tcp(localhost:3306)/shop"
  },
  "sensitive": ["SHOP_DB"]
}

.zriz/resources/shopdb.json:

{
  "type": "sql",
  "connection": "${env.SHOP_DB}",
  "read-only": true,
  "actions": {
    "users-by-email": { "query": "SELECT id FROM users WHERE email = ${ctx.email}" }
  }
}

.zriz/pipelines/register-three-times.json:

{
  "description": "Three same sign-ups make one row",
  "steps": [
    { "set": { "email": "u-${gen.uuid}@test.com" } },
    {
      "call": "target/register",
      "body": { "email": "${ctx.email}", "password": "secret123", "name": "Ann" },
      "expect": [["status", "==", 201]]
    },
    {
      "for": { "each": "attempt", "in": [2, 3] },
      "steps": [
        {
          "call": "target/register",
          "body": { "email": "${ctx.email}", "password": "secret123", "name": "Ann" },
          "expect": [["status", "==", 409], ["body.error", "==", "email already registered"]]
        }
      ]
    },
    {
      "call": "shopdb/users-by-email",
      "expect": [["row-count", "==", 1]]
    }
  ]
}

Run it

zz run register-three-times
"status":"pass"

What it proves

Note: An API with an idempotency key answers the copy with the first result, not with 409. Then expect the same status and the same id three times. The row check stays the same.

Next