From 0cf8a69f52cb4ad8faafb4591f05fa2a3aa879c2 Mon Sep 17 00:00:00 2001 From: kikashy <35789537+kikashy@users.noreply.github.com> Date: Wed, 21 Oct 2026 10:50:12 -0400 Subject: [PATCH] Cover the Idempotency-Key ASCII and length boundaries (carries #76) Closes #71. Carries #76 by @Haxxyyyy: a table-driven test for the POST /v1/jobs/{job}/runs Idempotency-Key rule. Valid boundary values (the shortest and the longest allowed key, the first and last visible ASCII characters) reach the unknown-job lookup and get 404; invalid values (one past the length bound, a space, DEL, control bytes, the empty header) are rejected first with 400. The author's commits are carried as one change, their content unchanged. **What my commit adds:** the checks the author could not run. The new test alone with -count=3 and with -race: passed. go.mod and go.sum byte-identical. The four mutation runs issue #71 asks for, each on a scratch copy with the handler's check changed as the issue describes: 4 of 4 made the test fail (129 characters, space, DEL, empty). The repository check list ran once: gofmt, go vet, build-pinned and the manual gate pass; the internal/runner package timed out under the machine's load in that run and passed in the focused runs; CI runs it. Co-authored-by: Haxxyyyy Co-Authored-By: Claude Fable 5.1 Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com> --- internal/runner/events_admission_test.go | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/internal/runner/events_admission_test.go b/internal/runner/events_admission_test.go index 392d64c..feeeb79 100644 --- a/internal/runner/events_admission_test.go +++ b/internal/runner/events_admission_test.go @@ -289,6 +289,39 @@ func TestEveryAdmissionRefusalIsJournaledOnce(t *testing.T) { // The store-wide route for a job serves what the job's route does, its // release's preview included, and Runner's own entries besides. +func TestSubmitRunIdempotencyKeyVisibleASCIIBoundaries(t *testing.T) { + s := openJournal(t, journalConfig(t)) + t.Cleanup(func() { s.Close() }) + + cases := []struct { + name string + key string + want int + }{ + {"one character", "k", 404}, + {"128 characters", strings.Repeat("k", 128), 404}, + {"first visible character", "!", 404}, + {"last visible character", "~", 404}, + {"empty", "", 400}, + {"129 characters", strings.Repeat("k", 129), 400}, + {"space", "a b", 400}, + {"control character", "a\x01b", 400}, + {"DEL", "a\x7fb", 400}, + {"non-ASCII letter", "é", 400}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got := serve(t, s, "POST", "/v1/jobs/job_unknown/runs", `{"facts":{}}`, map[string]string{ + "Idempotency-Key": c.key, + }) + if got.Code != c.want { + t.Fatalf("answered %d, want %d; body: %s", got.Code, c.want, got.Body.String()) + } + }) + } +} + func TestTheStoreWideRouteForAJobHasItsReleasesEntries(t *testing.T) { w := newCoverWorld(t) var page EventPage