Skip to content

Commit 4e89b95

Browse files
committed
Use the closed ASCII blank set in Redrive and enforce mandatory roundtrip case fields
1 parent 756ede1 commit 4e89b95

4 files changed

Lines changed: 38 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ normalisation). The envelope stays frozen at `schema_version: 1`; conformance su
3535
(`fail` / `delete` / `release`). With `delete` it is removed silently; with `fail` the DLQ copy keeps the
3636
whitespace `job` and, since decode drops `urn`, loses its routable identity (ADR-0031 Consequences/Negative).
3737
This is what keeps cross-SDK parity (GR-5).
38+
`Redrive` follows the same set: a DLQ message whose `job` is ASCII-blank is restored, while one whose `job` is only
39+
Unicode whitespace is redriven like in the other SDKs (previously `Redrive` used `String.isBlank()`).
3840
- Conformance runners implement the v1.4.0 optional fields (`forbidden_keys.expect_preserved`,
3941
`roundtrip.expect_absent_after_reencode` / `apply_dead_letter` / `expect_dead_letter`) and the
4042
`extension_write` / `encode_reject` sections.

‎src/main/java/com/babelqueue/Redrive.java‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,10 @@ public static Result redrive(Transport transport, String dlq, Options opts) thro
246246
*/
247247
private static Item process(Transport transport, String dlq, Options opts, Reserved msg) throws Exception {
248248
// decode() never throws — a malformed/non-object body yields an empty envelope
249-
// (a null job), which is not redrivable; restore it rather than drop it.
249+
// (a null job), which is not redrivable; restore it rather than drop it. "Blank" is the closed
250+
// ASCII set of ADR-0031 (c), not Unicode whitespace, so every SDK redrives the same messages.
250251
Envelope env = EnvelopeCodec.decode(msg.body());
251-
if (env.job() == null || env.job().isBlank()) {
252+
if (EnvelopeCodec.isBlankAscii(env.job())) {
252253
restore(transport, dlq, msg);
253254
return new Item(null, null, null, null, dlq, null, false, false);
254255
}

‎src/test/java/com/babelqueue/BehaviourConformanceTest.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ class BehaviourConformanceTest {
4747
@TestFactory
4848
Stream<DynamicTest> roundtrip() {
4949
return cases("roundtrip").stream().map(c -> dynamicTest(name(c), () -> {
50+
// conformance/README.md (v1.4.0): "expect_attempts" and "expect_preserved" are required on every
51+
// roundtrip case (the other fields are optional). A case that loses one must fail, not silently verify less.
52+
assertNotNull(c.get("expect_attempts"), "roundtrip case lacks mandatory expect_attempts: " + name(c));
53+
assertNotNull(c.get("expect_preserved"), "roundtrip case lacks mandatory expect_preserved: " + name(c));
5054
List<String> violations = roundtripViolations(c, fixture(c));
5155
assertTrue(violations.isEmpty(), "roundtrip violations: " + violations);
5256
}));

‎src/test/java/com/babelqueue/RedriveTest.java‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,33 @@ void noDeadLetterFallsBackToMetaQueue() throws Exception {
183183
assertEquals(1, res.redriven());
184184
assertEquals(1, t.size("orders"));
185185
}
186+
187+
/** ADR-0031 (c): "blank" is the closed ASCII set, so an ASCII-blank job is not redrivable ... */
188+
@Test
189+
void asciiBlankJobIsRestoredNotRedriven() throws Exception {
190+
MemoryTransport t = new MemoryTransport();
191+
String body = "{\"job\":\" \\t\\n\",\"data\":{},\"meta\":{\"queue\":\"orders\"},\"attempts\":1}";
192+
t.publish("dlq", body);
193+
194+
Redrive.Result res = Redrive.redrive(t, "dlq", Redrive.Options.all());
195+
196+
assertEquals(0, res.redriven());
197+
assertEquals(1, res.skipped());
198+
assertEquals(1, t.size("dlq"), "blank-job message must stay in the DLQ");
199+
assertEquals(0, t.size("orders"));
200+
}
201+
202+
/** ... while a job made only of Unicode whitespace (U+2003) is not blank and is redriven like any other SDK does. */
203+
@Test
204+
void unicodeWhitespaceJobIsRedrivenNotTreatedAsBlank() throws Exception {
205+
MemoryTransport t = new MemoryTransport();
206+
String body = "{\"job\":\"\\u2003\",\"data\":{},\"meta\":{\"queue\":\"orders\"},\"attempts\":1}";
207+
t.publish("dlq", body);
208+
209+
Redrive.Result res = Redrive.redrive(t, "dlq", Redrive.Options.all());
210+
211+
assertEquals(1, res.redriven());
212+
assertEquals(0, t.size("dlq"));
213+
assertEquals(1, t.size("orders"));
214+
}
186215
}

0 commit comments

Comments
 (0)