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.