|
148 | 148 | { "name": "native-equals-body", "body_attempts": 3, "redelivery_count": 3, "expected_attempts": 3 } |
149 | 149 | ] |
150 | 150 | } |
| 151 | + }, |
| 152 | + "kafka": { |
| 153 | + "description": "Apache Kafka binding conformance (broker-bindings.md §6). Every SDK that ships a Kafka transport must satisfy these. The record value is the byte-identical envelope (the 'cases' above); these lock the bq- header projection + the bq-attempts-authoritative reconciliation the binding adds. Per-message values reuse fixtures/order-created.json so the expected projection is deterministic.", |
| 154 | + "property_projection": { |
| 155 | + "description": "On produce, the transport MUST project these native Kafka record headers from the envelope, all UTF-8 byte strings (Kafka headers are bytes, so integers are stringified): bq-job = job (the URN), bq-trace-id = trace_id, bq-message-id = meta.id, bq-schema-version = str(meta.schema_version), bq-source-lang = meta.lang, bq-attempts = str(attempts). The record value is the byte-identical envelope and the record timestamp mirrors meta.created_at (Unix ms). Applies to every Kafka-producing SDK.", |
| 156 | + "envelope_file": "fixtures/order-created.json", |
| 157 | + "headers": { |
| 158 | + "bq-job": "urn:babel:orders:created", |
| 159 | + "bq-trace-id": "7b3f9c2a-e41d-4f88-9b2a-1c0d5e6f7a8b", |
| 160 | + "bq-message-id": "f1e2d3c4-b5a6-4789-90ab-cdef01234567", |
| 161 | + "bq-schema-version": "1", |
| 162 | + "bq-source-lang": "php", |
| 163 | + "bq-attempts": "0" |
| 164 | + } |
| 165 | + }, |
| 166 | + "attempts_reconciliation": { |
| 167 | + "description": "On consume, attempts = the bq-attempts header when present (AUTHORITATIVE — Kafka has no native delivery count), else the body's own attempts (the fallback for a non-BabelQueue producer). This is NOT a max: the header overrides the body even when lower. A null header_attempts means the header is absent. The rule is identical across all five Kafka SDKs.", |
| 168 | + "cases": [ |
| 169 | + { "name": "header-matches-body", "body_attempts": 0, "header_attempts": 0, "expected_attempts": 0 }, |
| 170 | + { "name": "header-present", "body_attempts": 0, "header_attempts": 2, "expected_attempts": 2 }, |
| 171 | + { "name": "header-absent-falls-back", "body_attempts": 3, "header_attempts": null, "expected_attempts": 3 }, |
| 172 | + { "name": "header-authoritative-overrides", "body_attempts": 5, "header_attempts": 1, "expected_attempts": 1 }, |
| 173 | + { "name": "header-higher-than-body", "body_attempts": 1, "header_attempts": 4, "expected_attempts": 4 } |
| 174 | + ] |
| 175 | + } |
151 | 176 | } |
152 | 177 | } |
0 commit comments