From 11d8c110808b7ebdfb33fb92b2ca2a11710495bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammet=20=C5=9Eafak?= Date: Sat, 24 Oct 2026 20:47:55 +0300 Subject: [PATCH 1/3] test: use a conformant envelope in the legacy abandoned-reservation recovery test --- .../com/babelqueue/redis/RedisLayoutConformanceTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/babelqueue/redis/RedisLayoutConformanceTest.java b/src/test/java/com/babelqueue/redis/RedisLayoutConformanceTest.java index 5de5b98..524bc92 100644 --- a/src/test/java/com/babelqueue/redis/RedisLayoutConformanceTest.java +++ b/src/test/java/com/babelqueue/redis/RedisLayoutConformanceTest.java @@ -431,7 +431,11 @@ void legacyAbandonedReservationIsRecoveredByteForByte() throws Exception { RedisQueueOps dying = new RedisQueueOps(redis, queue, RedisLayout.LEGACY, null, 1L, -1, RedisQueueOpsClock.WALL); RedisQueueOps noDeadline = new RedisQueueOps(redis, stuck, RedisLayout.LEGACY, null, null, -1, RedisQueueOpsClock.WALL); try { - String raw = "{\"job\": \"" + URN + "\", \"attempts\": 2, \"data\": {\"id\": 1}}"; + // A conformant envelope (the consumer rejects one without trace_id / meta as poison) with + // deliberately non-canonical spacing, so a re-encode would be caught by the byte comparison. + String raw = "{\"job\": \"" + URN + "\", \"trace_id\": \"t-1\", \"attempts\": 2, \"data\": {\"id\": 1}," + + " \"meta\": {\"id\": \"f1e2d3c4-b5a6-4789-90ab-cdef01234567\", \"queue\": \"orders\"," + + " \"lang\": \"php\", \"schema_version\": 1, \"created_at\": 1749132727000}}"; redis.rpush(queue, raw); redis.rpush(stuck, raw); assertEquals(raw, dying.reserve().body()); From b70f9eb2bd5023beaa3ee7996790844d433009e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammet=20=C5=9Eafak?= Date: Sat, 24 Oct 2026 20:50:04 +0300 Subject: [PATCH 2/3] tmp: coverage dump --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdb73b5..830b324 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,10 @@ jobs: # The conformance runner writes results.json (ADR-0032) when this is set. BQ_CONFORMANCE_RESULTS: ${{ runner.temp }}/results.json + - name: TMP coverage dump + if: always() + run: awk -F, 'NR>1 {printf "%s %d/%d\n", $2"."$3, $9, $8+$9}' target/site/jacoco/jacoco.csv | sort + - uses: actions/setup-python@v6 with: python-version: "3.12" From 9d9b9cfc60173ce1d3d37165f57822880767e1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammet=20=C5=9Eafak?= Date: Sat, 24 Oct 2026 20:52:39 +0300 Subject: [PATCH 3/3] test: cover the poison-record JSON scanner and drop the temporary coverage dump --- .github/workflows/ci.yml | 4 --- .../babelqueue/redis/PoisonRecordTest.java | 33 +++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 830b324..bdb73b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,10 +49,6 @@ jobs: # The conformance runner writes results.json (ADR-0032) when this is set. BQ_CONFORMANCE_RESULTS: ${{ runner.temp }}/results.json - - name: TMP coverage dump - if: always() - run: awk -F, 'NR>1 {printf "%s %d/%d\n", $2"."$3, $9, $8+$9}' target/site/jacoco/jacoco.csv | sort - - uses: actions/setup-python@v6 with: python-version: "3.12" diff --git a/src/test/java/com/babelqueue/redis/PoisonRecordTest.java b/src/test/java/com/babelqueue/redis/PoisonRecordTest.java index fb3bf0e..bbf7825 100644 --- a/src/test/java/com/babelqueue/redis/PoisonRecordTest.java +++ b/src/test/java/com/babelqueue/redis/PoisonRecordTest.java @@ -53,4 +53,37 @@ void rejectsADeadLetterThatIsNotASingleJsonObject() { assertThrows(IllegalArgumentException.class, () -> PoisonRecord.build(new byte[] {'{', '}'}, bad), String.valueOf(bad)); } } + + @Test + void acceptsEveryStrictJsonShapeTheScannerKnows() { + String[] valid = { + " {\t\"a\" :\r\n 1 } ", + "{\"a\":\"\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0041\\u00E9\\ud83d\\ude00 plain\"}", + "{\"a\":[true,false,null,-0,0,0.5,1E+2,-12.5e-3,2e5,[],{},\"\"]}", + "{\"a\":{\"b\":{\"c\":[1,[2,[3]]]}},\"d\":[{\"e\":1},{\"f\":[]}]}", + "{\"a\":{\"dead_letter\":1},\"b\":[{\"dead_letter\":2}]}", + "{\"a\":" + "[".repeat(100) + "]".repeat(100) + "}", + }; + for (String body : valid) { + assertTrue(PoisonRecord.isSpliceableObject(body), body); + } + } + + @Test + void rejectsEveryNonStrictOrUnspliceableObject() { + String[] invalid = { + "", " ", "{", "{\"a\"", "{\"a\":", "{\"a\":1", "{\"a\":1,", "{\"a\":1,}", "{a:1}", "{\"a\" 1}", + "{\"a\":1 \"b\":2}", "{\"a\":[1,]}", "{\"a\":[1}", "{\"a\":{\"b\":1]}", "{\"a\":[1,2}", + "{\"a\":tru}", "{\"a\":nul}", "{\"a\":fals}", "{\"a\":NaN}", "{\"a\":Infinity}", + "{\"a\":-}", "{\"a\":-", "{\"a\":01}", "{\"a\":1.}", "{\"a\":1e}", "{\"a\":1e+}", "{\"a\":+1}", "{\"a\":.5}", + "{\"a\":\"x}", "{\"a\":\"\\x\"}", "{\"a\":\"\\", "{\"a\":\"\\u12\"}", "{\"a\":\"\\u12G4\"}", + "{\"a\":\"\\u\uff10\uff10\uff14\uff11\"}", + "{\"a\":\"\\ud800\"}", "{\"a\":\"\\ud800x\"}", "{\"a\":\"\\ud800\\u0041\"}", "{\"a\":\"\\udc00\"}", + "{\"a\":\"line\nbreak\"}", "{\"a\":1}x", "{\"a\":1}{}", "[]", "1", + "{\"dead_letter\":1}", "{\"x\":1,\"dead_letter\":{}}", "{\"dead\\u005fletter\":1}", + }; + for (String body : invalid) { + assertFalse(PoisonRecord.isSpliceableObject(body), body); + } + } }