Evidence

The rate-limit fixture in Hurl, k6 and Karate

Historical experiment (2026-09-27). Its machines, installed tools, and deployment topology describe that run, not vero prerequisites. For current setup see Getting started and Running the examples.

Chapter 1.3 described Hurl, k6 and Karate from their documentation; none was installed or run. Chapter 10.4 asks for the rate-limit fixture to be ported to them: if one can express "four requests, the fourth 429, no overlap with other holders of this budget" without a shell wrapper, vero's claim narrows from a framework to format discipline. Run on 2026-09-27 against vero-testserver on loopback, 50 trials each (20 for Karate, whose JVM start costs seconds).

Answer: none of the three can. Each expresses the sequence inside one file or scenario. None has a way to keep a second test that spends the same budget from overlapping it, short of running everything serially; k6's only cross-scenario ordering is a wall-clock offset.

Historical tool versions

Nix supplied the packages for these measurements; it is not an installation requirement for vero:

Tool Version Command
Hurl hurl 8.0.1 (x86_64-pc-linux-gnu) libcurl/8.22.0 nix shell nixpkgs#hurl
k6 k6 v2.2.0 (commit/devel, go1.26.7, linux/amd64) nix shell nixpkgs#k6
Karate karate 2.1.1 on openjdk 21.0.12.1+1 nix shell nixpkgs#karate

Options were read from each installed tool's --help, not from memory of its docs.

The fixture

The same two tests as B06: rate-limit resets the key, logs in and sends four GET /orders, asserting 200 200 200 429 and a Retry-After; pagination resets the same key, logs in and spends one token. In vero both hold ratelimit/orders-api-key, and B06 passes 50/50 at --jobs 1 and --jobs 8. Files: testdata/prior-art/.

Hurl

hurl/rate-limit.hurl and hurl/pagination.hurl. Entries in one file run in order, so the sequence is expressible. Files run in parallel under --test (--parallel is the default there), and --jobs is the only control: a number of workers for all files.

Command rate-limit.hurl pagination.hurl
hurl --test --variable base=... rate-limit.hurl 50/50 not run
hurl --test --parallel ... rate-limit.hurl pagination.hurl 6/50 45/50
hurl --test --jobs 1 ... rate-limit.hurl pagination.hurl 50/50 50/50
hurl --test --jobs 1 ... pagination.hurl rate-limit.hurl 50/50 50/50

With both files in parallel, the pagination request lands inside the burst and the rate-limit file passes 6 times in 50. --jobs 1 fixes it by serializing every file in the run, including any that share nothing. Keeping these two apart while others run in parallel means splitting them into separate invocations, which is the shell wrapper chapter 1.3 predicted. --retry exists and re-sends on any error, the default chapter 3.4 warns about.

Four, the fourth 429, no overlap with other holders, no shell wrapper: no. No per-resource exclusion; only all-or-nothing parallelism.

k6

k6/rate-limit.js: two scenarios, rateLimit and pagination, one VU and one iteration each. Requests inside an iteration run in order. Between scenarios the only ordering k6 has is startTime, an offset in wall-clock time.

pagination scenario's startTime Runs passing every check
0s (both start together) 8/50
1ms 12/50
1s 50/50

Command: BASE=http://127.0.0.1:18096 PAGINATION_START=<offset> k6 run --quiet rate-limit.js (a threshold checks: rate==1 makes any failed check fail the run). (--no-summary, from older k6, no longer exists in v2: unknown flag: --no-summary.)

A one-second offset passes because the burst takes milliseconds on loopback. It says "start one second in", not "start after rate-limit has finished": the moment the burst takes longer than the offset, the plan is wrong and nothing tells you. That is chapter 1.3's near miss, measured.

No: ordering only by wall clock, and no exclusion.

Karate

karate/rate-limit.feature and karate/pagination.feature. Steps in a scenario run in order. Features run on -T threads.

Command Runs with both features passing
karate run -T 2 rate-limit.feature pagination.feature 0/20
karate run -T 1 rate-limit.feature pagination.feature 20/20

With two threads the features overlap and the run failed every time; with one thread everything is serial. Karate's callonce shares setup and does not exclude. (Karate has a @parallel=false feature tag for keeping a feature's own scenarios on one thread; it was not tried, because it serializes within a feature, not between two features that share a budget.)

No, for the same reason as Hurl: parallelism is a thread count for the whole run.

What this says about vero's claim

The claim in chapter 1.4 stands: of the tools in chapter 1.3's table that could be run here, none can say "these two must not overlap" about two tests while letting everything else run in parallel. Each can express the sequence; each makes the author choose between parallelism and correctness for the whole run, or coordinate runs from outside. Venom, Step CI and Tekton were out of scope, and Tekton's model was never in question: it orders, it does not exclude.

Nothing here narrows the product claim, so BLOCKING.md has no entry for it.

This page is docs/content/evidence/prior-art-ports.md in the repository.

verodocs