@durable-streams/client-conformance-tests 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/typescript-adapter.cjs +75 -3
- package/dist/adapters/typescript-adapter.js +76 -4
- package/dist/{benchmark-runner-D-YSAvRy.js → benchmark-runner-CrE6JkbX.js} +86 -8
- package/dist/{benchmark-runner-BlKqhoXE.cjs → benchmark-runner-Db4he452.cjs} +87 -8
- package/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +106 -6
- package/dist/index.d.ts +106 -6
- package/dist/index.js +1 -1
- package/dist/{protocol-3cf94Xyb.d.cts → protocol-D37G3c4e.d.cts} +80 -4
- package/dist/{protocol-DyEvTHPF.d.ts → protocol-Mcbiq3nQ.d.ts} +80 -4
- package/dist/protocol.d.cts +2 -2
- package/dist/protocol.d.ts +2 -2
- package/package.json +3 -3
- package/src/adapters/typescript-adapter.ts +127 -6
- package/src/protocol.ts +85 -1
- package/src/runner.ts +178 -13
- package/src/test-cases.ts +110 -3
- package/test-cases/consumer/error-handling.yaml +42 -0
- package/test-cases/consumer/offset-handling.yaml +209 -0
- package/test-cases/producer/idempotent/autoclaim.yaml +214 -0
- package/test-cases/producer/idempotent/batching.yaml +98 -0
- package/test-cases/producer/idempotent/concurrent-requests.yaml +100 -0
- package/test-cases/producer/idempotent/epoch-management.yaml +333 -0
- package/test-cases/producer/idempotent/error-handling.yaml +194 -0
- package/test-cases/producer/idempotent/multi-producer.yaml +322 -0
- package/test-cases/producer/idempotent/sequence-validation.yaml +339 -0
- package/test-cases/producer/idempotent-json-batching.yaml +134 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
id: idempotent-epoch-management
|
|
2
|
+
name: Idempotent Producer - Epoch Management
|
|
3
|
+
description: |
|
|
4
|
+
Tests for producer epoch validation and zombie fencing.
|
|
5
|
+
Epochs enable split-brain protection - only the latest instance can write.
|
|
6
|
+
category: producer
|
|
7
|
+
tags:
|
|
8
|
+
- idempotent
|
|
9
|
+
- epoch
|
|
10
|
+
- fencing
|
|
11
|
+
|
|
12
|
+
tests:
|
|
13
|
+
- id: epoch-upgrade-accepted
|
|
14
|
+
name: Epoch upgrade resets sequence to 0
|
|
15
|
+
description: New epoch must start at seq=0 and should be accepted
|
|
16
|
+
setup:
|
|
17
|
+
- action: create
|
|
18
|
+
as: streamPath
|
|
19
|
+
contentType: text/plain
|
|
20
|
+
operations:
|
|
21
|
+
- action: server-append
|
|
22
|
+
path: ${streamPath}
|
|
23
|
+
data: "epoch0-msg0"
|
|
24
|
+
producerId: test-producer
|
|
25
|
+
producerEpoch: 0
|
|
26
|
+
producerSeq: 0
|
|
27
|
+
expect:
|
|
28
|
+
status: 200
|
|
29
|
+
- action: server-append
|
|
30
|
+
path: ${streamPath}
|
|
31
|
+
data: "epoch0-msg1"
|
|
32
|
+
producerId: test-producer
|
|
33
|
+
producerEpoch: 0
|
|
34
|
+
producerSeq: 1
|
|
35
|
+
expect:
|
|
36
|
+
status: 200
|
|
37
|
+
# Upgrade to epoch=1
|
|
38
|
+
- action: server-append
|
|
39
|
+
path: ${streamPath}
|
|
40
|
+
data: "epoch1-msg0"
|
|
41
|
+
producerId: test-producer
|
|
42
|
+
producerEpoch: 1
|
|
43
|
+
producerSeq: 0
|
|
44
|
+
expect:
|
|
45
|
+
status: 200
|
|
46
|
+
producerEpoch: 1
|
|
47
|
+
|
|
48
|
+
- id: stale-epoch-rejected
|
|
49
|
+
name: Stale epoch returns 403 (zombie fencing)
|
|
50
|
+
description: Old epoch should be rejected with 403 and current epoch in response
|
|
51
|
+
setup:
|
|
52
|
+
- action: create
|
|
53
|
+
as: streamPath
|
|
54
|
+
contentType: text/plain
|
|
55
|
+
operations:
|
|
56
|
+
# Establish epoch=1
|
|
57
|
+
- action: server-append
|
|
58
|
+
path: ${streamPath}
|
|
59
|
+
data: "msg"
|
|
60
|
+
producerId: test-producer
|
|
61
|
+
producerEpoch: 1
|
|
62
|
+
producerSeq: 0
|
|
63
|
+
expect:
|
|
64
|
+
status: 200
|
|
65
|
+
# Try to write with stale epoch=0
|
|
66
|
+
- action: server-append
|
|
67
|
+
path: ${streamPath}
|
|
68
|
+
data: "zombie"
|
|
69
|
+
producerId: test-producer
|
|
70
|
+
producerEpoch: 0
|
|
71
|
+
producerSeq: 0
|
|
72
|
+
expect:
|
|
73
|
+
status: 403
|
|
74
|
+
producerEpoch: 1
|
|
75
|
+
|
|
76
|
+
- id: epoch-rollback-rejected
|
|
77
|
+
name: Epoch rollback is rejected
|
|
78
|
+
description: Cannot go back to a lower epoch
|
|
79
|
+
setup:
|
|
80
|
+
- action: create
|
|
81
|
+
as: streamPath
|
|
82
|
+
contentType: text/plain
|
|
83
|
+
operations:
|
|
84
|
+
# Establish epoch=2
|
|
85
|
+
- action: server-append
|
|
86
|
+
path: ${streamPath}
|
|
87
|
+
data: "msg"
|
|
88
|
+
producerId: test-producer
|
|
89
|
+
producerEpoch: 2
|
|
90
|
+
producerSeq: 0
|
|
91
|
+
expect:
|
|
92
|
+
status: 200
|
|
93
|
+
# Try epoch=1 (rollback)
|
|
94
|
+
- action: server-append
|
|
95
|
+
path: ${streamPath}
|
|
96
|
+
data: "rollback"
|
|
97
|
+
producerId: test-producer
|
|
98
|
+
producerEpoch: 1
|
|
99
|
+
producerSeq: 0
|
|
100
|
+
expect:
|
|
101
|
+
status: 403
|
|
102
|
+
|
|
103
|
+
- id: epoch-increase-requires-seq-zero
|
|
104
|
+
name: Epoch increase with seq != 0 is rejected
|
|
105
|
+
description: New epoch must start at seq=0
|
|
106
|
+
setup:
|
|
107
|
+
- action: create
|
|
108
|
+
as: streamPath
|
|
109
|
+
contentType: text/plain
|
|
110
|
+
operations:
|
|
111
|
+
- action: server-append
|
|
112
|
+
path: ${streamPath}
|
|
113
|
+
data: "msg"
|
|
114
|
+
producerId: test-producer
|
|
115
|
+
producerEpoch: 0
|
|
116
|
+
producerSeq: 0
|
|
117
|
+
expect:
|
|
118
|
+
status: 200
|
|
119
|
+
# Try epoch=1 with seq=5
|
|
120
|
+
- action: server-append
|
|
121
|
+
path: ${streamPath}
|
|
122
|
+
data: "bad"
|
|
123
|
+
producerId: test-producer
|
|
124
|
+
producerEpoch: 1
|
|
125
|
+
producerSeq: 5
|
|
126
|
+
expect:
|
|
127
|
+
status: 400
|
|
128
|
+
|
|
129
|
+
- id: epoch-gap-allowed
|
|
130
|
+
name: Epoch can skip values (gap allowed)
|
|
131
|
+
description: |
|
|
132
|
+
Epochs don't need to be sequential. Jumping from epoch=0 to epoch=10
|
|
133
|
+
should be allowed (unlike sequences which must be sequential).
|
|
134
|
+
setup:
|
|
135
|
+
- action: create
|
|
136
|
+
as: streamPath
|
|
137
|
+
contentType: text/plain
|
|
138
|
+
operations:
|
|
139
|
+
- action: server-append
|
|
140
|
+
path: ${streamPath}
|
|
141
|
+
data: "epoch0"
|
|
142
|
+
producerId: test-producer
|
|
143
|
+
producerEpoch: 0
|
|
144
|
+
producerSeq: 0
|
|
145
|
+
expect:
|
|
146
|
+
status: 200
|
|
147
|
+
# Jump to epoch=10 (skipping 1-9)
|
|
148
|
+
- action: server-append
|
|
149
|
+
path: ${streamPath}
|
|
150
|
+
data: "epoch10"
|
|
151
|
+
producerId: test-producer
|
|
152
|
+
producerEpoch: 10
|
|
153
|
+
producerSeq: 0
|
|
154
|
+
expect:
|
|
155
|
+
status: 200
|
|
156
|
+
# Previous epochs now fenced
|
|
157
|
+
- action: server-append
|
|
158
|
+
path: ${streamPath}
|
|
159
|
+
data: "epoch5"
|
|
160
|
+
producerId: test-producer
|
|
161
|
+
producerEpoch: 5
|
|
162
|
+
producerSeq: 0
|
|
163
|
+
expect:
|
|
164
|
+
status: 403
|
|
165
|
+
producerEpoch: 10
|
|
166
|
+
|
|
167
|
+
- id: epoch-zero-after-higher-epoch
|
|
168
|
+
name: Epoch 0 rejected after higher epoch established
|
|
169
|
+
description: |
|
|
170
|
+
Once a higher epoch is established, epoch=0 should be rejected.
|
|
171
|
+
This is critical for zombie fencing.
|
|
172
|
+
setup:
|
|
173
|
+
- action: create
|
|
174
|
+
as: streamPath
|
|
175
|
+
contentType: text/plain
|
|
176
|
+
operations:
|
|
177
|
+
# Establish epoch=5
|
|
178
|
+
- action: server-append
|
|
179
|
+
path: ${streamPath}
|
|
180
|
+
data: "epoch5"
|
|
181
|
+
producerId: test-producer
|
|
182
|
+
producerEpoch: 5
|
|
183
|
+
producerSeq: 0
|
|
184
|
+
expect:
|
|
185
|
+
status: 200
|
|
186
|
+
# Epoch 0 should be fenced
|
|
187
|
+
- action: server-append
|
|
188
|
+
path: ${streamPath}
|
|
189
|
+
data: "epoch0-zombie"
|
|
190
|
+
producerId: test-producer
|
|
191
|
+
producerEpoch: 0
|
|
192
|
+
producerSeq: 0
|
|
193
|
+
expect:
|
|
194
|
+
status: 403
|
|
195
|
+
producerEpoch: 5
|
|
196
|
+
# Epoch 4 should also be fenced
|
|
197
|
+
- action: server-append
|
|
198
|
+
path: ${streamPath}
|
|
199
|
+
data: "epoch4-zombie"
|
|
200
|
+
producerId: test-producer
|
|
201
|
+
producerEpoch: 4
|
|
202
|
+
producerSeq: 0
|
|
203
|
+
expect:
|
|
204
|
+
status: 403
|
|
205
|
+
producerEpoch: 5
|
|
206
|
+
|
|
207
|
+
- id: split-brain-fencing
|
|
208
|
+
name: Split-brain fencing scenario
|
|
209
|
+
description: Old producer instance (zombie) should be fenced when new instance claims higher epoch
|
|
210
|
+
setup:
|
|
211
|
+
- action: create
|
|
212
|
+
as: streamPath
|
|
213
|
+
contentType: text/plain
|
|
214
|
+
operations:
|
|
215
|
+
# Producer A (original): epoch=0
|
|
216
|
+
- action: server-append
|
|
217
|
+
path: ${streamPath}
|
|
218
|
+
data: "A0"
|
|
219
|
+
producerId: shared-producer
|
|
220
|
+
producerEpoch: 0
|
|
221
|
+
producerSeq: 0
|
|
222
|
+
expect:
|
|
223
|
+
status: 200
|
|
224
|
+
# Producer B (new instance): claims epoch=1
|
|
225
|
+
- action: server-append
|
|
226
|
+
path: ${streamPath}
|
|
227
|
+
data: "B0"
|
|
228
|
+
producerId: shared-producer
|
|
229
|
+
producerEpoch: 1
|
|
230
|
+
producerSeq: 0
|
|
231
|
+
expect:
|
|
232
|
+
status: 200
|
|
233
|
+
# Producer A (zombie): tries epoch=0, seq=1 - should be fenced
|
|
234
|
+
- action: server-append
|
|
235
|
+
path: ${streamPath}
|
|
236
|
+
data: "A1"
|
|
237
|
+
producerId: shared-producer
|
|
238
|
+
producerEpoch: 0
|
|
239
|
+
producerSeq: 1
|
|
240
|
+
expect:
|
|
241
|
+
status: 403
|
|
242
|
+
producerEpoch: 1
|
|
243
|
+
|
|
244
|
+
- id: large-epoch-numbers
|
|
245
|
+
name: Large epoch numbers handled correctly
|
|
246
|
+
description: Test that large epoch values work correctly
|
|
247
|
+
setup:
|
|
248
|
+
- action: create
|
|
249
|
+
as: streamPath
|
|
250
|
+
contentType: text/plain
|
|
251
|
+
operations:
|
|
252
|
+
- action: server-append
|
|
253
|
+
path: ${streamPath}
|
|
254
|
+
data: "large-epoch"
|
|
255
|
+
producerId: test-producer
|
|
256
|
+
producerEpoch: 2147483640
|
|
257
|
+
producerSeq: 0
|
|
258
|
+
expect:
|
|
259
|
+
status: 200
|
|
260
|
+
# Stale epoch should still be rejected
|
|
261
|
+
- action: server-append
|
|
262
|
+
path: ${streamPath}
|
|
263
|
+
data: "stale"
|
|
264
|
+
producerId: test-producer
|
|
265
|
+
producerEpoch: 100
|
|
266
|
+
producerSeq: 0
|
|
267
|
+
expect:
|
|
268
|
+
status: 403
|
|
269
|
+
producerEpoch: 2147483640
|
|
270
|
+
|
|
271
|
+
- id: sequence-resets-on-epoch-upgrade
|
|
272
|
+
name: Each epoch has independent sequence space
|
|
273
|
+
description: |
|
|
274
|
+
Sequence numbers reset to 0 with each epoch upgrade.
|
|
275
|
+
The old epoch's sequences don't affect the new epoch.
|
|
276
|
+
setup:
|
|
277
|
+
- action: create
|
|
278
|
+
as: streamPath
|
|
279
|
+
contentType: text/plain
|
|
280
|
+
operations:
|
|
281
|
+
# Epoch 0: sequences 0, 1, 2
|
|
282
|
+
- action: server-append
|
|
283
|
+
path: ${streamPath}
|
|
284
|
+
data: "e0s0"
|
|
285
|
+
producerId: test-producer
|
|
286
|
+
producerEpoch: 0
|
|
287
|
+
producerSeq: 0
|
|
288
|
+
expect:
|
|
289
|
+
status: 200
|
|
290
|
+
- action: server-append
|
|
291
|
+
path: ${streamPath}
|
|
292
|
+
data: "e0s1"
|
|
293
|
+
producerId: test-producer
|
|
294
|
+
producerEpoch: 0
|
|
295
|
+
producerSeq: 1
|
|
296
|
+
expect:
|
|
297
|
+
status: 200
|
|
298
|
+
- action: server-append
|
|
299
|
+
path: ${streamPath}
|
|
300
|
+
data: "e0s2"
|
|
301
|
+
producerId: test-producer
|
|
302
|
+
producerEpoch: 0
|
|
303
|
+
producerSeq: 2
|
|
304
|
+
expect:
|
|
305
|
+
status: 200
|
|
306
|
+
# Epoch 1: starts fresh at seq=0
|
|
307
|
+
- action: server-append
|
|
308
|
+
path: ${streamPath}
|
|
309
|
+
data: "e1s0"
|
|
310
|
+
producerId: test-producer
|
|
311
|
+
producerEpoch: 1
|
|
312
|
+
producerSeq: 0
|
|
313
|
+
expect:
|
|
314
|
+
status: 200
|
|
315
|
+
- action: server-append
|
|
316
|
+
path: ${streamPath}
|
|
317
|
+
data: "e1s1"
|
|
318
|
+
producerId: test-producer
|
|
319
|
+
producerEpoch: 1
|
|
320
|
+
producerSeq: 1
|
|
321
|
+
expect:
|
|
322
|
+
status: 200
|
|
323
|
+
# Verify all 5 messages present
|
|
324
|
+
- action: read
|
|
325
|
+
path: ${streamPath}
|
|
326
|
+
expect:
|
|
327
|
+
dataContainsAll:
|
|
328
|
+
- "e0s0"
|
|
329
|
+
- "e0s1"
|
|
330
|
+
- "e0s2"
|
|
331
|
+
- "e1s0"
|
|
332
|
+
- "e1s1"
|
|
333
|
+
upToDate: true
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
id: idempotent-error-handling
|
|
2
|
+
name: Idempotent Producer - Error Handling
|
|
3
|
+
description: |
|
|
4
|
+
Tests for producer header validation and error conditions.
|
|
5
|
+
Includes atomicity guarantees when appends fail.
|
|
6
|
+
category: producer
|
|
7
|
+
tags:
|
|
8
|
+
- idempotent
|
|
9
|
+
- validation
|
|
10
|
+
- errors
|
|
11
|
+
|
|
12
|
+
tests:
|
|
13
|
+
- id: partial-headers-rejected
|
|
14
|
+
name: Partial producer headers are rejected
|
|
15
|
+
description: All three producer headers must be provided together
|
|
16
|
+
setup:
|
|
17
|
+
- action: create
|
|
18
|
+
as: streamPath
|
|
19
|
+
contentType: text/plain
|
|
20
|
+
operations:
|
|
21
|
+
# Only Producer-Id
|
|
22
|
+
- action: server-append
|
|
23
|
+
path: ${streamPath}
|
|
24
|
+
data: "msg"
|
|
25
|
+
headers:
|
|
26
|
+
Producer-Id: test-producer
|
|
27
|
+
expect:
|
|
28
|
+
status: 400
|
|
29
|
+
# Missing Producer-Seq
|
|
30
|
+
- action: server-append
|
|
31
|
+
path: ${streamPath}
|
|
32
|
+
data: "msg"
|
|
33
|
+
headers:
|
|
34
|
+
Producer-Id: test-producer
|
|
35
|
+
Producer-Epoch: "0"
|
|
36
|
+
expect:
|
|
37
|
+
status: 400
|
|
38
|
+
|
|
39
|
+
- id: invalid-integer-formats-rejected
|
|
40
|
+
name: Invalid integer formats in producer headers are rejected
|
|
41
|
+
description: Server should reject non-integer values like "1abc" or "1e3"
|
|
42
|
+
setup:
|
|
43
|
+
- action: create
|
|
44
|
+
as: streamPath
|
|
45
|
+
contentType: text/plain
|
|
46
|
+
operations:
|
|
47
|
+
# Trailing junk in Producer-Seq
|
|
48
|
+
- action: server-append
|
|
49
|
+
path: ${streamPath}
|
|
50
|
+
data: "msg"
|
|
51
|
+
headers:
|
|
52
|
+
Producer-Id: test-producer
|
|
53
|
+
Producer-Epoch: "0"
|
|
54
|
+
Producer-Seq: "1abc"
|
|
55
|
+
expect:
|
|
56
|
+
status: 400
|
|
57
|
+
# Scientific notation
|
|
58
|
+
- action: server-append
|
|
59
|
+
path: ${streamPath}
|
|
60
|
+
data: "msg"
|
|
61
|
+
headers:
|
|
62
|
+
Producer-Id: test-producer
|
|
63
|
+
Producer-Epoch: "1e3"
|
|
64
|
+
Producer-Seq: "0"
|
|
65
|
+
expect:
|
|
66
|
+
status: 400
|
|
67
|
+
# Negative value
|
|
68
|
+
- action: server-append
|
|
69
|
+
path: ${streamPath}
|
|
70
|
+
data: "msg"
|
|
71
|
+
headers:
|
|
72
|
+
Producer-Id: test-producer
|
|
73
|
+
Producer-Epoch: "-1"
|
|
74
|
+
Producer-Seq: "0"
|
|
75
|
+
expect:
|
|
76
|
+
status: 400
|
|
77
|
+
|
|
78
|
+
- id: empty-producer-id-rejected
|
|
79
|
+
name: Empty producer ID is rejected
|
|
80
|
+
description: Producer-Id header with empty string should be rejected
|
|
81
|
+
setup:
|
|
82
|
+
- action: create
|
|
83
|
+
as: streamPath
|
|
84
|
+
contentType: text/plain
|
|
85
|
+
operations:
|
|
86
|
+
- action: server-append
|
|
87
|
+
path: ${streamPath}
|
|
88
|
+
data: "msg"
|
|
89
|
+
headers:
|
|
90
|
+
Producer-Id: ""
|
|
91
|
+
Producer-Epoch: "0"
|
|
92
|
+
Producer-Seq: "0"
|
|
93
|
+
expect:
|
|
94
|
+
status: 400
|
|
95
|
+
|
|
96
|
+
- id: failed-append-does-not-advance-producer-state
|
|
97
|
+
name: Failed append does not advance producer state (atomicity)
|
|
98
|
+
description: If append fails after producer validation (e.g., invalid JSON), producer seq should not advance
|
|
99
|
+
setup:
|
|
100
|
+
- action: create
|
|
101
|
+
as: streamPath
|
|
102
|
+
contentType: application/json
|
|
103
|
+
operations:
|
|
104
|
+
# First append succeeds
|
|
105
|
+
- action: server-append
|
|
106
|
+
path: ${streamPath}
|
|
107
|
+
data: '{"msg": 1}'
|
|
108
|
+
producerId: test-producer
|
|
109
|
+
producerEpoch: 0
|
|
110
|
+
producerSeq: 0
|
|
111
|
+
expect:
|
|
112
|
+
status: 200
|
|
113
|
+
# Second append with invalid JSON - should fail
|
|
114
|
+
- action: server-append
|
|
115
|
+
path: ${streamPath}
|
|
116
|
+
data: "not valid json"
|
|
117
|
+
producerId: test-producer
|
|
118
|
+
producerEpoch: 0
|
|
119
|
+
producerSeq: 1
|
|
120
|
+
expect:
|
|
121
|
+
status: 400
|
|
122
|
+
# Retry seq=1 with valid JSON - should succeed (not be rejected as duplicate or gap)
|
|
123
|
+
# If producer state was incorrectly advanced, this would fail
|
|
124
|
+
- action: server-append
|
|
125
|
+
path: ${streamPath}
|
|
126
|
+
data: '{"msg": 2}'
|
|
127
|
+
producerId: test-producer
|
|
128
|
+
producerEpoch: 0
|
|
129
|
+
producerSeq: 1
|
|
130
|
+
expect:
|
|
131
|
+
status: 200
|
|
132
|
+
# seq=2 should also work
|
|
133
|
+
- action: server-append
|
|
134
|
+
path: ${streamPath}
|
|
135
|
+
data: '{"msg": 3}'
|
|
136
|
+
producerId: test-producer
|
|
137
|
+
producerEpoch: 0
|
|
138
|
+
producerSeq: 2
|
|
139
|
+
expect:
|
|
140
|
+
status: 200
|
|
141
|
+
|
|
142
|
+
- id: gap-fill-resume
|
|
143
|
+
name: Gap can be filled and producer can resume
|
|
144
|
+
description: |
|
|
145
|
+
After a 409 gap error, the producer should be able to fill the gap
|
|
146
|
+
and then continue. Some implementations accidentally "lock out" a
|
|
147
|
+
sequence once a gap was observed, or mutate state on the 409 path.
|
|
148
|
+
setup:
|
|
149
|
+
- action: create
|
|
150
|
+
as: streamPath
|
|
151
|
+
contentType: text/plain
|
|
152
|
+
operations:
|
|
153
|
+
# seq=0 succeeds
|
|
154
|
+
- action: server-append
|
|
155
|
+
path: ${streamPath}
|
|
156
|
+
data: "msg0"
|
|
157
|
+
producerId: test-producer
|
|
158
|
+
producerEpoch: 0
|
|
159
|
+
producerSeq: 0
|
|
160
|
+
expect:
|
|
161
|
+
status: 200
|
|
162
|
+
# seq=2 creates a gap -> 409
|
|
163
|
+
- action: server-append
|
|
164
|
+
path: ${streamPath}
|
|
165
|
+
data: "msg2"
|
|
166
|
+
producerId: test-producer
|
|
167
|
+
producerEpoch: 0
|
|
168
|
+
producerSeq: 2
|
|
169
|
+
expect:
|
|
170
|
+
status: 409
|
|
171
|
+
# Fill the gap with seq=1
|
|
172
|
+
- action: server-append
|
|
173
|
+
path: ${streamPath}
|
|
174
|
+
data: "msg1"
|
|
175
|
+
producerId: test-producer
|
|
176
|
+
producerEpoch: 0
|
|
177
|
+
producerSeq: 1
|
|
178
|
+
expect:
|
|
179
|
+
status: 200
|
|
180
|
+
# Now seq=2 should succeed
|
|
181
|
+
- action: server-append
|
|
182
|
+
path: ${streamPath}
|
|
183
|
+
data: "msg2"
|
|
184
|
+
producerId: test-producer
|
|
185
|
+
producerEpoch: 0
|
|
186
|
+
producerSeq: 2
|
|
187
|
+
expect:
|
|
188
|
+
status: 200
|
|
189
|
+
# Verify correct order in stream (text/plain concatenates all data)
|
|
190
|
+
- action: read
|
|
191
|
+
path: ${streamPath}
|
|
192
|
+
expect:
|
|
193
|
+
data: "msg0msg1msg2"
|
|
194
|
+
upToDate: true
|